mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge Birch and m-c
This commit is contained in:
commit
557eb980cc
@ -10,16 +10,12 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
XPIDL_SOURCES += [
|
||||
'nsIAccessible.idl',
|
||||
'nsIAccessibleApplication.idl',
|
||||
'nsIAccessibleCaretMoveEvent.idl',
|
||||
'nsIAccessibleCursorable.idl',
|
||||
'nsIAccessibleDocument.idl',
|
||||
'nsIAccessibleEditableText.idl',
|
||||
'nsIAccessibleCaretMoveEvent.idl',
|
||||
'nsIAccessibleEvent.idl',
|
||||
'nsIAccessibleHideEvent.idl',
|
||||
'nsIAccessibleStateChangeEvent.idl',
|
||||
'nsIAccessibleTableChangeEvent.idl',
|
||||
'nsIAccessibleTextChangeEvent.idl',
|
||||
'nsIAccessibleVirtualCursorChangeEvent.idl',
|
||||
'nsIAccessibleHyperLink.idl',
|
||||
'nsIAccessibleHyperText.idl',
|
||||
'nsIAccessibleImage.idl',
|
||||
@ -29,11 +25,15 @@ XPIDL_SOURCES += [
|
||||
'nsIAccessibleRetrieval.idl',
|
||||
'nsIAccessibleRole.idl',
|
||||
'nsIAccessibleSelectable.idl',
|
||||
'nsIAccessibleStateChangeEvent.idl',
|
||||
'nsIAccessibleStates.idl',
|
||||
'nsIAccessibleTable.idl',
|
||||
'nsIAccessibleTableChangeEvent.idl',
|
||||
'nsIAccessibleText.idl',
|
||||
'nsIAccessibleTextChangeEvent.idl',
|
||||
'nsIAccessibleTypes.idl',
|
||||
'nsIAccessibleValue.idl',
|
||||
'nsIAccessibleVirtualCursorChangeEvent.idl',
|
||||
'nsIXBLAccessible.idl',
|
||||
]
|
||||
|
||||
|
@ -1078,28 +1078,15 @@ HyperTextAccessible::GetTextAtOffset(int32_t aOffset,
|
||||
return GetCharAt(aOffset, eGetAt, aText, aStartOffset, aEndOffset) ?
|
||||
NS_OK : NS_ERROR_INVALID_ARG;
|
||||
|
||||
case BOUNDARY_WORD_START: {
|
||||
uint32_t textLen = CharacterCount();
|
||||
if (offset == textLen) {
|
||||
*aStartOffset = *aEndOffset = textLen;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
case BOUNDARY_WORD_START:
|
||||
*aEndOffset = FindWordBoundary(offset, eDirNext, eStartWord);
|
||||
*aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
|
||||
return GetText(*aStartOffset, *aEndOffset, aText);
|
||||
}
|
||||
|
||||
case BOUNDARY_WORD_END: {
|
||||
if (offset == 0) {
|
||||
*aStartOffset = *aEndOffset = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
case BOUNDARY_WORD_END:
|
||||
*aStartOffset = FindWordBoundary(offset, eDirPrevious, eEndWord);
|
||||
*aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eEndWord);
|
||||
return GetText(*aStartOffset, *aEndOffset, aText);
|
||||
}
|
||||
|
||||
case BOUNDARY_LINE_START:
|
||||
case BOUNDARY_LINE_END:
|
||||
|
@ -335,12 +335,7 @@
|
||||
testTextAtOffset(13, BOUNDARY_WORD_START, "words\n", 13, 19, IDs);
|
||||
|
||||
// BOUNDARY_WORD_END
|
||||
testTextAtOffset(0, BOUNDARY_WORD_END, "oneword", 0, 7,
|
||||
"div", kTodo, kOk, kTodo,
|
||||
"divbr", kTodo, kOk, kTodo,
|
||||
"editable", kTodo, kOk, kTodo,
|
||||
"editablebr", kTodo, kOk, kTodo,
|
||||
"textarea", kTodo, kOk, kTodo);
|
||||
testTextAtOffset(0, BOUNDARY_WORD_END, "oneword", 0, 7, IDs);
|
||||
testTextAtOffset(8, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
|
||||
"div", kOk, kOk, kOk,
|
||||
"divbr", kTodo, kTodo, kTodo,
|
||||
|
@ -310,14 +310,10 @@
|
||||
testTextAtOffset(9, BOUNDARY_WORD_START, "friend", 9, 15, IDs);
|
||||
testTextAtOffset(10, BOUNDARY_WORD_START, "friend", 9, 15, IDs);
|
||||
testTextAtOffset(14, BOUNDARY_WORD_START, "friend", 9, 15, IDs);
|
||||
testTextAtOffset(15, BOUNDARY_WORD_START, "", 15, 15,
|
||||
"input", kOk, kOk, kOk,
|
||||
"div", kOk, kOk, kOk,
|
||||
"editable", kOk, kOk, kOk,
|
||||
"textarea", kTodo, kTodo, kOk);
|
||||
testTextAtOffset(15, BOUNDARY_WORD_START, "friend", 9, 15, IDs);
|
||||
|
||||
// BOUNDARY_WORD_END
|
||||
testTextAtOffset(0, BOUNDARY_WORD_END, "", 0, 0, IDs);
|
||||
testTextAtOffset(0, BOUNDARY_WORD_END, "hello", 0, 5, IDs);
|
||||
testTextAtOffset(1, BOUNDARY_WORD_END, "hello", 0, 5, IDs);
|
||||
testTextAtOffset(5, BOUNDARY_WORD_END, "hello", 0, 5, IDs);
|
||||
testTextAtOffset(6, BOUNDARY_WORD_END, " my", 5, 8, IDs);
|
||||
|
@ -333,14 +333,14 @@
|
||||
"div", kOk, kOk, kOk,
|
||||
"editable", kOk, kOk, kOk,
|
||||
"textarea", kTodo, kOk, kTodo);
|
||||
testTextAtOffset(22, BOUNDARY_WORD_START, "", 22, 22,
|
||||
testTextAtOffset(22, BOUNDARY_WORD_START, "ran", 19, 22,
|
||||
"input", kOk, kOk, kOk,
|
||||
"div", kOk, kOk, kOk,
|
||||
"editable", kOk, kOk, kOk,
|
||||
"textarea", kTodo, kTodo, kTodo);
|
||||
"textarea", kTodo, kOk, kTodo);
|
||||
|
||||
// BOUNDARY_WORD_END
|
||||
testTextAtOffset(0, BOUNDARY_WORD_END, "", 0, 0, IDs);
|
||||
testTextAtOffset(0, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
|
||||
testTextAtOffset(4, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
|
||||
testTextAtOffset(5, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
|
||||
testTextAtOffset(6, BOUNDARY_WORD_END, " Sir", 5, 9, IDs);
|
||||
|
@ -27,6 +27,7 @@ MOCHITEST_A11Y_FILES =\
|
||||
test_listbox.xul \
|
||||
test_menu.xul \
|
||||
test_menubutton.xul \
|
||||
test_optgroup.html \
|
||||
test_recreation.html \
|
||||
test_select.html \
|
||||
test_textleaf.html \
|
||||
|
133
accessible/tests/mochitest/treeupdate/test_optgroup.html
Normal file
133
accessible/tests/mochitest/treeupdate/test_optgroup.html
Normal file
@ -0,0 +1,133 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Add and remove optgroup test</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
function addOptGroup(aID)
|
||||
{
|
||||
this.selectNode = getNode(aID);
|
||||
this.select = getAccessible(this.selectNode);
|
||||
|
||||
this.invoke = function addOptGroup_invoke()
|
||||
{
|
||||
var optGroup = document.createElement("optgroup");
|
||||
for (i = 0; i < 2; i++) {
|
||||
var opt = document.createElement("option");
|
||||
opt.value = i;
|
||||
opt.text = "Option: Value " + i;
|
||||
|
||||
optGroup.appendChild(opt);
|
||||
}
|
||||
|
||||
this.selectNode.add(optGroup, null);
|
||||
var option = document.createElement("option");
|
||||
this.selectNode.add(option, null);
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, this.select)
|
||||
];
|
||||
|
||||
this.finalCheck = function addOptGroup_finalCheck()
|
||||
{
|
||||
var tree =
|
||||
{ COMBOBOX: [
|
||||
{ COMBOBOX_LIST: [
|
||||
{ GROUPING: [
|
||||
{ COMBOBOX_OPTION: [
|
||||
{ TEXT_LEAF: [] }
|
||||
] },
|
||||
{ COMBOBOX_OPTION: [
|
||||
{ TEXT_LEAF: [] }
|
||||
] },
|
||||
]},
|
||||
{ COMBOBOX_OPTION: [] }
|
||||
] }
|
||||
] };
|
||||
testAccessibleTree(this.select, tree);
|
||||
}
|
||||
|
||||
this.getID = function addOptGroup_getID()
|
||||
{
|
||||
return "test optgroup's insertion into a select";
|
||||
}
|
||||
}
|
||||
|
||||
function removeOptGroup(aID)
|
||||
{
|
||||
this.selectNode = getNode(aID);
|
||||
this.select = getAccessible(this.selectNode);
|
||||
|
||||
this.invoke = function removeOptGroup_invoke()
|
||||
{
|
||||
this.option1Node = this.selectNode.firstChild.firstChild;
|
||||
this.selectNode.removeChild(this.selectNode.firstChild);
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, this.select)
|
||||
];
|
||||
|
||||
this.finalCheck = function removeOptGroup_finalCheck()
|
||||
{
|
||||
var tree =
|
||||
{ COMBOBOX: [
|
||||
{ COMBOBOX_LIST: [
|
||||
{ COMBOBOX_OPTION: [] }
|
||||
] }
|
||||
] };
|
||||
testAccessibleTree(this.select, tree);
|
||||
is(isAccessible(this.option1Node), false, "removed option shouldn't be accessible anymore!");
|
||||
}
|
||||
|
||||
this.getID = function removeOptGroup_getID()
|
||||
{
|
||||
return "test optgroup's removal from a select";
|
||||
}
|
||||
}
|
||||
|
||||
// gA11yEventDumpToConsole = true;
|
||||
|
||||
function doTest()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.push(new addOptGroup("select"));
|
||||
gQueue.push(new removeOptGroup("select"));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=616452"
|
||||
title="Bug 616452 - Dynamically inserted select options aren't reflected in accessible tree">
|
||||
Bug 616452</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<select id="select"></select>
|
||||
|
||||
<div id="debug"/>
|
||||
</body>
|
||||
</html>
|
@ -203,6 +203,8 @@ let FormAssistant = {
|
||||
scrollIntoViewTimeout: null,
|
||||
_focusedElement: null,
|
||||
_documentEncoder: null,
|
||||
_editor: null,
|
||||
_editing: false,
|
||||
|
||||
get focusedElement() {
|
||||
if (this._focusedElement && Cu.isDeadWrapper(this._focusedElement))
|
||||
@ -228,6 +230,10 @@ let FormAssistant = {
|
||||
}
|
||||
|
||||
this._documentEncoder = null;
|
||||
if (this._editor) {
|
||||
this._editor.removeEditorObserver(this);
|
||||
this._editor = null;
|
||||
}
|
||||
|
||||
if (element) {
|
||||
element.addEventListener('mousedown', this);
|
||||
@ -235,6 +241,12 @@ let FormAssistant = {
|
||||
if (isContentEditable(element)) {
|
||||
this._documentEncoder = getDocumentEncoder(element);
|
||||
}
|
||||
this._editor = getPlaintextEditor(element);
|
||||
if (this._editor) {
|
||||
// Add a nsIEditorObserver to monitor the text content of the focused
|
||||
// element.
|
||||
this._editor.addEditorObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
this.focusedElement = element;
|
||||
@ -244,6 +256,15 @@ let FormAssistant = {
|
||||
return this._documentEncoder;
|
||||
},
|
||||
|
||||
// Implements nsIEditorObserver get notification when the text content of
|
||||
// current input field has changed.
|
||||
EditAction: function fa_editAction() {
|
||||
if (this._editing) {
|
||||
return;
|
||||
}
|
||||
this.sendKeyboardState(this.focusedElement);
|
||||
},
|
||||
|
||||
handleEvent: function fa_handleEvent(evt) {
|
||||
let target = evt.target;
|
||||
|
||||
@ -331,10 +352,14 @@ let FormAssistant = {
|
||||
break;
|
||||
|
||||
case "keydown":
|
||||
// Don't monitor the text change resulting from key event.
|
||||
this._editing = true;
|
||||
|
||||
// We use 'setTimeout' to wait until the input element accomplishes the
|
||||
// change in selection range
|
||||
// change in selection range or text content.
|
||||
content.setTimeout(function() {
|
||||
this.updateSelection();
|
||||
this._editing = false;
|
||||
}.bind(this), 0);
|
||||
break;
|
||||
}
|
||||
@ -346,6 +371,7 @@ let FormAssistant = {
|
||||
return;
|
||||
}
|
||||
|
||||
this._editing = true;
|
||||
let json = msg.json;
|
||||
switch (msg.name) {
|
||||
case "Forms:Input:Value": {
|
||||
@ -396,6 +422,8 @@ let FormAssistant = {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._editing = false;
|
||||
|
||||
},
|
||||
|
||||
showKeyboard: function fa_showKeyboard(target) {
|
||||
@ -525,7 +553,7 @@ function getJSON(element) {
|
||||
let attributeType = element.getAttribute("type") || "";
|
||||
|
||||
if (attributeType) {
|
||||
var typeLowerCase = attributeType.toLowerCase();
|
||||
var typeLowerCase = attributeType.toLowerCase();
|
||||
switch (typeLowerCase) {
|
||||
case "datetime":
|
||||
case "datetime-local":
|
||||
@ -724,3 +752,27 @@ function setSelectionRange(element, start, end) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get nsIPlaintextEditor object from an input field
|
||||
function getPlaintextEditor(element) {
|
||||
let editor = null;
|
||||
// Get nsIEditor
|
||||
if (element instanceof HTMLInputElement ||
|
||||
element instanceof HTMLTextAreaElement) {
|
||||
// Get from the <input> and <textarea> elements
|
||||
editor = element.QueryInterface(Ci.nsIDOMNSEditableElement).editor;
|
||||
} else if (isContentEditable(element)) {
|
||||
// Get from content editable element
|
||||
let win = element.ownerDocument.defaultView;
|
||||
let editingSession = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIEditingSession);
|
||||
if (editingSession) {
|
||||
editor = editingSession.getEditorForWindow(win);
|
||||
}
|
||||
}
|
||||
if (editor) {
|
||||
editor.QueryInterface(Ci.nsIPlaintextEditor);
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
@ -37,9 +37,6 @@
|
||||
label="&bookmarkThisLinkCmd.label;"
|
||||
accesskey="&bookmarkThisLinkCmd.accesskey;"
|
||||
oncommand="gContextMenu.bookmarkLink();"/>
|
||||
<menuitem id="context-marklink"
|
||||
accesskey="&social.marklink.accesskey;"
|
||||
oncommand="gContextMenu.markLink();"/>
|
||||
<menuitem id="context-savelink"
|
||||
label="&saveLinkCmd.label;"
|
||||
accesskey="&saveLinkCmd.accesskey;"
|
||||
@ -225,9 +222,6 @@
|
||||
label="&bookmarkPageCmd2.label;"
|
||||
accesskey="&bookmarkPageCmd2.accesskey;"
|
||||
oncommand="gContextMenu.bookmarkThisPage();"/>
|
||||
<menuitem id="context-markpage"
|
||||
accesskey="&social.markpage.accesskey;"
|
||||
command="Social:TogglePageMark"/>
|
||||
<menuitem id="context-savepage"
|
||||
label="&savePageCmd.label;"
|
||||
accesskey="&savePageCmd.accesskey2;"
|
||||
|
@ -109,7 +109,8 @@
|
||||
<command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
|
||||
<command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
|
||||
<command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
|
||||
<command id="Social:TogglePageMark" oncommand="SocialMark.togglePageMark();" disabled="true"/>
|
||||
<command id="Social:SharePage" oncommand="SocialShareButton.sharePage();" disabled="true"/>
|
||||
<command id="Social:UnsharePage" oncommand="SocialShareButton.unsharePage();"/>
|
||||
<command id="Social:ToggleSidebar" oncommand="Social.toggleSidebar();"/>
|
||||
<command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
|
||||
<command id="Social:FocusChat" oncommand="SocialChatBar.focus();" hidden="true" disabled="true"/>
|
||||
@ -349,8 +350,8 @@
|
||||
# overridden for other purposes there.
|
||||
<key id="viewBookmarksSidebarWinKb" key="&bookmarksWinCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
|
||||
#endif
|
||||
|
||||
<key id="markPage" key="&markPageCmd.commandkey;" command="Social:TogglePageMark" modifiers="accel,shift"/>
|
||||
|
||||
<key id="sharePage" key="&sharePageCmd.commandkey;" command="Social:SharePage" modifiers="accel,shift"/>
|
||||
<key id="focusChatBar" key="&social.chatBar.commandkey;" command="Social:FocusChat" modifiers="accel,shift"/>
|
||||
|
||||
<key id="key_stop" keycode="VK_ESCAPE" command="Browser:Stop"/>
|
||||
|
@ -6,7 +6,7 @@
|
||||
let SocialUI,
|
||||
SocialChatBar,
|
||||
SocialFlyout,
|
||||
SocialMark,
|
||||
SocialShareButton,
|
||||
SocialMenu,
|
||||
SocialToolbar,
|
||||
SocialSidebar;
|
||||
@ -25,7 +25,7 @@ SocialUI = {
|
||||
init: function SocialUI_init() {
|
||||
Services.obs.addObserver(this, "social:ambient-notification-changed", false);
|
||||
Services.obs.addObserver(this, "social:profile-changed", false);
|
||||
Services.obs.addObserver(this, "social:page-mark-config", false);
|
||||
Services.obs.addObserver(this, "social:recommend-info-changed", false);
|
||||
Services.obs.addObserver(this, "social:frameworker-error", false);
|
||||
Services.obs.addObserver(this, "social:provider-set", false);
|
||||
Services.obs.addObserver(this, "social:providers-changed", false);
|
||||
@ -42,7 +42,7 @@ SocialUI = {
|
||||
});
|
||||
|
||||
SocialChatBar.init();
|
||||
SocialMark.init();
|
||||
SocialShareButton.init();
|
||||
SocialMenu.init();
|
||||
SocialToolbar.init();
|
||||
SocialSidebar.init();
|
||||
@ -61,7 +61,7 @@ SocialUI = {
|
||||
uninit: function SocialUI_uninit() {
|
||||
Services.obs.removeObserver(this, "social:ambient-notification-changed");
|
||||
Services.obs.removeObserver(this, "social:profile-changed");
|
||||
Services.obs.removeObserver(this, "social:page-mark-config");
|
||||
Services.obs.removeObserver(this, "social:recommend-info-changed");
|
||||
Services.obs.removeObserver(this, "social:frameworker-error");
|
||||
Services.obs.removeObserver(this, "social:provider-set");
|
||||
Services.obs.removeObserver(this, "social:providers-changed");
|
||||
@ -88,7 +88,7 @@ SocialUI = {
|
||||
SocialFlyout.unload();
|
||||
SocialChatBar.update();
|
||||
SocialSidebar.update();
|
||||
SocialMark.update();
|
||||
SocialShareButton.update();
|
||||
SocialToolbar.update();
|
||||
SocialMenu.populate();
|
||||
break;
|
||||
@ -109,13 +109,13 @@ SocialUI = {
|
||||
case "social:profile-changed":
|
||||
if (this._matchesCurrentProvider(data)) {
|
||||
SocialToolbar.updateProfile();
|
||||
SocialMark.update();
|
||||
SocialShareButton.update();
|
||||
SocialChatBar.update();
|
||||
}
|
||||
break;
|
||||
case "social:page-mark-config":
|
||||
case "social:recommend-info-changed":
|
||||
if (this._matchesCurrentProvider(data)) {
|
||||
SocialMark.updateMarkState();
|
||||
SocialShareButton.updateShareState();
|
||||
}
|
||||
break;
|
||||
case "social:frameworker-error":
|
||||
@ -566,81 +566,146 @@ SocialFlyout = {
|
||||
}
|
||||
}
|
||||
|
||||
SocialMark = {
|
||||
SocialShareButton = {
|
||||
// Called once, after window load, when the Social.provider object is initialized
|
||||
init: function SSB_init() {
|
||||
},
|
||||
|
||||
get button() {
|
||||
return document.getElementById("social-mark-button");
|
||||
// Called when the Social.provider changes
|
||||
update: function() {
|
||||
this._updateButtonHiddenState();
|
||||
let profileRow = document.getElementById("unsharePopupHeader");
|
||||
let profile = SocialUI.enabled ? Social.provider.profile : null;
|
||||
if (profile && profile.displayName) {
|
||||
profileRow.hidden = false;
|
||||
let portrait = document.getElementById("socialUserPortrait");
|
||||
if (profile.portrait) {
|
||||
portrait.setAttribute("src", profile.portrait);
|
||||
} else {
|
||||
portrait.removeAttribute("src");
|
||||
}
|
||||
let displayName = document.getElementById("socialUserDisplayName");
|
||||
displayName.setAttribute("label", profile.displayName);
|
||||
} else {
|
||||
profileRow.hidden = true;
|
||||
}
|
||||
},
|
||||
|
||||
canMarkPage: function SSB_canMarkPage(aURI) {
|
||||
get shareButton() {
|
||||
return document.getElementById("share-button");
|
||||
},
|
||||
get unsharePopup() {
|
||||
return document.getElementById("unsharePopup");
|
||||
},
|
||||
|
||||
dismissUnsharePopup: function SSB_dismissUnsharePopup() {
|
||||
this.unsharePopup.hidePopup();
|
||||
},
|
||||
|
||||
canSharePage: function SSB_canSharePage(aURI) {
|
||||
// We only allow sharing of http or https
|
||||
return aURI && (aURI.schemeIs('http') || aURI.schemeIs('https'));
|
||||
},
|
||||
|
||||
// Called when the Social.provider changes
|
||||
update: function SSB_updateButtonState() {
|
||||
let markButton = this.button;
|
||||
// always show button if provider supports marks
|
||||
markButton.hidden = !SocialUI.enabled || Social.provider.pageMarkInfo == null;
|
||||
markButton.disabled = markButton.hidden || !this.canMarkPage(gBrowser.currentURI);
|
||||
_updateButtonHiddenState: function SSB_updateButtonHiddenState() {
|
||||
let shareButton = this.shareButton;
|
||||
if (shareButton)
|
||||
shareButton.hidden = !SocialUI.enabled || Social.provider.recommendInfo == null ||
|
||||
!Social.haveLoggedInUser() ||
|
||||
!this.canSharePage(gBrowser.currentURI);
|
||||
|
||||
// also update the relevent command's disabled state so the keyboard
|
||||
// shortcut only works when available.
|
||||
let cmd = document.getElementById("Social:TogglePageMark");
|
||||
cmd.setAttribute("disabled", markButton.disabled ? "true" : "false");
|
||||
let cmd = document.getElementById("Social:SharePage");
|
||||
cmd.setAttribute("disabled", shareButton.hidden ? "true" : "false");
|
||||
},
|
||||
|
||||
togglePageMark: function(aCallback) {
|
||||
if (this.button.disabled)
|
||||
onClick: function SSB_onClick(aEvent) {
|
||||
if (aEvent.button != 0)
|
||||
return;
|
||||
this.toggleURIMark(gBrowser.currentURI, aCallback)
|
||||
},
|
||||
|
||||
toggleURIMark: function(aURI, aCallback) {
|
||||
let update = function(marked) {
|
||||
this._updateMarkState(marked);
|
||||
if (aCallback)
|
||||
aCallback(marked);
|
||||
}.bind(this);
|
||||
Social.isURIMarked(aURI, function(marked) {
|
||||
if (marked) {
|
||||
Social.unmarkURI(aURI, update);
|
||||
} else {
|
||||
Social.markURI(aURI, update);
|
||||
}
|
||||
});
|
||||
|
||||
// Don't bubble to the textbox, to avoid unwanted selection of the address.
|
||||
aEvent.stopPropagation();
|
||||
|
||||
this.sharePage();
|
||||
},
|
||||
|
||||
updateMarkState: function SSB_updateMarkState() {
|
||||
this.update();
|
||||
Social.isURIMarked(gBrowser.currentURI, this._updateMarkState.bind(this));
|
||||
panelShown: function SSB_panelShown(aEvent) {
|
||||
function updateElement(id, attrs) {
|
||||
let el = document.getElementById(id);
|
||||
Object.keys(attrs).forEach(function(attr) {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
});
|
||||
}
|
||||
let continueSharingButton = document.getElementById("unsharePopupContinueSharingButton");
|
||||
continueSharingButton.focus();
|
||||
let recommendInfo = Social.provider.recommendInfo;
|
||||
updateElement("unsharePopupContinueSharingButton",
|
||||
{label: recommendInfo.messages.unshareCancelLabel,
|
||||
accesskey: recommendInfo.messages.unshareCancelAccessKey});
|
||||
updateElement("unsharePopupStopSharingButton",
|
||||
{label: recommendInfo.messages.unshareConfirmLabel,
|
||||
accesskey: recommendInfo.messages.unshareConfirmAccessKey});
|
||||
updateElement("socialUserPortrait",
|
||||
{"aria-label": recommendInfo.messages.portraitLabel});
|
||||
updateElement("socialUserRecommendedText",
|
||||
{value: recommendInfo.messages.unshareLabel});
|
||||
},
|
||||
|
||||
_updateMarkState: function(currentPageMarked) {
|
||||
// callback for isURIMarked
|
||||
let markButton = this.button;
|
||||
let pageMarkInfo = SocialUI.enabled ? Social.provider.pageMarkInfo : null;
|
||||
sharePage: function SSB_sharePage() {
|
||||
this.unsharePopup.hidden = false;
|
||||
|
||||
// Update the mark button, if present
|
||||
if (!markButton || markButton.hidden || !pageMarkInfo)
|
||||
let uri = gBrowser.currentURI;
|
||||
if (!Social.isPageShared(uri)) {
|
||||
Social.sharePage(uri);
|
||||
this.updateShareState();
|
||||
} else {
|
||||
this.unsharePopup.openPopup(this.shareButton, "bottomcenter topright");
|
||||
}
|
||||
},
|
||||
|
||||
unsharePage: function SSB_unsharePage() {
|
||||
Social.unsharePage(gBrowser.currentURI);
|
||||
this.updateShareState();
|
||||
this.dismissUnsharePopup();
|
||||
},
|
||||
|
||||
updateShareState: function SSB_updateShareState() {
|
||||
this._updateButtonHiddenState();
|
||||
|
||||
let shareButton = this.shareButton;
|
||||
let currentPageShared = shareButton && !shareButton.hidden && Social.isPageShared(gBrowser.currentURI);
|
||||
|
||||
let recommendInfo = SocialUI.enabled ? Social.provider.recommendInfo : null;
|
||||
// Provide a11y-friendly notification of share.
|
||||
let status = document.getElementById("share-button-status");
|
||||
if (status) {
|
||||
// XXX - this should also be capable of reflecting that the page was
|
||||
// unshared (ie, it needs to manage three-states: (1) nothing done, (2)
|
||||
// shared, (3) shared then unshared)
|
||||
// Note that we *do* have an appropriate string from the provider for
|
||||
// this (recommendInfo.messages.unsharedLabel) but currently lack a way of
|
||||
// tracking this state)
|
||||
let statusString = currentPageShared && recommendInfo ?
|
||||
recommendInfo.messages.sharedLabel : "";
|
||||
status.setAttribute("value", statusString);
|
||||
}
|
||||
|
||||
// Update the share button, if present
|
||||
if (!shareButton || shareButton.hidden)
|
||||
return;
|
||||
|
||||
let imageURL;
|
||||
if (!markButton.disabled && currentPageMarked) {
|
||||
markButton.setAttribute("marked", "true");
|
||||
markButton.setAttribute("label", pageMarkInfo.messages.markedLabel);
|
||||
markButton.setAttribute("tooltiptext", pageMarkInfo.messages.markedTooltip);
|
||||
imageURL = pageMarkInfo.images.marked;
|
||||
if (currentPageShared) {
|
||||
shareButton.setAttribute("shared", "true");
|
||||
shareButton.setAttribute("tooltiptext", recommendInfo.messages.unshareTooltip);
|
||||
imageURL = recommendInfo.images.unshare;
|
||||
} else {
|
||||
markButton.removeAttribute("marked");
|
||||
markButton.setAttribute("label", pageMarkInfo.messages.unmarkedLabel);
|
||||
markButton.setAttribute("tooltiptext", pageMarkInfo.messages.unmarkedTooltip);
|
||||
imageURL = pageMarkInfo.images.unmarked;
|
||||
shareButton.removeAttribute("shared");
|
||||
shareButton.setAttribute("tooltiptext", recommendInfo.messages.shareTooltip);
|
||||
imageURL = recommendInfo.images.share;
|
||||
}
|
||||
markButton.style.listStyleImage = "url(" + imageURL + ")";
|
||||
shareButton.src = imageURL;
|
||||
}
|
||||
};
|
||||
|
||||
@ -729,12 +794,8 @@ SocialToolbar = {
|
||||
|
||||
let tbi = document.getElementById("social-toolbar-item");
|
||||
if (tbi) {
|
||||
// SocialMark is the last button allways
|
||||
let next = SocialMark.button.previousSibling;
|
||||
while (next != tbi.firstChild) {
|
||||
tbi.removeChild(next);
|
||||
next = SocialMark.button.previousSibling;
|
||||
}
|
||||
while (tbi.lastChild != tbi.firstChild)
|
||||
tbi.removeChild(tbi.lastChild);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -882,7 +943,7 @@ SocialToolbar = {
|
||||
toolbarButton.setAttribute("aria-label", ariaLabel);
|
||||
}
|
||||
let socialToolbarItem = document.getElementById("social-toolbar-item");
|
||||
socialToolbarItem.insertBefore(toolbarButtons, SocialMark.button);
|
||||
socialToolbarItem.appendChild(toolbarButtons);
|
||||
|
||||
for (let frame of createdFrames) {
|
||||
if (frame.socialErrorListener) {
|
||||
|
@ -3405,7 +3405,7 @@ function BrowserToolboxCustomizeDone(aToolboxChanged) {
|
||||
URLBarSetURI();
|
||||
XULBrowserWindow.asyncUpdateUI();
|
||||
BookmarksMenuButton.updateStarState();
|
||||
SocialMark.updateMarkState();
|
||||
SocialShareButton.updateShareState();
|
||||
}
|
||||
|
||||
TabsInTitlebar.allowedBy("customizing-toolbars", true);
|
||||
@ -3879,7 +3879,7 @@ var XULBrowserWindow = {
|
||||
|
||||
// Update starring UI
|
||||
BookmarksMenuButton.updateStarState();
|
||||
SocialMark.updateMarkState();
|
||||
SocialShareButton.updateShareState();
|
||||
}
|
||||
|
||||
// Show or hide browser chrome based on the whitelist
|
||||
|
@ -205,6 +205,52 @@
|
||||
</vbox>
|
||||
</panel>
|
||||
|
||||
<panel id="unsharePopup"
|
||||
type="arrow"
|
||||
orient="vertical"
|
||||
ignorekeys="true"
|
||||
hidden="true"
|
||||
onpopupshown="SocialShareButton.panelShown(event);"
|
||||
consumeoutsideclicks="true"
|
||||
level="top">
|
||||
<!-- Note that 'label', 'accesskey', 'value' and 'aria-label' attributes
|
||||
for many of these elements are supplied by the provider and filled
|
||||
in at runtime
|
||||
-->
|
||||
<row id="unsharePopupHeader" align="center">
|
||||
<vbox align="center">
|
||||
<image id="socialUserPortrait" onclick="SocialUI.showProfile();"/>
|
||||
</vbox>
|
||||
<vbox id="unsharePopupText">
|
||||
<button id="socialUserDisplayName" pack="start"
|
||||
oncommand="SocialUI.showProfile();"/>
|
||||
<spacer flex="1"/>
|
||||
<label id="socialUserRecommendedText"/>
|
||||
</vbox>
|
||||
</row>
|
||||
<hbox id="unsharePopupBottomButtons" pack="end">
|
||||
#ifdef XP_UNIX
|
||||
<button id="unsharePopupStopSharingButton"
|
||||
class="unsharePopupBottomButton"
|
||||
command="Social:UnsharePage"/>
|
||||
<button id="unsharePopupContinueSharingButton"
|
||||
class="unsharePopupBottomButton"
|
||||
default="true"
|
||||
autofocus="autofocus"
|
||||
oncommand="SocialShareButton.dismissUnsharePopup();"/>
|
||||
#else
|
||||
<button id="unsharePopupContinueSharingButton"
|
||||
class="unsharePopupBottomButton"
|
||||
default="true"
|
||||
autofocus="autofocus"
|
||||
oncommand="SocialShareButton.dismissUnsharePopup();"/>
|
||||
<button id="unsharePopupStopSharingButton"
|
||||
class="unsharePopupBottomButton"
|
||||
command="Social:UnsharePage"/>
|
||||
#endif
|
||||
</hbox>
|
||||
</panel>
|
||||
|
||||
<panel id="social-notification-panel"
|
||||
class="social-panel"
|
||||
type="arrow"
|
||||
@ -567,6 +613,13 @@
|
||||
hidden="true"
|
||||
tooltiptext="&pageReportIcon.tooltip;"
|
||||
onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
|
||||
|
||||
<label id="share-button-status" collapsed="true" role="status"/>
|
||||
<image id="share-button"
|
||||
class="urlbar-icon"
|
||||
hidden="true"
|
||||
onclick="SocialShareButton.onClick(event);"/>
|
||||
|
||||
<image id="go-button"
|
||||
class="urlbar-icon"
|
||||
tooltiptext="&goEndCap.tooltip;"
|
||||
@ -749,9 +802,6 @@
|
||||
oncommand="SocialUI.showLearnMore();"/>
|
||||
</menupopup>
|
||||
</toolbarbutton>
|
||||
<toolbarbutton id="social-mark-button"
|
||||
class="toolbarbutton-1"
|
||||
command="Social:TogglePageMark"/>
|
||||
</toolbaritem>
|
||||
|
||||
<hbox id="window-controls" hidden="true" pack="end">
|
||||
|
@ -292,32 +292,6 @@ nsContextMenu.prototype = {
|
||||
this.onTextInput && top.gBidiUI);
|
||||
this.showItem("context-bidi-page-direction-toggle",
|
||||
!this.onTextInput && top.gBidiUI);
|
||||
|
||||
// SocialMarks
|
||||
let marksEnabled = SocialUI.enabled && Social.provider.pageMarkInfo;
|
||||
let enablePageMark = marksEnabled && !(this.isContentSelected ||
|
||||
this.onTextInput || this.onLink || this.onImage ||
|
||||
this.onVideo || this.onAudio || this.onSocial);
|
||||
let enableLinkMark = marksEnabled && ((this.onLink && !this.onMailtoLink &&
|
||||
!this.onSocial) || this.onPlainTextLink);
|
||||
if (enablePageMark) {
|
||||
Social.isURIMarked(gBrowser.currentURI, function(marked) {
|
||||
let label = marked ? "social.unmarkpage.label" : "social.markpage.label";
|
||||
let provider = Social.provider || Social.defaultProvider;
|
||||
let menuLabel = gNavigatorBundle.getFormattedString(label, [provider.name]);
|
||||
this.setItemAttr("context-markpage", "label", menuLabel);
|
||||
}.bind(this));
|
||||
}
|
||||
this.showItem("context-markpage", enablePageMark);
|
||||
if (enableLinkMark) {
|
||||
Social.isURIMarked(this.linkURI, function(marked) {
|
||||
let label = marked ? "social.unmarklink.label" : "social.marklink.label";
|
||||
let provider = Social.provider || Social.defaultProvider;
|
||||
let menuLabel = gNavigatorBundle.getFormattedString(label, [provider.name]);
|
||||
this.setItemAttr("context-marklink", "label", menuLabel);
|
||||
}.bind(this));
|
||||
}
|
||||
this.showItem("context-marklink", enableLinkMark);
|
||||
},
|
||||
|
||||
initSpellingItems: function() {
|
||||
@ -1493,11 +1467,6 @@ nsContextMenu.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
markLink: function CM_markLink() {
|
||||
// send link to social
|
||||
SocialMark.toggleURIMark(this.linkURI);
|
||||
},
|
||||
|
||||
savePageAs: function CM_savePageAs() {
|
||||
saveDocument(this.browser.contentDocument);
|
||||
},
|
||||
|
@ -20,7 +20,7 @@ _BROWSER_FILES = \
|
||||
browser_social_activation.js \
|
||||
browser_social_perwindowPB.js \
|
||||
browser_social_toolbar.js \
|
||||
browser_social_markButton.js \
|
||||
browser_social_shareButton.js \
|
||||
browser_social_sidebar.js \
|
||||
browser_social_flyout.js \
|
||||
browser_social_mozSocial_API.js \
|
||||
@ -33,7 +33,7 @@ _BROWSER_FILES = \
|
||||
social_activate.html \
|
||||
social_activate_iframe.html \
|
||||
social_panel.html \
|
||||
social_mark_image.png \
|
||||
social_share_image.png \
|
||||
social_sidebar.html \
|
||||
social_chat.html \
|
||||
social_flyout.html \
|
||||
|
@ -1,210 +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/. */
|
||||
|
||||
let prefName = "social.enabled",
|
||||
gFinishCB;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Need to load a http/https/ftp/ftps page for the social mark button to appear
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("https://example.com", {skipAnimation: true});
|
||||
tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
|
||||
tab.linkedBrowser.removeEventListener("load", tabLoad, true);
|
||||
executeSoon(tabLoaded);
|
||||
}, true);
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref(prefName);
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
}
|
||||
|
||||
function tabLoaded() {
|
||||
ok(Social, "Social module loaded");
|
||||
|
||||
let manifest = { // normal provider
|
||||
name: "provider 1",
|
||||
origin: "https://example.com",
|
||||
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
|
||||
};
|
||||
runSocialTestWithProvider(manifest, function (finishcb) {
|
||||
gFinishCB = finishcb;
|
||||
testInitial();
|
||||
});
|
||||
}
|
||||
|
||||
function testInitial(finishcb) {
|
||||
ok(Social.provider, "Social provider is active");
|
||||
ok(Social.provider.enabled, "Social provider is enabled");
|
||||
let port = Social.provider.getWorkerPort();
|
||||
ok(port, "Social provider has a port to its FrameWorker");
|
||||
port.close();
|
||||
|
||||
let markButton = SocialMark.button;
|
||||
ok(markButton, "mark button exists");
|
||||
|
||||
// ensure the worker initialization and handshakes are all done and we
|
||||
// have a profile and the worker has sent a page-mark-config msg.
|
||||
waitForCondition(function() Social.provider.pageMarkInfo != null, function() {
|
||||
is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' attribute before mark button is clicked");
|
||||
// Check the strings from our worker actually ended up on the button.
|
||||
is(markButton.getAttribute("tooltiptext"), "Mark this page", "check tooltip text is correct");
|
||||
// Check the relative URL was resolved correctly (note this image has offsets of zero...)
|
||||
is(markButton.style.listStyleImage, 'url("https://example.com/browser/browser/base/content/test/social/social_mark_image.png")', "check image url is correct");
|
||||
|
||||
// Test the mark button command handler
|
||||
SocialMark.togglePageMark(function() {
|
||||
is(markButton.hasAttribute("marked"), true, "mark button should have 'marked' attribute after mark button is clicked");
|
||||
is(markButton.getAttribute("tooltiptext"), "Unmark this page", "check tooltip text is correct");
|
||||
// Check the URL and offsets were applied correctly
|
||||
is(markButton.style.listStyleImage, 'url("https://example.com/browser/browser/base/content/test/social/social_mark_image.png")', "check image url is correct");
|
||||
SocialMark.togglePageMark(function() {
|
||||
is(markButton.hasAttribute("marked"), false, "mark button should not be marked");
|
||||
executeSoon(function() {
|
||||
testStillMarkedIn2Tabs();
|
||||
});
|
||||
});
|
||||
});
|
||||
markButton.click();
|
||||
}, "provider didn't provide page-mark-config");
|
||||
}
|
||||
|
||||
function testStillMarkedIn2Tabs() {
|
||||
let toMark = "http://example.com";
|
||||
let markUri = Services.io.newURI(toMark, null, null);
|
||||
let markButton = SocialMark.button;
|
||||
let initialTab = gBrowser.selectedTab;
|
||||
is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' for the initial tab");
|
||||
let tab1 = gBrowser.selectedTab = gBrowser.addTab(toMark);
|
||||
let tab1b = gBrowser.getBrowserForTab(tab1);
|
||||
|
||||
tab1b.addEventListener("load", function tabLoad(event) {
|
||||
tab1b.removeEventListener("load", tabLoad, true);
|
||||
let tab2 = gBrowser.selectedTab = gBrowser.addTab(toMark);
|
||||
let tab2b = gBrowser.getBrowserForTab(tab2);
|
||||
tab2b.addEventListener("load", function tabLoad(event) {
|
||||
tab2b.removeEventListener("load", tabLoad, true);
|
||||
// should start without either page being marked.
|
||||
is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' before we've done anything");
|
||||
Social.isURIMarked(markUri, function(marked) {
|
||||
ok(!marked, "page is unmarked in annotations");
|
||||
markButton.click();
|
||||
waitForCondition(function() markButton.hasAttribute("marked"), function() {
|
||||
Social.isURIMarked(markUri, function(marked) {
|
||||
ok(marked, "page is marked in annotations");
|
||||
// and switching to the first tab (with the same URL) should still reflect marked.
|
||||
gBrowser.selectedTab = tab1;
|
||||
is(markButton.hasAttribute("marked"), true, "SocialMark button should reflect the marked state");
|
||||
// but switching back the initial one should reflect not marked.
|
||||
gBrowser.selectedTab = initialTab;
|
||||
waitForCondition(function() !markButton.hasAttribute("marked"), function() {
|
||||
gBrowser.selectedTab = tab1;
|
||||
|
||||
SocialMark.togglePageMark(function() {
|
||||
Social.isURIMarked(gBrowser.currentURI, function(marked) {
|
||||
ok(!marked, "page is unmarked in annotations");
|
||||
is(markButton.hasAttribute("marked"), false, "mark button should not be marked");
|
||||
gBrowser.removeTab(tab1);
|
||||
gBrowser.removeTab(tab2);
|
||||
executeSoon(testStillMarkedAfterReopen);
|
||||
});
|
||||
});
|
||||
}, "button has been unmarked");
|
||||
});
|
||||
}, "button has been marked");
|
||||
});
|
||||
|
||||
}, true);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function testStillMarkedAfterReopen() {
|
||||
let toMark = "http://example.com";
|
||||
let markButton = SocialMark.button;
|
||||
|
||||
is(markButton.hasAttribute("marked"), false, "Reopen: SocialMark button should not have 'marked' for the initial tab");
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(toMark);
|
||||
let tabb = gBrowser.getBrowserForTab(tab);
|
||||
tabb.addEventListener("load", function tabLoad(event) {
|
||||
tabb.removeEventListener("load", tabLoad, true);
|
||||
SocialMark.togglePageMark(function() {
|
||||
is(markButton.hasAttribute("marked"), true, "SocialMark button should reflect the marked state");
|
||||
gBrowser.removeTab(tab);
|
||||
// should be on the initial unmarked tab now.
|
||||
waitForCondition(function() !markButton.hasAttribute("marked"), function() {
|
||||
// now open the same URL - should be back to Marked.
|
||||
tab = gBrowser.selectedTab = gBrowser.addTab(toMark, {skipAnimation: true});
|
||||
tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
|
||||
tab.linkedBrowser.removeEventListener("load", tabLoad, true);
|
||||
executeSoon(function() {
|
||||
is(markButton.hasAttribute("marked"), true, "New tab to previously marked URL should reflect marked state");
|
||||
SocialMark.togglePageMark(function() {
|
||||
gBrowser.removeTab(tab);
|
||||
executeSoon(testOnlyMarkCertainUrlsTabSwitch);
|
||||
});
|
||||
});
|
||||
}, true);
|
||||
}, "button is now unmarked");
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
function testOnlyMarkCertainUrlsTabSwitch() {
|
||||
let toMark = "http://example.com";
|
||||
let notSharable = "about:blank";
|
||||
let markButton = SocialMark.button;
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(toMark);
|
||||
let tabb = gBrowser.getBrowserForTab(tab);
|
||||
tabb.addEventListener("load", function tabLoad(event) {
|
||||
tabb.removeEventListener("load", tabLoad, true);
|
||||
ok(!markButton.hidden, "SocialMark button not hidden for http url");
|
||||
let tab2 = gBrowser.selectedTab = gBrowser.addTab(notSharable);
|
||||
let tabb2 = gBrowser.getBrowserForTab(tab2);
|
||||
tabb2.addEventListener("load", function tabLoad(event) {
|
||||
tabb2.removeEventListener("load", tabLoad, true);
|
||||
ok(markButton.disabled, "SocialMark button disabled for about:blank");
|
||||
gBrowser.selectedTab = tab;
|
||||
ok(!markButton.disabled, "SocialMark button re-shown when switching back to http: url");
|
||||
gBrowser.selectedTab = tab2;
|
||||
ok(markButton.disabled, "SocialMark button re-hidden when switching back to about:blank");
|
||||
gBrowser.removeTab(tab);
|
||||
gBrowser.removeTab(tab2);
|
||||
executeSoon(testOnlyMarkCertainUrlsSameTab);
|
||||
}, true);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function testOnlyMarkCertainUrlsSameTab() {
|
||||
let toMark = "http://example.com";
|
||||
let notSharable = "about:blank";
|
||||
let markButton = SocialMark.button;
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(toMark);
|
||||
let tabb = gBrowser.getBrowserForTab(tab);
|
||||
tabb.addEventListener("load", function tabLoad(event) {
|
||||
tabb.removeEventListener("load", tabLoad, true);
|
||||
ok(!markButton.disabled, "SocialMark button not disabled for http url");
|
||||
tabb.addEventListener("load", function tabLoad(event) {
|
||||
tabb.removeEventListener("load", tabLoad, true);
|
||||
ok(markButton.disabled, "SocialMark button disabled for about:blank");
|
||||
tabb.addEventListener("load", function tabLoad(event) {
|
||||
tabb.removeEventListener("load", tabLoad, true);
|
||||
ok(!markButton.disabled, "SocialMark button re-enabled http url");
|
||||
gBrowser.removeTab(tab);
|
||||
executeSoon(testDisable);
|
||||
}, true);
|
||||
tabb.loadURI(toMark);
|
||||
}, true);
|
||||
tabb.loadURI(notSharable);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function testDisable() {
|
||||
let markButton = SocialMark.button;
|
||||
Services.prefs.setBoolPref(prefName, false);
|
||||
is(markButton.hidden, true, "SocialMark button should be hidden when pref is disabled");
|
||||
gFinishCB();
|
||||
}
|
@ -59,6 +59,10 @@ var tests = {
|
||||
|
||||
function checkUIStateMatchesProvider(provider) {
|
||||
let profileData = getExpectedProfileData(provider);
|
||||
// Bug 789863 - share button uses 'displayName', toolbar uses 'userName'
|
||||
// Check the "share button"
|
||||
let displayNameEl = document.getElementById("socialUserDisplayName");
|
||||
is(displayNameEl.getAttribute("label"), profileData.displayName, "display name matches provider profile");
|
||||
// The toolbar
|
||||
let loginStatus = document.getElementsByClassName("social-statusarea-loggedInStatus");
|
||||
for (let label of loginStatus) {
|
||||
|
328
browser/base/content/test/social/browser_social_shareButton.js
Normal file
328
browser/base/content/test/social/browser_social_shareButton.js
Normal file
@ -0,0 +1,328 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
let prefName = "social.enabled",
|
||||
gFinishCB;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Need to load a http/https/ftp/ftps page for the social share button to appear
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("https://example.com", {skipAnimation: true});
|
||||
tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
|
||||
tab.linkedBrowser.removeEventListener("load", tabLoad, true);
|
||||
executeSoon(tabLoaded);
|
||||
}, true);
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref(prefName);
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
}
|
||||
|
||||
function tabLoaded() {
|
||||
ok(Social, "Social module loaded");
|
||||
|
||||
let manifest = { // normal provider
|
||||
name: "provider 1",
|
||||
origin: "https://example.com",
|
||||
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
|
||||
};
|
||||
runSocialTestWithProvider(manifest, function (finishcb) {
|
||||
gFinishCB = finishcb;
|
||||
testInitial();
|
||||
});
|
||||
}
|
||||
|
||||
function testInitial(finishcb) {
|
||||
ok(Social.provider, "Social provider is active");
|
||||
ok(Social.provider.enabled, "Social provider is enabled");
|
||||
let port = Social.provider.getWorkerPort();
|
||||
ok(port, "Social provider has a port to its FrameWorker");
|
||||
port.close();
|
||||
|
||||
let {shareButton, unsharePopup} = SocialShareButton;
|
||||
ok(shareButton, "share button exists");
|
||||
ok(unsharePopup, "share popup exists");
|
||||
|
||||
let okButton = document.getElementById("unsharePopupContinueSharingButton");
|
||||
let undoButton = document.getElementById("unsharePopupStopSharingButton");
|
||||
let shareStatusLabel = document.getElementById("share-button-status");
|
||||
|
||||
// ensure the worker initialization and handshakes are all done and we
|
||||
// have a profile and the worker has responsed to the recommend-prompt msg.
|
||||
waitForCondition(function() Social.provider.profile && Social.provider.recommendInfo != null, function() {
|
||||
is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' attribute before share button is clicked");
|
||||
// check dom values
|
||||
let profile = Social.provider.profile;
|
||||
let portrait = document.getElementById("socialUserPortrait").getAttribute("src");
|
||||
is(profile.portrait, portrait, "portrait is set");
|
||||
let displayName = document.getElementById("socialUserDisplayName");
|
||||
is(displayName.label, profile.displayName, "display name is set");
|
||||
ok(!document.getElementById("unsharePopupHeader").hidden, "user profile is visible");
|
||||
|
||||
// Check the strings from our worker actually ended up on the button.
|
||||
is(shareButton.getAttribute("tooltiptext"), "Share this page", "check tooltip text is correct");
|
||||
is(shareStatusLabel.getAttribute("value"), "", "check status label text is blank");
|
||||
// Check the relative URL was resolved correctly (note this image has offsets of zero...)
|
||||
is(shareButton.src, 'https://example.com/browser/browser/base/content/test/social/social_share_image.png', "check image url is correct");
|
||||
|
||||
// Test clicking the share button
|
||||
shareButton.addEventListener("click", function listener() {
|
||||
shareButton.removeEventListener("click", listener);
|
||||
is(shareButton.hasAttribute("shared"), true, "Share button should have 'shared' attribute after share button is clicked");
|
||||
is(shareButton.getAttribute("tooltiptext"), "Unshare this page", "check tooltip text is correct");
|
||||
is(shareStatusLabel.getAttribute("value"), "This page has been shared", "check status label text is correct");
|
||||
// Check the URL and offsets were applied correctly
|
||||
is(shareButton.src, 'https://example.com/browser/browser/base/content/test/social/social_share_image.png', "check image url is correct");
|
||||
executeSoon(testSecondClick.bind(window, testPopupOKButton));
|
||||
});
|
||||
shareButton.click();
|
||||
}, "provider didn't provide user-recommend-prompt response");
|
||||
}
|
||||
|
||||
function testSecondClick(nextTest) {
|
||||
let {shareButton, unsharePopup} = SocialShareButton;
|
||||
unsharePopup.addEventListener("popupshown", function listener() {
|
||||
unsharePopup.removeEventListener("popupshown", listener);
|
||||
ok(true, "popup was shown after second click");
|
||||
executeSoon(nextTest);
|
||||
});
|
||||
shareButton.click();
|
||||
}
|
||||
|
||||
function testPopupOKButton() {
|
||||
let {shareButton, unsharePopup} = SocialShareButton;
|
||||
let okButton = document.getElementById("unsharePopupContinueSharingButton");
|
||||
unsharePopup.addEventListener("popuphidden", function listener() {
|
||||
unsharePopup.removeEventListener("popuphidden", listener);
|
||||
is(shareButton.hasAttribute("shared"), true, "Share button should still have 'shared' attribute after OK button is clicked");
|
||||
executeSoon(testSecondClick.bind(window, testPopupUndoButton));
|
||||
});
|
||||
okButton.click();
|
||||
}
|
||||
|
||||
function testPopupUndoButton() {
|
||||
let {shareButton, unsharePopup} = SocialShareButton;
|
||||
let undoButton = document.getElementById("unsharePopupStopSharingButton");
|
||||
unsharePopup.addEventListener("popuphidden", function listener() {
|
||||
unsharePopup.removeEventListener("popuphidden", listener);
|
||||
is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' attribute after Undo button is clicked");
|
||||
executeSoon(testShortcut);
|
||||
});
|
||||
undoButton.click();
|
||||
}
|
||||
|
||||
function testShortcut() {
|
||||
let keyTarget = window;
|
||||
keyTarget.addEventListener("keyup", function listener() {
|
||||
keyTarget.removeEventListener("keyup", listener);
|
||||
executeSoon(checkShortcutWorked.bind(window, keyTarget));
|
||||
});
|
||||
EventUtils.synthesizeKey("l", {accelKey: true, shiftKey: true}, keyTarget);
|
||||
}
|
||||
|
||||
function checkShortcutWorked(keyTarget) {
|
||||
let {unsharePopup, shareButton} = SocialShareButton;
|
||||
is(shareButton.hasAttribute("shared"), true, "Share button should be in the 'shared' state after keyboard shortcut is used");
|
||||
|
||||
// Test a second invocation of the shortcut
|
||||
unsharePopup.addEventListener("popupshown", function listener() {
|
||||
unsharePopup.removeEventListener("popupshown", listener);
|
||||
ok(true, "popup was shown after second use of keyboard shortcut");
|
||||
executeSoon(checkOKButton);
|
||||
});
|
||||
EventUtils.synthesizeKey("l", {accelKey: true, shiftKey: true}, keyTarget);
|
||||
}
|
||||
|
||||
function checkOKButton() {
|
||||
let okButton = document.getElementById("unsharePopupContinueSharingButton");
|
||||
let undoButton = document.getElementById("unsharePopupStopSharingButton");
|
||||
is(document.activeElement, okButton, "ok button should be focused by default");
|
||||
|
||||
// the undo button text, label text, access keys, etc should be as
|
||||
// specified by the provider.
|
||||
function isEltAttr(eltid, attr, expected) {
|
||||
is(document.getElementById(eltid).getAttribute(attr), expected,
|
||||
"element '" + eltid + "' has correct value for attribute '" + attr + "'");
|
||||
}
|
||||
isEltAttr("socialUserRecommendedText", "value", "You have already shared this page");
|
||||
isEltAttr("unsharePopupContinueSharingButton", "label", "Got it!");
|
||||
isEltAttr("unsharePopupContinueSharingButton", "accesskey", "G");
|
||||
isEltAttr("unsharePopupStopSharingButton", "label", "Unshare it!");
|
||||
isEltAttr("unsharePopupStopSharingButton", "accesskey", "U");
|
||||
isEltAttr("socialUserPortrait", "aria-label", "Your pretty face");
|
||||
|
||||
// This rest of particular test doesn't really apply on Mac, since buttons
|
||||
// aren't focusable by default.
|
||||
if (navigator.platform.contains("Mac")) {
|
||||
executeSoon(testCloseBySpace);
|
||||
return;
|
||||
}
|
||||
|
||||
let displayName = document.getElementById("socialUserDisplayName");
|
||||
|
||||
// Linux has the buttons in the [unshare] [ok] order, so displayName will come first.
|
||||
if (navigator.platform.contains("Linux")) {
|
||||
checkNextInTabOrder(displayName, function () {
|
||||
checkNextInTabOrder(undoButton, function () {
|
||||
checkNextInTabOrder(okButton, testCloseBySpace);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
checkNextInTabOrder(undoButton, function () {
|
||||
checkNextInTabOrder(displayName, function () {
|
||||
checkNextInTabOrder(okButton, testCloseBySpace);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkNextInTabOrder(element, next) {
|
||||
function listener() {
|
||||
element.removeEventListener("focus", listener);
|
||||
is(document.activeElement, element, element.id + " should be next in tab order");
|
||||
executeSoon(next);
|
||||
}
|
||||
element.addEventListener("focus", listener);
|
||||
// Register a cleanup function to remove the listener in case this test fails
|
||||
registerCleanupFunction(function () {
|
||||
element.removeEventListener("focus", listener);
|
||||
});
|
||||
EventUtils.synthesizeKey("VK_TAB", {});
|
||||
}
|
||||
|
||||
function testCloseBySpace() {
|
||||
let unsharePopup = SocialShareButton.unsharePopup;
|
||||
is(document.activeElement.id, "unsharePopupContinueSharingButton", "testCloseBySpace, the ok button should be focused");
|
||||
unsharePopup.addEventListener("popuphidden", function listener() {
|
||||
unsharePopup.removeEventListener("popuphidden", listener);
|
||||
ok(true, "space closed the share popup");
|
||||
executeSoon(testStillSharedIn2Tabs);
|
||||
});
|
||||
EventUtils.synthesizeKey("VK_SPACE", {});
|
||||
}
|
||||
|
||||
function testStillSharedIn2Tabs() {
|
||||
let toShare = "http://example.com";
|
||||
let {shareButton} = SocialShareButton;
|
||||
let initialTab = gBrowser.selectedTab;
|
||||
if (shareButton.hasAttribute("shared")) {
|
||||
SocialShareButton.unsharePage();
|
||||
}
|
||||
is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' for the initial tab");
|
||||
let tab1 = gBrowser.selectedTab = gBrowser.addTab(toShare);
|
||||
let tab1b = gBrowser.getBrowserForTab(tab1);
|
||||
|
||||
tab1b.addEventListener("load", function tabLoad(event) {
|
||||
tab1b.removeEventListener("load", tabLoad, true);
|
||||
let tab2 = gBrowser.selectedTab = gBrowser.addTab(toShare);
|
||||
let tab2b = gBrowser.getBrowserForTab(tab2);
|
||||
tab2b.addEventListener("load", function tabLoad(event) {
|
||||
tab2b.removeEventListener("load", tabLoad, true);
|
||||
// should start without either page being shared.
|
||||
is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' before we've done anything");
|
||||
shareButton.click();
|
||||
is(shareButton.hasAttribute("shared"), true, "Share button should reflect the share");
|
||||
// and switching to the first tab (with the same URL) should still reflect shared.
|
||||
gBrowser.selectedTab = tab1;
|
||||
is(shareButton.hasAttribute("shared"), true, "Share button should reflect the share");
|
||||
// but switching back the initial one should reflect not shared.
|
||||
gBrowser.selectedTab = initialTab;
|
||||
is(shareButton.hasAttribute("shared"), false, "Initial tab should not reflect shared");
|
||||
|
||||
gBrowser.selectedTab = tab1;
|
||||
SocialShareButton.unsharePage();
|
||||
gBrowser.removeTab(tab1);
|
||||
gBrowser.removeTab(tab2);
|
||||
executeSoon(testStillSharedAfterReopen);
|
||||
}, true);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function testStillSharedAfterReopen() {
|
||||
let toShare = "http://example.com";
|
||||
let {shareButton} = SocialShareButton;
|
||||
|
||||
is(shareButton.hasAttribute("shared"), false, "Reopen: Share button should not have 'shared' for the initial tab");
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(toShare);
|
||||
let tabb = gBrowser.getBrowserForTab(tab);
|
||||
tabb.addEventListener("load", function tabLoad(event) {
|
||||
tabb.removeEventListener("load", tabLoad, true);
|
||||
SocialShareButton.sharePage();
|
||||
is(shareButton.hasAttribute("shared"), true, "Share button should reflect the share");
|
||||
gBrowser.removeTab(tab);
|
||||
// should be on the initial unshared tab now.
|
||||
is(shareButton.hasAttribute("shared"), false, "Initial tab should be selected and be unshared.");
|
||||
// now open the same URL - should be back to shared.
|
||||
tab = gBrowser.selectedTab = gBrowser.addTab(toShare, {skipAnimation: true});
|
||||
tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
|
||||
tab.linkedBrowser.removeEventListener("load", tabLoad, true);
|
||||
executeSoon(function() {
|
||||
is(shareButton.hasAttribute("shared"), true, "New tab to previously shared URL should reflect shared");
|
||||
SocialShareButton.unsharePage();
|
||||
gBrowser.removeTab(tab);
|
||||
executeSoon(testOnlyShareCertainUrlsTabSwitch);
|
||||
});
|
||||
}, true);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function testOnlyShareCertainUrlsTabSwitch() {
|
||||
let toShare = "http://example.com";
|
||||
let notSharable = "about:blank";
|
||||
let {shareButton} = SocialShareButton;
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(toShare);
|
||||
let tabb = gBrowser.getBrowserForTab(tab);
|
||||
tabb.addEventListener("load", function tabLoad(event) {
|
||||
tabb.removeEventListener("load", tabLoad, true);
|
||||
ok(!shareButton.hidden, "share button not hidden for http url");
|
||||
let tab2 = gBrowser.selectedTab = gBrowser.addTab(notSharable);
|
||||
let tabb2 = gBrowser.getBrowserForTab(tab2);
|
||||
tabb2.addEventListener("load", function tabLoad(event) {
|
||||
tabb2.removeEventListener("load", tabLoad, true);
|
||||
ok(shareButton.hidden, "share button hidden for about:blank");
|
||||
gBrowser.selectedTab = tab;
|
||||
ok(!shareButton.hidden, "share button re-shown when switching back to http: url");
|
||||
gBrowser.selectedTab = tab2;
|
||||
ok(shareButton.hidden, "share button re-hidden when switching back to about:blank");
|
||||
gBrowser.removeTab(tab);
|
||||
gBrowser.removeTab(tab2);
|
||||
executeSoon(testOnlyShareCertainUrlsSameTab);
|
||||
}, true);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function testOnlyShareCertainUrlsSameTab() {
|
||||
let toShare = "http://example.com";
|
||||
let notSharable = "about:blank";
|
||||
let {shareButton} = SocialShareButton;
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(toShare);
|
||||
let tabb = gBrowser.getBrowserForTab(tab);
|
||||
tabb.addEventListener("load", function tabLoad(event) {
|
||||
tabb.removeEventListener("load", tabLoad, true);
|
||||
ok(!shareButton.hidden, "share button not hidden for http url");
|
||||
tabb.addEventListener("load", function tabLoad(event) {
|
||||
tabb.removeEventListener("load", tabLoad, true);
|
||||
ok(shareButton.hidden, "share button hidden for about:blank");
|
||||
tabb.addEventListener("load", function tabLoad(event) {
|
||||
tabb.removeEventListener("load", tabLoad, true);
|
||||
ok(!shareButton.hidden, "share button re-enabled http url");
|
||||
gBrowser.removeTab(tab);
|
||||
executeSoon(testDisable);
|
||||
}, true);
|
||||
tabb.loadURI(toShare);
|
||||
}, true);
|
||||
tabb.loadURI(notSharable);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function testDisable() {
|
||||
let shareButton = SocialShareButton.shareButton;
|
||||
Services.prefs.setBoolPref(prefName, false);
|
||||
is(shareButton.hidden, true, "Share button should be hidden when pref is disabled");
|
||||
gFinishCB();
|
||||
}
|
@ -185,10 +185,8 @@ function checkSocialUI(win) {
|
||||
isbool(win.SocialChatBar.isAvailable, enabled && Social.haveLoggedInUser(), "chatbar available?");
|
||||
isbool(!win.SocialChatBar.chatbar.hidden, enabled && Social.haveLoggedInUser(), "chatbar visible?");
|
||||
|
||||
let markVisible = enabled && provider.pageMarkInfo;
|
||||
let canMark = markVisible && win.SocialMark.canMarkPage(win.gBrowser.currentURI);
|
||||
isbool(!win.SocialMark.button.hidden, markVisible, "SocialMark button visible?");
|
||||
isbool(!win.SocialMark.button.disabled, canMark, "SocialMark button enabled?");
|
||||
let canShare = enabled && provider.recommendInfo && Social.haveLoggedInUser() && win.SocialShareButton.canSharePage(win.gBrowser.currentURI)
|
||||
isbool(!win.SocialShareButton.shareButton.hidden, canShare, "share button visible?");
|
||||
isbool(!doc.getElementById("social-toolbar-item").hidden, active, "toolbar items visible?");
|
||||
if (active)
|
||||
is(win.SocialToolbar.button.style.listStyleImage, 'url("' + Social.defaultProvider.iconURL + '")', "toolbar button has provider icon");
|
||||
@ -203,7 +201,7 @@ function checkSocialUI(win) {
|
||||
isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "Social:ToggleNotifications visible?");
|
||||
isbool(!doc.getElementById("Social:FocusChat").hidden, enabled && Social.haveLoggedInUser(), "Social:FocusChat visible?");
|
||||
isbool(doc.getElementById("Social:FocusChat").getAttribute("disabled"), enabled ? "false" : "true", "Social:FocusChat disabled?");
|
||||
is(doc.getElementById("Social:TogglePageMark").getAttribute("disabled"), canMark ? "false" : "true", "Social:TogglePageMark enabled?");
|
||||
is(doc.getElementById("Social:SharePage").getAttribute("disabled"), canShare ? "false" : "true", "Social:SharePage visible?");
|
||||
|
||||
// broadcasters.
|
||||
isbool(!doc.getElementById("socialActiveBroadcaster").hidden, active, "socialActiveBroadcaster hidden?");
|
||||
|
Before Width: | Height: | Size: 934 B After Width: | Height: | Size: 934 B |
@ -100,23 +100,6 @@ onconnect = function(e) {
|
||||
};
|
||||
}
|
||||
port.postMessage({topic: "social.user-profile", data: profile});
|
||||
port.postMessage({
|
||||
topic: "social.page-mark-config",
|
||||
data: {
|
||||
images: {
|
||||
// this one is relative to test we handle relative ones.
|
||||
marked: "/browser/browser/base/content/test/social/social_mark_image.png",
|
||||
// absolute to check we handle them too.
|
||||
unmarked: "https://example.com/browser/browser/base/content/test/social/social_mark_image.png"
|
||||
},
|
||||
messages: {
|
||||
unmarkedTooltip: "Mark this page",
|
||||
markedTooltip: "Unmark this page",
|
||||
unmarkedLabel: "Mark",
|
||||
markedLabel: "Unmark",
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "test-ambient-notification":
|
||||
let icon = {
|
||||
@ -133,6 +116,31 @@ onconnect = function(e) {
|
||||
case "test-isVisible-response":
|
||||
testPort.postMessage({topic: "got-isVisible-response", result: event.data.result});
|
||||
break;
|
||||
case "social.user-recommend-prompt":
|
||||
port.postMessage({
|
||||
topic: "social.user-recommend-prompt-response",
|
||||
data: {
|
||||
images: {
|
||||
// this one is relative to test we handle relative ones.
|
||||
share: "browser/browser/base/content/test/social/social_share_image.png",
|
||||
// absolute to check we handle them too.
|
||||
unshare: "https://example.com/browser/browser/base/content/test/social/social_share_image.png"
|
||||
},
|
||||
messages: {
|
||||
shareTooltip: "Share this page",
|
||||
unshareTooltip: "Unshare this page",
|
||||
sharedLabel: "This page has been shared",
|
||||
unsharedLabel: "This page is no longer shared",
|
||||
unshareLabel: "You have already shared this page",
|
||||
portraitLabel: "Your pretty face",
|
||||
unshareConfirmLabel: "Unshare it!",
|
||||
unshareConfirmAccessKey: "U",
|
||||
unshareCancelLabel: "Got it!",
|
||||
unshareCancelAccessKey: "G"
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
||||
<!ENTITY bookmarksMenu.accesskey "B">
|
||||
<!ENTITY bookmarkThisPageCmd.label "Bookmark This Page">
|
||||
<!ENTITY bookmarkThisPageCmd.commandkey "d">
|
||||
<!ENTITY markPageCmd.commandkey "l">
|
||||
<!ENTITY sharePageCmd.commandkey "l">
|
||||
<!ENTITY subscribeToPageMenupopup.label "Subscribe to This Page">
|
||||
<!ENTITY subscribeToPageMenuitem.label "Subscribe to This Page…">
|
||||
<!ENTITY addCurPagesCmd.label "Bookmark All Tabs…">
|
||||
@ -644,10 +644,6 @@ just addresses the organization to follow, e.g. "This site is run by " -->
|
||||
<!ENTITY social.chatBar.label "Focus chats">
|
||||
<!ENTITY social.chatBar.accesskey "c">
|
||||
|
||||
<!-- labels are set dynamically, see browser.properties -->
|
||||
<!ENTITY social.markpage.accesskey "m">
|
||||
<!ENTITY social.marklink.accesskey "M">
|
||||
|
||||
<!ENTITY getUserMedia.selectCamera.label "Camera to share:">
|
||||
<!ENTITY getUserMedia.selectCamera.accesskey "C">
|
||||
<!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">
|
||||
|
@ -386,13 +386,6 @@ social.turnOff.accesskey=T
|
||||
social.turnOn.label=Turn on %S
|
||||
social.turnOn.accesskey=T
|
||||
|
||||
# LOCALIZATION NOTE (social.markpage.label): %S is the name of the social provider
|
||||
social.markpage.label=Send Page to %S
|
||||
social.unmarkpage.label=Remove Page from %S
|
||||
# LOCALIZATION NOTE (social.marklink.label): %S is the name of the social provider
|
||||
social.marklink.label=Send Link to %S
|
||||
social.unmarklink.label=Remove Link from %S
|
||||
|
||||
# LOCALIZATION NOTE (social.error.message): %1$S is brandShortName (e.g. Firefox), %2$S is the name of the social provider
|
||||
social.error.message=%1$S is unable to connect with %2$S right now.
|
||||
social.error.tryAgain.label=Try Again
|
||||
|
@ -15,10 +15,6 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SocialService",
|
||||
"resource://gre/modules/SocialService.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
|
||||
// Add a pref observer for the enabled state
|
||||
function prefObserver(subject, topic, data) {
|
||||
@ -36,46 +32,6 @@ Services.obs.addObserver(function xpcomShutdown() {
|
||||
Services.prefs.removeObserver("social.enabled", prefObserver);
|
||||
}, "xpcom-shutdown", false);
|
||||
|
||||
function promiseSetAnnotation(aURI, providerList) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
// Delaying to catch issues with asynchronous behavior while waiting
|
||||
// to implement asynchronous annotations in bug 699844.
|
||||
Services.tm.mainThread.dispatch(function() {
|
||||
try {
|
||||
if (providerList && providerList.length > 0) {
|
||||
PlacesUtils.annotations.setPageAnnotation(
|
||||
aURI, "social/mark", JSON.stringify(providerList), 0,
|
||||
PlacesUtils.annotations.EXPIRE_WITH_HISTORY);
|
||||
} else {
|
||||
PlacesUtils.annotations.removePageAnnotation(aURI, "social/mark");
|
||||
}
|
||||
} catch(e) {
|
||||
Cu.reportError("SocialAnnotation failed: " + e);
|
||||
}
|
||||
deferred.resolve();
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promiseGetAnnotation(aURI) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
// Delaying to catch issues with asynchronous behavior while waiting
|
||||
// to implement asynchronous annotations in bug 699844.
|
||||
Services.tm.mainThread.dispatch(function() {
|
||||
let val = null;
|
||||
try {
|
||||
val = PlacesUtils.annotations.getPageAnnotation(aURI, "social/mark");
|
||||
} catch (ex) { }
|
||||
|
||||
deferred.resolve(val);
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
this.Social = {
|
||||
initialized: false,
|
||||
lastEventReceived: 0,
|
||||
@ -257,8 +213,8 @@ this.Social = {
|
||||
SocialService.removeProvider(origin);
|
||||
},
|
||||
|
||||
// Page Marking functionality
|
||||
_getMarkablePageUrl: function Social_getMarkablePageUrl(aURI) {
|
||||
// Sharing functionality
|
||||
_getShareablePageUrl: function Social_getShareablePageUrl(aURI) {
|
||||
let uri = aURI.clone();
|
||||
try {
|
||||
// Setting userPass on about:config throws.
|
||||
@ -267,97 +223,53 @@ this.Social = {
|
||||
return uri.spec;
|
||||
},
|
||||
|
||||
isURIMarked: function(aURI, aCallback) {
|
||||
promiseGetAnnotation(aURI).then(function(val) {
|
||||
if (val) {
|
||||
let providerList = JSON.parse(val);
|
||||
val = providerList.indexOf(this.provider.origin) >= 0;
|
||||
}
|
||||
aCallback(!!val);
|
||||
}.bind(this));
|
||||
isPageShared: function Social_isPageShared(aURI) {
|
||||
let url = this._getShareablePageUrl(aURI);
|
||||
return this._sharedUrls.hasOwnProperty(url);
|
||||
},
|
||||
|
||||
markURI: function(aURI, aCallback) {
|
||||
sharePage: function Social_sharePage(aURI) {
|
||||
// this should not be called if this.provider or the port is null
|
||||
if (!this.provider) {
|
||||
Cu.reportError("Can't mark a page when no provider is current");
|
||||
Cu.reportError("Can't share a page when no provider is current");
|
||||
return;
|
||||
}
|
||||
let port = this.provider.getWorkerPort();
|
||||
if (!port) {
|
||||
Cu.reportError("Can't mark page as no provider port is available");
|
||||
Cu.reportError("Can't share page as no provider port is available");
|
||||
return;
|
||||
}
|
||||
|
||||
// update or set our annotation
|
||||
promiseGetAnnotation(aURI).then(function(val) {
|
||||
|
||||
let providerList = val ? JSON.parse(val) : [];
|
||||
let marked = providerList.indexOf(this.provider.origin) >= 0;
|
||||
if (marked)
|
||||
return;
|
||||
providerList.push(this.provider.origin);
|
||||
// we allow marking links in a page that may not have been visited yet.
|
||||
// make sure there is a history entry for the uri, then annotate it.
|
||||
let place = {
|
||||
uri: aURI,
|
||||
visits: [{
|
||||
visitDate: Date.now() + 1000,
|
||||
transitionType: Ci.nsINavHistoryService.TRANSITION_LINK
|
||||
}]
|
||||
};
|
||||
PlacesUtils.asyncHistory.updatePlaces(place, {
|
||||
handleError: function () Cu.reportError("couldn't update history for socialmark annotation"),
|
||||
handleResult: function () {},
|
||||
handleCompletion: function () {
|
||||
promiseSetAnnotation(aURI, providerList).then();
|
||||
// post to the provider
|
||||
let url = this._getMarkablePageUrl(aURI);
|
||||
port.postMessage({
|
||||
topic: "social.page-mark",
|
||||
data: { url: url, 'marked': true }
|
||||
});
|
||||
port.close();
|
||||
if (aCallback)
|
||||
schedule(function() { aCallback(true); } );
|
||||
}.bind(this)
|
||||
});
|
||||
}.bind(this));
|
||||
let url = this._getShareablePageUrl(aURI);
|
||||
this._sharedUrls[url] = true;
|
||||
port.postMessage({
|
||||
topic: "social.user-recommend",
|
||||
data: { url: url }
|
||||
});
|
||||
port.close();
|
||||
},
|
||||
|
||||
unmarkURI: function(aURI, aCallback) {
|
||||
|
||||
unsharePage: function Social_unsharePage(aURI) {
|
||||
// this should not be called if this.provider or the port is null
|
||||
if (!this.provider) {
|
||||
Cu.reportError("Can't mark a page when no provider is current");
|
||||
Cu.reportError("Can't unshare a page when no provider is current");
|
||||
return;
|
||||
}
|
||||
let port = this.provider.getWorkerPort();
|
||||
if (!port) {
|
||||
Cu.reportError("Can't mark page as no provider port is available");
|
||||
Cu.reportError("Can't unshare page as no provider port is available");
|
||||
return;
|
||||
}
|
||||
|
||||
// set our annotation
|
||||
promiseGetAnnotation(aURI).then(function(val) {
|
||||
let providerList = val ? JSON.parse(val) : [];
|
||||
let marked = providerList.indexOf(this.provider.origin) >= 0;
|
||||
if (marked) {
|
||||
// remove the annotation
|
||||
providerList.splice(providerList.indexOf(this.provider.origin), 1);
|
||||
promiseSetAnnotation(aURI, providerList).then();
|
||||
}
|
||||
// post to the provider regardless
|
||||
let url = this._getMarkablePageUrl(aURI);
|
||||
port.postMessage({
|
||||
topic: "social.page-mark",
|
||||
data: { url: url, 'marked': false }
|
||||
});
|
||||
port.close();
|
||||
if (aCallback)
|
||||
schedule(function() { aCallback(false); } );
|
||||
}.bind(this));
|
||||
let url = this._getShareablePageUrl(aURI);
|
||||
delete this._sharedUrls[url];
|
||||
port.postMessage({
|
||||
topic: "social.user-unrecommend",
|
||||
data: { url: url }
|
||||
});
|
||||
port.close();
|
||||
},
|
||||
|
||||
_sharedUrls: {},
|
||||
|
||||
setErrorListener: function(iframe, errorHandler) {
|
||||
if (iframe.socialErrorListener)
|
||||
return iframe.socialErrorListener;
|
||||
|
@ -1435,10 +1435,50 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
|
||||
|
||||
/* social recommending panel */
|
||||
|
||||
#social-mark-button {
|
||||
#share-button {
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
|
||||
#socialUserPortrait {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
list-style-image:url("chrome://global/skin/icons/information-32.png");
|
||||
}
|
||||
|
||||
#socialUserDisplayName,
|
||||
#socialUserPortrait {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#socialUserDisplayName {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
font-size: 130%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#socialUserDisplayName > .button-box {
|
||||
-moz-padding-start: 0;
|
||||
padding-top: 0;
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
#socialUserDisplayName:hover {
|
||||
color: -moz-nativehyperlinktext;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#unsharePopupText {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
#unsharePopupBottomButtons {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
/* bookmarks menu-button */
|
||||
|
||||
#bookmarks-menu-button {
|
||||
|
@ -1657,10 +1657,50 @@ window[tabsontop="false"] richlistitem[type~="action"][actiontype="switchtab"][s
|
||||
|
||||
/* social recommending panel */
|
||||
|
||||
#social-mark-button {
|
||||
#share-button {
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
|
||||
#socialUserPortrait {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
list-style-image:url("chrome://global/skin/icons/information-32.png");
|
||||
}
|
||||
|
||||
#socialUserDisplayName,
|
||||
#socialUserPortrait {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#socialUserDisplayName {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
margin: 1px;
|
||||
padding: 0;
|
||||
font-size: 130%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#socialUserDisplayName > .button-box {
|
||||
-moz-padding-start: 0;
|
||||
padding-top: 0;
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
#socialUserDisplayName:hover {
|
||||
color: -moz-nativehyperlinktext;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#unsharePopupText {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
#unsharePopupBottomButtons {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
/* bookmarks menu-button */
|
||||
|
||||
#bookmarks-menu-button {
|
||||
|
@ -1715,10 +1715,50 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
|
||||
|
||||
/* social recommending panel */
|
||||
|
||||
#social-mark-button {
|
||||
#share-button {
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
|
||||
#socialUserPortrait {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
list-style-image:url("chrome://global/skin/icons/information-32.png");
|
||||
}
|
||||
|
||||
#socialUserDisplayName,
|
||||
#socialUserPortrait {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#socialUserDisplayName {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
font-size: 130%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#socialUserDisplayName > .button-box {
|
||||
-moz-padding-start: 0;
|
||||
padding-top: 0;
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
#socialUserDisplayName:hover {
|
||||
color: -moz-nativehyperlinktext;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#unsharePopupText {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
#unsharePopupBottomButtons {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
/* bookmarks menu-button */
|
||||
|
||||
#bookmarks-menu-button {
|
||||
|
@ -74,7 +74,7 @@ class RemoteAutomation(Automation):
|
||||
status = proc.wait(timeout = maxTime)
|
||||
self.lastTestSeen = proc.getLastTestSeen
|
||||
|
||||
if (status == 1 and self._devicemanager.processExist(proc.procName)):
|
||||
if (status == 1 and self._devicemanager.getTopActivity() == proc.procName):
|
||||
# Then we timed out, make sure Fennec is dead
|
||||
if maxTime:
|
||||
print "TEST-UNEXPECTED-FAIL | %s | application ran for longer than " \
|
||||
@ -269,7 +269,7 @@ class RemoteAutomation(Automation):
|
||||
if timeout == None:
|
||||
timeout = self.timeout
|
||||
|
||||
while (self.dm.processExist(self.procName)):
|
||||
while (self.dm.getTopActivity() == self.procName):
|
||||
t = self.stdout
|
||||
if t != '': print t
|
||||
time.sleep(interval)
|
||||
|
@ -6,8 +6,8 @@
|
||||
|
||||
CONFIGURE_SUBST_FILES += [
|
||||
'autoconf.mk',
|
||||
'emptyvars.mk',
|
||||
'doxygen.cfg',
|
||||
'emptyvars.mk',
|
||||
'makefiles/test/Makefile',
|
||||
'tests/makefiles/autodeps/Makefile',
|
||||
'tests/src-simple/Makefile',
|
||||
|
@ -534,10 +534,9 @@ public:
|
||||
* method is responsible for calling BeginObservingDocument() on the
|
||||
* presshell if the presshell should observe document mutations.
|
||||
*/
|
||||
virtual nsresult CreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet,
|
||||
nsIPresShell** aInstancePtrResult) = 0;
|
||||
virtual already_AddRefed<nsIPresShell> CreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet) = 0;
|
||||
virtual void DeleteShell() = 0;
|
||||
|
||||
nsIPresShell* GetShell() const
|
||||
|
@ -7,31 +7,31 @@
|
||||
MODULE = 'content'
|
||||
|
||||
EXPORTS += [
|
||||
'mozAutoDocUpdate.h',
|
||||
'nsAtomListUtils.h',
|
||||
'nsAttrAndChildArray.h',
|
||||
'nsAttrName.h',
|
||||
'nsAttrValue.h',
|
||||
'nsAttrValueInlines.h',
|
||||
'nsContentList.h',
|
||||
'nsContentListDeclarations.h',
|
||||
'nsContentSink.h',
|
||||
'nsCrossSiteListenerProxy.h',
|
||||
'nsDOMAttributeMap.h',
|
||||
'nsFrameMessageManager.h',
|
||||
'nsGkAtomList.h',
|
||||
'nsGkAtoms.h',
|
||||
'nsMappedAttributeElement.h',
|
||||
'nsNodeInfoManager.h',
|
||||
'nsNodeUtils.h',
|
||||
'nsPropertyTable.h',
|
||||
'nsRange.h',
|
||||
'nsSandboxFlags.h',
|
||||
'nsScriptLoader.h',
|
||||
'nsStubDocumentObserver.h',
|
||||
'nsStubMutationObserver.h',
|
||||
'nsTextFragment.h',
|
||||
'mozAutoDocUpdate.h',
|
||||
'nsFrameMessageManager.h',
|
||||
'nsAttrAndChildArray.h',
|
||||
'nsAttrValue.h',
|
||||
'nsAttrValueInlines.h',
|
||||
'nsCrossSiteListenerProxy.h',
|
||||
'nsDOMAttributeMap.h',
|
||||
'nsMappedAttributeElement.h',
|
||||
'nsStyledElement.h',
|
||||
'nsSandboxFlags.h',
|
||||
'nsTextFragment.h',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WEBRTC']:
|
||||
@ -40,9 +40,9 @@ if CONFIG['MOZ_WEBRTC']:
|
||||
EXPORTS.mozilla.dom += [
|
||||
'Attr.h',
|
||||
'Comment.h',
|
||||
'DOMImplementation.h',
|
||||
'DocumentFragment.h',
|
||||
'DocumentType.h',
|
||||
'DOMImplementation.h',
|
||||
'EventSource.h',
|
||||
'Link.h',
|
||||
'NodeIterator.h',
|
||||
|
@ -3337,35 +3337,30 @@ nsDocument::TryChannelCharset(nsIChannel *aChannel,
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
already_AddRefed<nsIPresShell>
|
||||
nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet,
|
||||
nsIPresShell** aInstancePtrResult)
|
||||
nsStyleSet* aStyleSet)
|
||||
{
|
||||
// Don't add anything here. Add it to |doCreateShell| instead.
|
||||
// This exists so that subclasses can pass other values for the 4th
|
||||
// parameter some of the time.
|
||||
return doCreateShell(aContext, aViewManager, aStyleSet,
|
||||
eCompatibility_FullStandards, aInstancePtrResult);
|
||||
eCompatibility_FullStandards);
|
||||
}
|
||||
|
||||
nsresult
|
||||
already_AddRefed<nsIPresShell>
|
||||
nsDocument::doCreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager, nsStyleSet* aStyleSet,
|
||||
nsCompatibility aCompatMode,
|
||||
nsIPresShell** aInstancePtrResult)
|
||||
nsCompatibility aCompatMode)
|
||||
{
|
||||
*aInstancePtrResult = nullptr;
|
||||
|
||||
NS_ASSERTION(!mPresShell, "We have a presshell already!");
|
||||
|
||||
NS_ENSURE_FALSE(GetBFCacheEntry(), NS_ERROR_FAILURE);
|
||||
NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
|
||||
|
||||
FillStyleSet(aStyleSet);
|
||||
|
||||
nsRefPtr<PresShell> shell = new PresShell;
|
||||
nsresult rv = shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
|
||||
|
||||
// Note: we don't hold a ref to the shell (it holds a ref to us)
|
||||
mPresShell = shell;
|
||||
@ -3374,9 +3369,7 @@ nsDocument::doCreateShell(nsPresContext* aContext,
|
||||
|
||||
MaybeRescheduleAnimationFrameNotifications();
|
||||
|
||||
shell.forget(aInstancePtrResult);
|
||||
|
||||
return NS_OK;
|
||||
return shell.forget();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -590,13 +590,12 @@ public:
|
||||
|
||||
/**
|
||||
* Create a new presentation shell that will use aContext for
|
||||
* its presentation context (presentation context's <b>must not</b> be
|
||||
* shared among multiple presentation shell's).
|
||||
* its presentation context (presentation contexts <b>must not</b> be
|
||||
* shared among multiple presentation shells).
|
||||
*/
|
||||
virtual nsresult CreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet,
|
||||
nsIPresShell** aInstancePtrResult);
|
||||
virtual already_AddRefed<nsIPresShell> CreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet) MOZ_OVERRIDE;
|
||||
virtual void DeleteShell();
|
||||
|
||||
virtual nsresult GetAllowPlugins(bool* aAllowPlugins);
|
||||
@ -1141,10 +1140,10 @@ public:
|
||||
|
||||
static void XPCOMShutdown();
|
||||
protected:
|
||||
nsresult doCreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager, nsStyleSet* aStyleSet,
|
||||
nsCompatibility aCompatMode,
|
||||
nsIPresShell** aInstancePtrResult);
|
||||
already_AddRefed<nsIPresShell> doCreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet,
|
||||
nsCompatibility aCompatMode);
|
||||
|
||||
void RemoveDocStyleSheetsFromStyleSets();
|
||||
void RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets,
|
||||
|
@ -733,17 +733,8 @@ HTMLInputElement::Clone(nsINodeInfo* aNodeInfo, nsINode** aResult) const
|
||||
nsresult rv = const_cast<HTMLInputElement*>(this)->CopyInnerTo(it);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
switch (mType) {
|
||||
case NS_FORM_INPUT_EMAIL:
|
||||
case NS_FORM_INPUT_SEARCH:
|
||||
case NS_FORM_INPUT_TEXT:
|
||||
case NS_FORM_INPUT_PASSWORD:
|
||||
case NS_FORM_INPUT_TEL:
|
||||
case NS_FORM_INPUT_URL:
|
||||
case NS_FORM_INPUT_NUMBER:
|
||||
case NS_FORM_INPUT_DATE:
|
||||
case NS_FORM_INPUT_TIME:
|
||||
case NS_FORM_INPUT_RANGE:
|
||||
switch (GetValueMode()) {
|
||||
case VALUE_MODE_VALUE:
|
||||
if (mValueChanged) {
|
||||
// We don't have our default value anymore. Set our value on
|
||||
// the clone.
|
||||
@ -753,7 +744,7 @@ HTMLInputElement::Clone(nsINodeInfo* aNodeInfo, nsINode** aResult) const
|
||||
it->SetValueInternal(value, false, true);
|
||||
}
|
||||
break;
|
||||
case NS_FORM_INPUT_FILE:
|
||||
case VALUE_MODE_FILENAME:
|
||||
if (it->OwnerDoc()->IsStaticDocument()) {
|
||||
// We're going to be used in print preview. Since the doc is static
|
||||
// we can just grab the pretty string and use it as wallpaper
|
||||
@ -763,21 +754,18 @@ HTMLInputElement::Clone(nsINodeInfo* aNodeInfo, nsINode** aResult) const
|
||||
it->mFiles.AppendObjects(mFiles);
|
||||
}
|
||||
break;
|
||||
case NS_FORM_INPUT_RADIO:
|
||||
case NS_FORM_INPUT_CHECKBOX:
|
||||
case VALUE_MODE_DEFAULT_ON:
|
||||
if (mCheckedChanged) {
|
||||
// We no longer have our original checked state. Set our
|
||||
// checked state on the clone.
|
||||
it->DoSetChecked(mChecked, false, true);
|
||||
}
|
||||
break;
|
||||
case NS_FORM_INPUT_IMAGE:
|
||||
if (it->OwnerDoc()->IsStaticDocument()) {
|
||||
case VALUE_MODE_DEFAULT:
|
||||
if (mType == NS_FORM_INPUT_IMAGE && it->OwnerDoc()->IsStaticDocument()) {
|
||||
CreateStaticImageClone(it);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
it.forget(aResult);
|
||||
@ -4415,53 +4403,40 @@ NS_IMETHODIMP
|
||||
HTMLInputElement::SaveState()
|
||||
{
|
||||
nsRefPtr<HTMLInputElementState> inputState;
|
||||
switch (mType) {
|
||||
case NS_FORM_INPUT_CHECKBOX:
|
||||
case NS_FORM_INPUT_RADIO:
|
||||
{
|
||||
if (mCheckedChanged) {
|
||||
inputState = new HTMLInputElementState();
|
||||
inputState->SetChecked(mChecked);
|
||||
}
|
||||
switch (GetValueMode()) {
|
||||
case VALUE_MODE_DEFAULT_ON:
|
||||
if (mCheckedChanged) {
|
||||
inputState = new HTMLInputElementState();
|
||||
inputState->SetChecked(mChecked);
|
||||
}
|
||||
break;
|
||||
case VALUE_MODE_FILENAME:
|
||||
if (mFiles.Count()) {
|
||||
inputState = new HTMLInputElementState();
|
||||
inputState->SetFiles(mFiles);
|
||||
}
|
||||
break;
|
||||
case VALUE_MODE_VALUE:
|
||||
case VALUE_MODE_DEFAULT:
|
||||
// VALUE_MODE_DEFAULT shouldn't have their value saved except 'hidden',
|
||||
// mType shouldn't be NS_FORM_INPUT_PASSWORD and value should have changed.
|
||||
if ((GetValueMode() == VALUE_MODE_DEFAULT &&
|
||||
mType != NS_FORM_INPUT_HIDDEN) ||
|
||||
mType == NS_FORM_INPUT_PASSWORD || !mValueChanged) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Never save passwords in session history
|
||||
case NS_FORM_INPUT_PASSWORD:
|
||||
inputState = new HTMLInputElementState();
|
||||
nsAutoString value;
|
||||
GetValue(value);
|
||||
DebugOnly<nsresult> rv =
|
||||
nsLinebreakConverter::ConvertStringLineBreaks(
|
||||
value,
|
||||
nsLinebreakConverter::eLinebreakPlatform,
|
||||
nsLinebreakConverter::eLinebreakContent);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "Converting linebreaks failed!");
|
||||
inputState->SetValue(value);
|
||||
break;
|
||||
case NS_FORM_INPUT_EMAIL:
|
||||
case NS_FORM_INPUT_SEARCH:
|
||||
case NS_FORM_INPUT_TEXT:
|
||||
case NS_FORM_INPUT_TEL:
|
||||
case NS_FORM_INPUT_URL:
|
||||
case NS_FORM_INPUT_HIDDEN:
|
||||
case NS_FORM_INPUT_NUMBER:
|
||||
case NS_FORM_INPUT_DATE:
|
||||
case NS_FORM_INPUT_TIME:
|
||||
case NS_FORM_INPUT_RANGE:
|
||||
{
|
||||
if (mValueChanged) {
|
||||
inputState = new HTMLInputElementState();
|
||||
nsAutoString value;
|
||||
GetValue(value);
|
||||
DebugOnly<nsresult> rv =
|
||||
nsLinebreakConverter::ConvertStringLineBreaks(
|
||||
value,
|
||||
nsLinebreakConverter::eLinebreakPlatform,
|
||||
nsLinebreakConverter::eLinebreakContent);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "Converting linebreaks failed!");
|
||||
inputState->SetValue(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NS_FORM_INPUT_FILE:
|
||||
{
|
||||
if (mFiles.Count()) {
|
||||
inputState = new HTMLInputElementState();
|
||||
inputState->SetFiles(mFiles);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
@ -4605,37 +4580,29 @@ HTMLInputElement::RestoreState(nsPresState* aState)
|
||||
(do_QueryInterface(aState->GetStateProperty()));
|
||||
|
||||
if (inputState) {
|
||||
switch (mType) {
|
||||
case NS_FORM_INPUT_CHECKBOX:
|
||||
case NS_FORM_INPUT_RADIO:
|
||||
{
|
||||
if (inputState->IsCheckedSet()) {
|
||||
restoredCheckedState = true;
|
||||
DoSetChecked(inputState->GetChecked(), true, true);
|
||||
}
|
||||
break;
|
||||
switch (GetValueMode()) {
|
||||
case VALUE_MODE_DEFAULT_ON:
|
||||
if (inputState->IsCheckedSet()) {
|
||||
restoredCheckedState = true;
|
||||
DoSetChecked(inputState->GetChecked(), true, true);
|
||||
}
|
||||
|
||||
case NS_FORM_INPUT_EMAIL:
|
||||
case NS_FORM_INPUT_SEARCH:
|
||||
case NS_FORM_INPUT_TEXT:
|
||||
case NS_FORM_INPUT_TEL:
|
||||
case NS_FORM_INPUT_URL:
|
||||
case NS_FORM_INPUT_HIDDEN:
|
||||
case NS_FORM_INPUT_NUMBER:
|
||||
case NS_FORM_INPUT_DATE:
|
||||
case NS_FORM_INPUT_TIME:
|
||||
case NS_FORM_INPUT_RANGE:
|
||||
{
|
||||
SetValueInternal(inputState->GetValue(), false, true);
|
||||
break;
|
||||
}
|
||||
case NS_FORM_INPUT_FILE:
|
||||
break;
|
||||
case VALUE_MODE_FILENAME:
|
||||
{
|
||||
const nsCOMArray<nsIDOMFile>& files = inputState->GetFiles();
|
||||
SetFiles(files, true);
|
||||
}
|
||||
break;
|
||||
case VALUE_MODE_VALUE:
|
||||
case VALUE_MODE_DEFAULT:
|
||||
if (GetValueMode() == VALUE_MODE_DEFAULT &&
|
||||
mType != NS_FORM_INPUT_HIDDEN) {
|
||||
break;
|
||||
}
|
||||
|
||||
SetValueInternal(inputState->GetValue(), false, true);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,16 +8,16 @@ MODULE = 'content'
|
||||
|
||||
EXPORTS += [
|
||||
'HTMLPropertiesCollection.h',
|
||||
'nsGenericHTMLElement.h',
|
||||
'nsClientRect.h',
|
||||
'nsGenericHTMLElement.h',
|
||||
'nsHTMLDNSPrefetch.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'HTMLAnchorElement.h',
|
||||
'HTMLAreaElement.h',
|
||||
'HTMLBodyElement.h',
|
||||
'HTMLBRElement.h',
|
||||
'HTMLBodyElement.h',
|
||||
'HTMLButtonElement.h',
|
||||
'HTMLDataElement.h',
|
||||
'HTMLDataListElement.h',
|
||||
@ -26,14 +26,14 @@ EXPORTS.mozilla.dom += [
|
||||
'HTMLFontElement.h',
|
||||
'HTMLFrameElement.h',
|
||||
'HTMLFrameSetElement.h',
|
||||
'HTMLHeadingElement.h',
|
||||
'HTMLHRElement.h',
|
||||
'HTMLHeadingElement.h',
|
||||
'HTMLIFrameElement.h',
|
||||
'HTMLImageElement.h',
|
||||
'HTMLInputElement.h',
|
||||
'HTMLLIElement.h',
|
||||
'HTMLLabelElement.h',
|
||||
'HTMLLegendElement.h',
|
||||
'HTMLLIElement.h',
|
||||
'HTMLLinkElement.h',
|
||||
'HTMLMapElement.h',
|
||||
'HTMLMenuElement.h',
|
||||
@ -42,9 +42,9 @@ EXPORTS.mozilla.dom += [
|
||||
'HTMLMeterElement.h',
|
||||
'HTMLModElement.h',
|
||||
'HTMLObjectElement.h',
|
||||
'HTMLOptGroupElement.h',
|
||||
'HTMLOptionElement.h',
|
||||
'HTMLOptionsCollection.h',
|
||||
'HTMLOptGroupElement.h',
|
||||
'HTMLOutputElement.h',
|
||||
'HTMLParagraphElement.h',
|
||||
'HTMLPreElement.h',
|
||||
|
@ -304,14 +304,12 @@ nsHTMLDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
|
||||
SetContentTypeInternal(nsDependentCString("text/html"));
|
||||
}
|
||||
|
||||
nsresult
|
||||
already_AddRefed<nsIPresShell>
|
||||
nsHTMLDocument::CreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet,
|
||||
nsIPresShell** aInstancePtrResult)
|
||||
nsStyleSet* aStyleSet)
|
||||
{
|
||||
return doCreateShell(aContext, aViewManager, aStyleSet, mCompatMode,
|
||||
aInstancePtrResult);
|
||||
return doCreateShell(aContext, aViewManager, aStyleSet, mCompatMode);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -54,10 +54,9 @@ public:
|
||||
virtual void ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
|
||||
nsIPrincipal* aPrincipal);
|
||||
|
||||
virtual nsresult CreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet,
|
||||
nsIPresShell** aInstancePtrResult);
|
||||
virtual already_AddRefed<nsIPresShell> CreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult StartDocumentLoad(const char* aCommand,
|
||||
nsIChannel* aChannel,
|
||||
|
@ -19,11 +19,6 @@ struct ThreeDPoint;
|
||||
|
||||
class AudioNodeStream;
|
||||
|
||||
// We ensure that the graph advances in steps that are multiples of the Web
|
||||
// Audio block size
|
||||
const uint32_t WEBAUDIO_BLOCK_SIZE_BITS = 7;
|
||||
const uint32_t WEBAUDIO_BLOCK_SIZE = 1 << WEBAUDIO_BLOCK_SIZE_BITS;
|
||||
|
||||
/**
|
||||
* This class holds onto a set of immutable channel buffers. The storage
|
||||
* for the buffers must be malloced, but the buffer pointers and the malloc
|
||||
@ -154,7 +149,6 @@ public:
|
||||
: mNode(aNode)
|
||||
, mNodeMutex("AudioNodeEngine::mNodeMutex")
|
||||
{
|
||||
MOZ_ASSERT(mNode, "The engine is constructed with a null node");
|
||||
MOZ_COUNT_CTOR(AudioNodeEngine);
|
||||
}
|
||||
virtual ~AudioNodeEngine()
|
||||
@ -210,6 +204,11 @@ public:
|
||||
|
||||
Mutex& NodeMutex() { return mNodeMutex;}
|
||||
|
||||
bool HasNode() const
|
||||
{
|
||||
return !!mNode;
|
||||
}
|
||||
|
||||
dom::AudioNode* Node() const
|
||||
{
|
||||
mNodeMutex.AssertCurrentThreadOwns();
|
||||
|
@ -102,19 +102,19 @@ AudioNodeStream::SetInt32Parameter(uint32_t aIndex, int32_t aValue)
|
||||
|
||||
void
|
||||
AudioNodeStream::SetTimelineParameter(uint32_t aIndex,
|
||||
const AudioEventTimeline<ErrorResult>& aValue)
|
||||
const AudioParamTimeline& aValue)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, uint32_t aIndex,
|
||||
const AudioEventTimeline<ErrorResult>& aValue)
|
||||
const AudioParamTimeline& aValue)
|
||||
: ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {}
|
||||
virtual void Run()
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->Engine()->
|
||||
SetTimelineParameter(mIndex, mValue);
|
||||
}
|
||||
AudioEventTimeline<ErrorResult> mValue;
|
||||
AudioParamTimeline mValue;
|
||||
uint32_t mIndex;
|
||||
};
|
||||
GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
|
||||
@ -247,7 +247,8 @@ AudioNodeStream::ObtainInputBlock(AudioChunk* aTmpChunk)
|
||||
MediaStream* s = mInputs[i]->GetSource();
|
||||
AudioNodeStream* a = static_cast<AudioNodeStream*>(s);
|
||||
MOZ_ASSERT(a == s->AsAudioNodeStream());
|
||||
if (a->IsFinishedOnGraphThread()) {
|
||||
if (a->IsFinishedOnGraphThread() ||
|
||||
a->IsAudioParamStream()) {
|
||||
continue;
|
||||
}
|
||||
AudioChunk* chunk = &a->mLastChunk;
|
||||
|
@ -22,6 +22,7 @@ namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
struct ThreeDPoint;
|
||||
class AudioParamTimeline;
|
||||
}
|
||||
|
||||
class ThreadSharedFloatArrayBufferList;
|
||||
@ -49,7 +50,8 @@ public:
|
||||
mEngine(aEngine),
|
||||
mKind(aKind),
|
||||
mNumberOfInputChannels(2),
|
||||
mMarkAsFinishedAfterThisBlock(false)
|
||||
mMarkAsFinishedAfterThisBlock(false),
|
||||
mAudioParamStream(false)
|
||||
{
|
||||
mMixingMode.mChannelCountMode = dom::ChannelCountMode::Max;
|
||||
mMixingMode.mChannelInterpretation = dom::ChannelInterpretation::Speakers;
|
||||
@ -74,6 +76,11 @@ public:
|
||||
void SetChannelMixingParameters(uint32_t aNumberOfChannels,
|
||||
dom::ChannelCountMode aChannelCountMoe,
|
||||
dom::ChannelInterpretation aChannelInterpretation);
|
||||
void SetAudioParamHelperStream()
|
||||
{
|
||||
MOZ_ASSERT(!mAudioParamStream, "Can only do this once");
|
||||
mAudioParamStream = true;
|
||||
}
|
||||
|
||||
virtual AudioNodeStream* AsAudioNodeStream() { return this; }
|
||||
|
||||
@ -86,6 +93,14 @@ public:
|
||||
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo);
|
||||
TrackTicks GetCurrentPosition();
|
||||
bool AllInputsFinished() const;
|
||||
bool IsAudioParamStream() const
|
||||
{
|
||||
return mAudioParamStream;
|
||||
}
|
||||
const AudioChunk& LastChunk() const
|
||||
{
|
||||
return mLastChunk;
|
||||
}
|
||||
|
||||
// Any thread
|
||||
AudioNodeEngine* Engine() { return mEngine; }
|
||||
@ -112,6 +127,8 @@ protected:
|
||||
// Whether the stream should be marked as finished as soon
|
||||
// as the current time range has been computed block by block.
|
||||
bool mMarkAsFinishedAfterThisBlock;
|
||||
// Whether the stream is an AudioParamHelper stream.
|
||||
bool mAudioParamStream;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,11 @@ class AudioStream;
|
||||
*/
|
||||
const int GUESS_AUDIO_CHANNELS = 2;
|
||||
|
||||
// We ensure that the graph advances in steps that are multiples of the Web
|
||||
// Audio block size
|
||||
const uint32_t WEBAUDIO_BLOCK_SIZE_BITS = 7;
|
||||
const uint32_t WEBAUDIO_BLOCK_SIZE = 1 << WEBAUDIO_BLOCK_SIZE_BITS;
|
||||
|
||||
/**
|
||||
* An AudioChunk represents a multi-channel buffer of audio samples.
|
||||
* It references an underlying ThreadSharedObject which manages the lifetime
|
||||
|
@ -2017,9 +2017,11 @@ MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine* aEngine,
|
||||
NS_ADDREF(stream);
|
||||
MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
|
||||
stream->SetGraphImpl(graph);
|
||||
stream->SetChannelMixingParametersImpl(aEngine->NodeMainThread()->ChannelCount(),
|
||||
aEngine->NodeMainThread()->ChannelCountModeValue(),
|
||||
aEngine->NodeMainThread()->ChannelInterpretationValue());
|
||||
if (aEngine->HasNode()) {
|
||||
stream->SetChannelMixingParametersImpl(aEngine->NodeMainThread()->ChannelCount(),
|
||||
aEngine->NodeMainThread()->ChannelCountModeValue(),
|
||||
aEngine->NodeMainThread()->ChannelInterpretationValue());
|
||||
}
|
||||
graph->AppendMessage(new CreateMessage(stream));
|
||||
return stream;
|
||||
}
|
||||
|
118
content/media/gstreamer/GStreamerMozVideoBuffer.cpp
Normal file
118
content/media/gstreamer/GStreamerMozVideoBuffer.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
/* -*- 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 <string.h>
|
||||
#include "GStreamerReader.h"
|
||||
#include "GStreamerMozVideoBuffer.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static GstMozVideoBuffer *gst_moz_video_buffer_copy(GstMozVideoBuffer* self);
|
||||
static void gst_moz_video_buffer_finalize(GstMozVideoBuffer* self);
|
||||
|
||||
G_DEFINE_TYPE(GstMozVideoBuffer, gst_moz_video_buffer, GST_TYPE_BUFFER);
|
||||
|
||||
static void
|
||||
gst_moz_video_buffer_class_init(GstMozVideoBufferClass* klass)
|
||||
{
|
||||
g_return_if_fail(GST_IS_MOZ_VIDEO_BUFFER_CLASS(klass));
|
||||
|
||||
GstMiniObjectClass *mo_class = GST_MINI_OBJECT_CLASS(klass);
|
||||
|
||||
mo_class->copy =(GstMiniObjectCopyFunction)gst_moz_video_buffer_copy;
|
||||
mo_class->finalize =(GstMiniObjectFinalizeFunction)gst_moz_video_buffer_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_moz_video_buffer_init(GstMozVideoBuffer* self)
|
||||
{
|
||||
g_return_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_moz_video_buffer_finalize(GstMozVideoBuffer* self)
|
||||
{
|
||||
g_return_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self));
|
||||
|
||||
if(self->data)
|
||||
g_boxed_free(GST_TYPE_MOZ_VIDEO_BUFFER_DATA, self->data);
|
||||
|
||||
GST_MINI_OBJECT_CLASS(gst_moz_video_buffer_parent_class)->finalize(GST_MINI_OBJECT(self));
|
||||
}
|
||||
|
||||
static GstMozVideoBuffer*
|
||||
gst_moz_video_buffer_copy(GstMozVideoBuffer* self)
|
||||
{
|
||||
GstMozVideoBuffer* copy;
|
||||
|
||||
g_return_val_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self), NULL);
|
||||
|
||||
copy = gst_moz_video_buffer_new();
|
||||
|
||||
/* we simply copy everything from our parent */
|
||||
GST_BUFFER_DATA(GST_BUFFER_CAST(copy)) =
|
||||
(guint8*)g_memdup(GST_BUFFER_DATA(GST_BUFFER_CAST(self)), GST_BUFFER_SIZE(GST_BUFFER_CAST(self)));
|
||||
|
||||
/* make sure it gets freed(even if the parent is subclassed, we return a
|
||||
normal buffer) */
|
||||
GST_BUFFER_MALLOCDATA(GST_BUFFER_CAST(copy)) = GST_BUFFER_DATA(GST_BUFFER_CAST(copy));
|
||||
GST_BUFFER_SIZE(GST_BUFFER_CAST(copy)) = GST_BUFFER_SIZE(GST_BUFFER_CAST(self));
|
||||
|
||||
/* copy metadata */
|
||||
gst_buffer_copy_metadata(GST_BUFFER_CAST(copy),
|
||||
GST_BUFFER_CAST(self),
|
||||
(GstBufferCopyFlags)GST_BUFFER_COPY_ALL);
|
||||
/* copy videobuffer */
|
||||
if(self->data)
|
||||
copy->data = (GstMozVideoBufferData*)g_boxed_copy(GST_TYPE_MOZ_VIDEO_BUFFER_DATA, self->data);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
GstMozVideoBuffer*
|
||||
gst_moz_video_buffer_new(void)
|
||||
{
|
||||
GstMozVideoBuffer *self;
|
||||
|
||||
self =(GstMozVideoBuffer*)gst_mini_object_new(GST_TYPE_MOZ_VIDEO_BUFFER);
|
||||
self->data = nullptr;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
gst_moz_video_buffer_set_data(GstMozVideoBuffer* self, GstMozVideoBufferData* data)
|
||||
{
|
||||
g_return_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self));
|
||||
|
||||
self->data = data;
|
||||
}
|
||||
|
||||
GstMozVideoBufferData*
|
||||
gst_moz_video_buffer_get_data(const GstMozVideoBuffer* self)
|
||||
{
|
||||
g_return_val_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self), NULL);
|
||||
|
||||
return self->data;
|
||||
}
|
||||
|
||||
GType
|
||||
gst_moz_video_buffer_data_get_type(void)
|
||||
{
|
||||
static volatile gsize g_define_type_id__volatile = 0;
|
||||
|
||||
if(g_once_init_enter(&g_define_type_id__volatile)) {
|
||||
GType g_define_type_id =
|
||||
g_boxed_type_register_static(g_intern_static_string("GstMozVideoBufferData"),
|
||||
(GBoxedCopyFunc)GstMozVideoBufferData::Copy,
|
||||
(GBoxedFreeFunc)GstMozVideoBufferData::Free);
|
||||
g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
|
||||
}
|
||||
|
||||
return g_define_type_id__volatile;
|
||||
}
|
||||
|
||||
}
|
60
content/media/gstreamer/GStreamerMozVideoBuffer.h
Normal file
60
content/media/gstreamer/GStreamerMozVideoBuffer.h
Normal file
@ -0,0 +1,60 @@
|
||||
/* 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 __GST_MOZ_VIDEO_BUFFER_H__
|
||||
#define __GST_MOZ_VIDEO_BUFFER_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "MediaDecoderReader.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#define GST_TYPE_MOZ_VIDEO_BUFFER_DATA (gst_moz_video_buffer_data_get_type())
|
||||
#define GST_IS_MOZ_VIDEO_BUFFER_DATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MOZ_VIDEO_BUFFER_DATA))
|
||||
|
||||
#define GST_TYPE_MOZ_VIDEO_BUFFER (gst_moz_video_buffer_get_type())
|
||||
#define GST_IS_MOZ_VIDEO_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MOZ_VIDEO_BUFFER))
|
||||
#define GST_IS_MOZ_VIDEO_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MOZ_VIDEO_BUFFER))
|
||||
#define GST_MOZ_VIDEO_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MOZ_VIDEO_BUFFER, GstMozVideoBuffer))
|
||||
|
||||
typedef struct _GstMozVideoBuffer GstMozVideoBuffer;
|
||||
typedef struct _GstMozVideoBufferClass GstMozVideoBufferClass;
|
||||
|
||||
class GstMozVideoBufferData;
|
||||
|
||||
struct _GstMozVideoBuffer {
|
||||
GstBuffer buffer;
|
||||
GstMozVideoBufferData* data;
|
||||
};
|
||||
|
||||
struct _GstMozVideoBufferClass {
|
||||
GstBufferClass buffer_class;
|
||||
};
|
||||
|
||||
GType gst_moz_video_buffer_get_type(void);
|
||||
GstMozVideoBuffer* gst_moz_video_buffer_new(void);
|
||||
void gst_moz_video_buffer_set_data(GstMozVideoBuffer* buf, GstMozVideoBufferData* data);
|
||||
GstMozVideoBufferData* gst_moz_video_buffer_get_data(const GstMozVideoBuffer* buf);
|
||||
|
||||
class GstMozVideoBufferData {
|
||||
public:
|
||||
GstMozVideoBufferData(layers::PlanarYCbCrImage* aImage) : mImage(aImage) {}
|
||||
|
||||
static void* Copy(void* aData) {
|
||||
return new GstMozVideoBufferData(reinterpret_cast<GstMozVideoBufferData*>(aData)->mImage);
|
||||
}
|
||||
|
||||
static void Free(void* aData) {
|
||||
delete reinterpret_cast<GstMozVideoBufferData*>(aData);
|
||||
}
|
||||
|
||||
nsRefPtr<layers::PlanarYCbCrImage> mImage;
|
||||
};
|
||||
|
||||
GType gst_moz_video_buffer_data_get_type (void);
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* __GST_MOZ_VIDEO_BUFFER_H__ */
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "MediaResource.h"
|
||||
#include "GStreamerReader.h"
|
||||
#include "GStreamerFormatHelper.h"
|
||||
#include "GStreamerMozVideoBuffer.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
@ -39,8 +40,6 @@ static const int SHORT_FILE_SIZE = 1024 * 1024;
|
||||
// The default resource->Read() size when working in push mode
|
||||
static const int DEFAULT_SOURCE_READ_SIZE = 50 * 1024;
|
||||
|
||||
G_DEFINE_BOXED_TYPE(BufferData, buffer_data, BufferData::Copy, BufferData::Free);
|
||||
|
||||
typedef enum {
|
||||
GST_PLAY_FLAG_VIDEO = (1 << 0),
|
||||
GST_PLAY_FLAG_AUDIO = (1 << 1),
|
||||
@ -139,9 +138,7 @@ nsresult GStreamerReader::Init(MediaDecoderReader* aCloneDonor)
|
||||
gst_pad_add_event_probe(sinkpad,
|
||||
G_CALLBACK(&GStreamerReader::EventProbeCb), this);
|
||||
gst_object_unref(sinkpad);
|
||||
#if GST_VERSION_MICRO >= 36
|
||||
gst_pad_set_bufferalloc_function(sinkpad, GStreamerReader::AllocateVideoBufferCb);
|
||||
#endif
|
||||
gst_pad_set_element_private(sinkpad, this);
|
||||
|
||||
mAudioSink = gst_parse_bin_from_description("capsfilter name=filter ! "
|
||||
@ -532,15 +529,11 @@ bool GStreamerReader::DecodeVideoFrame(bool &aKeyFrameSkip,
|
||||
return false;
|
||||
|
||||
nsRefPtr<PlanarYCbCrImage> image;
|
||||
#if GST_VERSION_MICRO >= 36
|
||||
const GstStructure* structure = gst_buffer_get_qdata(buffer,
|
||||
g_quark_from_string("moz-reader-data"));
|
||||
const GValue* value = gst_structure_get_value(structure, "image");
|
||||
if (value) {
|
||||
BufferData* data = reinterpret_cast<BufferData*>(g_value_get_boxed(value));
|
||||
image = data->mImage;
|
||||
}
|
||||
#endif
|
||||
GstMozVideoBufferData* bufferdata = reinterpret_cast<GstMozVideoBufferData*>
|
||||
GST_IS_MOZ_VIDEO_BUFFER(buffer)?gst_moz_video_buffer_get_data(GST_MOZ_VIDEO_BUFFER(buffer)):nullptr;
|
||||
|
||||
if(bufferdata)
|
||||
image = bufferdata->mImage;
|
||||
|
||||
if (!image) {
|
||||
/* Ugh, upstream is not calling gst_pad_alloc_buffer(). Fallback to
|
||||
@ -844,29 +837,18 @@ GstFlowReturn GStreamerReader::AllocateVideoBufferFull(GstPad* aPad,
|
||||
nsRefPtr<PlanarYCbCrImage> image = dont_AddRef(img);
|
||||
|
||||
/* prepare a GstBuffer pointing to the underlying PlanarYCbCrImage buffer */
|
||||
GstBuffer* buf = gst_buffer_new();
|
||||
GstBuffer* buf = GST_BUFFER(gst_moz_video_buffer_new());
|
||||
GST_BUFFER_SIZE(buf) = aSize;
|
||||
/* allocate the actual YUV buffer */
|
||||
GST_BUFFER_DATA(buf) = image->AllocateAndGetNewBuffer(aSize);
|
||||
|
||||
aImage = image;
|
||||
|
||||
#if GST_VERSION_MICRO >= 36
|
||||
/* create a GBoxed handle to hold the image */
|
||||
BufferData* data = new BufferData(image);
|
||||
/* create a GstMozVideoBufferData to hold the image */
|
||||
GstMozVideoBufferData* bufferdata = new GstMozVideoBufferData(image);
|
||||
|
||||
/* store it in a GValue so we can put it in a GstStructure */
|
||||
GValue value = {0,};
|
||||
g_value_init(&value, buffer_data_get_type());
|
||||
g_value_take_boxed(&value, data);
|
||||
|
||||
/* store the value in the structure */
|
||||
GstStructure* structure = gst_structure_new("moz-reader-data", nullptr);
|
||||
gst_structure_take_value(structure, "image", &value);
|
||||
|
||||
/* and attach the structure to the buffer */
|
||||
gst_buffer_set_qdata(buf, g_quark_from_string("moz-reader-data"), structure);
|
||||
#endif
|
||||
/* Attach bufferdata to our GstMozVideoBuffer, it will take care to free it */
|
||||
gst_moz_video_buffer_set_data(GST_MOZ_VIDEO_BUFFER(buf), bufferdata);
|
||||
|
||||
*aBuf = buf;
|
||||
return GST_FLOW_OK;
|
||||
|
@ -153,21 +153,6 @@ private:
|
||||
int fpsDen;
|
||||
};
|
||||
|
||||
class BufferData {
|
||||
public:
|
||||
BufferData(layers::PlanarYCbCrImage* aImage) : mImage(aImage) {}
|
||||
|
||||
static void* Copy(void* aData) {
|
||||
return new BufferData(reinterpret_cast<BufferData*>(aData)->mImage);
|
||||
}
|
||||
|
||||
static void Free(void* aData) {
|
||||
delete reinterpret_cast<BufferData*>(aData);
|
||||
}
|
||||
|
||||
nsRefPtr<layers::PlanarYCbCrImage> mImage;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
@ -18,6 +18,7 @@ CPPSRCS = \
|
||||
GStreamerReader.cpp \
|
||||
GStreamerDecoder.cpp \
|
||||
GStreamerFormatHelper.cpp \
|
||||
GStreamerMozVideoBuffer.cpp \
|
||||
$(NULL)
|
||||
|
||||
FORCE_STATIC_LIB = 1
|
||||
|
@ -21,6 +21,11 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioBufferSourceNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBuffer)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlaybackRate)
|
||||
if (tmp->Context()) {
|
||||
// AudioNode's Unlink implementation disconnects us from the graph
|
||||
// too, but we need to do this right here to make sure that
|
||||
// UnregisterAudioBufferSourceNode can properly untangle us from
|
||||
// the possibly connected PannerNodes.
|
||||
tmp->DisconnectFromGraph();
|
||||
tmp->Context()->UnregisterAudioBufferSourceNode(tmp);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode)
|
||||
@ -304,12 +309,12 @@ public:
|
||||
mSampleRate == IdealAudioRate());
|
||||
}
|
||||
|
||||
void UpdateSampleRateIfNeeded(AudioNodeStream* aStream)
|
||||
void UpdateSampleRateIfNeeded(AudioNodeStream* aStream, uint32_t aChannels)
|
||||
{
|
||||
if (mPlaybackRateTimeline.HasSimpleValue()) {
|
||||
mPlaybackRate = mPlaybackRateTimeline.GetValue();
|
||||
} else {
|
||||
mPlaybackRate = mPlaybackRateTimeline.GetValueAtTime<TrackTicks>(aStream->GetCurrentPosition());
|
||||
mPlaybackRate = mPlaybackRateTimeline.GetValueAtTime(aStream->GetCurrentPosition());
|
||||
}
|
||||
|
||||
// Make sure the playback rate if something our resampler can work with.
|
||||
@ -319,7 +324,7 @@ public:
|
||||
|
||||
uint32_t currentOutSampleRate, currentInSampleRate;
|
||||
if (ShouldResample()) {
|
||||
SpeexResamplerState* resampler = Resampler(mChannels);
|
||||
SpeexResamplerState* resampler = Resampler(aChannels);
|
||||
speex_resampler_get_rate(resampler, ¤tInSampleRate, ¤tOutSampleRate);
|
||||
uint32_t finalSampleRate = ComputeFinalOutSampleRate();
|
||||
if (currentOutSampleRate != finalSampleRate) {
|
||||
@ -345,7 +350,7 @@ public:
|
||||
// WebKit treats the playbackRate as a k-rate parameter in their code,
|
||||
// despite the spec saying that it should be an a-rate parameter. We treat
|
||||
// it as k-rate. Spec bug: https://www.w3.org/Bugs/Public/show_bug.cgi?id=21592
|
||||
UpdateSampleRateIfNeeded(aStream);
|
||||
UpdateSampleRateIfNeeded(aStream, channels);
|
||||
|
||||
uint32_t written = 0;
|
||||
TrackTicks currentPosition = GetPosition(aStream);
|
||||
@ -412,7 +417,6 @@ AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* aContext)
|
||||
, mOffset(0.0)
|
||||
, mDuration(std::numeric_limits<double>::min())
|
||||
, mPlaybackRate(new AudioParam(this, SendPlaybackRateToStream, 1.0f))
|
||||
, mPannerNode(nullptr)
|
||||
, mLoop(false)
|
||||
, mStartCalled(false)
|
||||
, mOffsetAndDurationRemembered(false)
|
||||
|
@ -37,15 +37,6 @@ public:
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
void UnregisterPannerNode() {
|
||||
mPannerNode = nullptr;
|
||||
}
|
||||
|
||||
void RegisterPannerNode(PannerNode* aPannerNode) {
|
||||
mPannerNode = aPannerNode;
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioBufferSourceNode, AudioNode)
|
||||
|
||||
@ -148,7 +139,6 @@ private:
|
||||
double mDuration;
|
||||
nsRefPtr<AudioBuffer> mBuffer;
|
||||
nsRefPtr<AudioParam> mPlaybackRate;
|
||||
PannerNode* mPannerNode;
|
||||
SelfReference<AudioBufferSourceNode> mPlayingRef; // a reference to self while playing
|
||||
bool mLoop;
|
||||
bool mStartCalled;
|
||||
|
@ -244,6 +244,7 @@ void
|
||||
AudioContext::UnregisterAudioBufferSourceNode(AudioBufferSourceNode* aNode)
|
||||
{
|
||||
mAudioBufferSourceNodes.RemoveEntry(aNode);
|
||||
UpdatePannerSource();
|
||||
}
|
||||
|
||||
void
|
||||
@ -258,13 +259,6 @@ AudioContext::UnregisterScriptProcessorNode(ScriptProcessorNode* aNode)
|
||||
mScriptProcessorNodes.RemoveEntry(aNode);
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
UnregisterPannerNodeOn(nsPtrHashKey<AudioBufferSourceNode>* aEntry, void* aData)
|
||||
{
|
||||
aEntry->GetKey()->UnregisterPannerNode();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
FindConnectedSourcesOn(nsPtrHashKey<PannerNode>* aEntry, void* aData)
|
||||
{
|
||||
@ -275,7 +269,6 @@ FindConnectedSourcesOn(nsPtrHashKey<PannerNode>* aEntry, void* aData)
|
||||
void
|
||||
AudioContext::UpdatePannerSource()
|
||||
{
|
||||
mAudioBufferSourceNodes.EnumerateEntries(UnregisterPannerNodeOn, nullptr);
|
||||
mPannerNodes.EnumerateEntries(FindConnectedSourcesOn, nullptr);
|
||||
}
|
||||
|
||||
|
@ -13,14 +13,18 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
static const uint32_t INVALID_PORT = 0xffffffff;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AudioNode, nsDOMEventTargetHelper)
|
||||
tmp->DisconnectFromGraph();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputNodes)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputParams)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioNode, nsDOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputNodes)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputParams)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(AudioNode, nsDOMEventTargetHelper)
|
||||
@ -59,21 +63,24 @@ AudioNode::~AudioNode()
|
||||
{
|
||||
MOZ_ASSERT(mInputNodes.IsEmpty());
|
||||
MOZ_ASSERT(mOutputNodes.IsEmpty());
|
||||
MOZ_ASSERT(mOutputParams.IsEmpty());
|
||||
}
|
||||
|
||||
template <class InputNode>
|
||||
static uint32_t
|
||||
FindIndexOfNode(const nsTArray<AudioNode::InputNode>& aInputNodes, const AudioNode* aNode)
|
||||
FindIndexOfNode(const nsTArray<InputNode>& aInputNodes, const AudioNode* aNode)
|
||||
{
|
||||
for (uint32_t i = 0; i < aInputNodes.Length(); ++i) {
|
||||
if (aInputNodes[i].mInputNode == aNode) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return nsTArray<AudioNode::InputNode>::NoIndex;
|
||||
return nsTArray<InputNode>::NoIndex;
|
||||
}
|
||||
|
||||
template <class InputNode>
|
||||
static uint32_t
|
||||
FindIndexOfNodeWithPorts(const nsTArray<AudioNode::InputNode>& aInputNodes, const AudioNode* aNode,
|
||||
FindIndexOfNodeWithPorts(const nsTArray<InputNode>& aInputNodes, const AudioNode* aNode,
|
||||
uint32_t aInputPort, uint32_t aOutputPort)
|
||||
{
|
||||
for (uint32_t i = 0; i < aInputNodes.Length(); ++i) {
|
||||
@ -83,7 +90,7 @@ FindIndexOfNodeWithPorts(const nsTArray<AudioNode::InputNode>& aInputNodes, cons
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return nsTArray<AudioNode::InputNode>::NoIndex;
|
||||
return nsTArray<InputNode>::NoIndex;
|
||||
}
|
||||
|
||||
void
|
||||
@ -114,6 +121,16 @@ AudioNode::DisconnectFromGraph()
|
||||
output->mInputNodes.RemoveElementAt(inputIndex);
|
||||
}
|
||||
|
||||
while (!mOutputParams.IsEmpty()) {
|
||||
uint32_t i = mOutputParams.Length() - 1;
|
||||
nsRefPtr<AudioParam> output = mOutputParams[i].forget();
|
||||
mOutputParams.RemoveElementAt(i);
|
||||
uint32_t inputIndex = FindIndexOfNode(output->InputNodes(), this);
|
||||
// It doesn't matter which one we remove, since we're going to remove all
|
||||
// entries for this node anyway.
|
||||
output->RemoveInputNode(inputIndex);
|
||||
}
|
||||
|
||||
DestroyMediaStream();
|
||||
}
|
||||
|
||||
@ -159,6 +176,40 @@ AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
|
||||
Context()->UpdatePannerSource();
|
||||
}
|
||||
|
||||
void
|
||||
AudioNode::Connect(AudioParam& aDestination, uint32_t aOutput,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aOutput >= NumberOfOutputs()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Context() != aDestination.GetParentObject()) {
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (FindIndexOfNodeWithPorts(aDestination.InputNodes(), this, INVALID_PORT, aOutput) !=
|
||||
nsTArray<AudioNode::InputNode>::NoIndex) {
|
||||
// connection already exists.
|
||||
return;
|
||||
}
|
||||
|
||||
mOutputParams.AppendElement(&aDestination);
|
||||
InputNode* input = aDestination.AppendInputNode();
|
||||
input->mInputNode = this;
|
||||
input->mInputPort = INVALID_PORT;
|
||||
input->mOutputPort = aOutput;
|
||||
|
||||
MediaStream* stream = aDestination.Stream();
|
||||
MOZ_ASSERT(stream->AsProcessedStream());
|
||||
ProcessedMediaStream* ps = static_cast<ProcessedMediaStream*>(stream);
|
||||
|
||||
// Setup our stream as an input to the AudioParam's stream
|
||||
input->mStreamPort = ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT);
|
||||
}
|
||||
|
||||
void
|
||||
AudioNode::SendDoubleParameterToStream(uint32_t aIndex, double aValue)
|
||||
{
|
||||
@ -224,6 +275,21 @@ AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = mOutputParams.Length() - 1; i >= 0; --i) {
|
||||
AudioParam* dest = mOutputParams[i];
|
||||
for (int32_t j = dest->InputNodes().Length() - 1; j >= 0; --j) {
|
||||
const InputNode& input = dest->InputNodes()[j];
|
||||
if (input.mInputNode == this && input.mOutputPort == aOutput) {
|
||||
dest->RemoveInputNode(j);
|
||||
// Remove one instance of 'dest' from mOutputParams. There could be
|
||||
// others, and it's not correct to remove them all since some of them
|
||||
// could be for different output ports.
|
||||
mOutputParams.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This disconnection may have disconnected a panner and a source.
|
||||
Context()->UpdatePannerSource();
|
||||
}
|
||||
@ -249,5 +315,11 @@ AudioNode::DestroyMediaStream()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioNode::RemoveOutputParam(AudioParam* aParam)
|
||||
{
|
||||
mOutputParams.RemoveElement(aParam);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class AudioParam;
|
||||
struct ThreeDPoint;
|
||||
|
||||
template<class T>
|
||||
@ -108,6 +109,9 @@ public:
|
||||
virtual void Connect(AudioNode& aDestination, uint32_t aOutput,
|
||||
uint32_t aInput, ErrorResult& aRv);
|
||||
|
||||
virtual void Connect(AudioParam& aDestination, uint32_t aOutput,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual void Disconnect(uint32_t aOutput, ErrorResult& aRv);
|
||||
|
||||
// The following two virtual methods must be implemented by each node type
|
||||
@ -153,6 +157,7 @@ public:
|
||||
AudioNode* mInputNode;
|
||||
nsRefPtr<MediaInputPort> mStreamPort;
|
||||
// The index of the input port this node feeds into.
|
||||
// This is not used for connections to AudioParams.
|
||||
uint32_t mInputPort;
|
||||
// The index of the output port this node comes out of.
|
||||
uint32_t mOutputPort;
|
||||
@ -165,7 +170,10 @@ public:
|
||||
return mInputNodes;
|
||||
}
|
||||
|
||||
void RemoveOutputParam(AudioParam* aParam);
|
||||
|
||||
private:
|
||||
friend class AudioBufferSourceNode;
|
||||
// This could possibly delete 'this'.
|
||||
void DisconnectFromGraph();
|
||||
|
||||
@ -197,6 +205,12 @@ private:
|
||||
// exact matching entry, since mOutputNodes doesn't include the port
|
||||
// identifiers and the same node could be connected on multiple ports.
|
||||
nsTArray<nsRefPtr<AudioNode> > mOutputNodes;
|
||||
// For every mOutputParams entry, there is a corresponding entry in
|
||||
// AudioParam::mInputNodes of the mOutputParams entry. We won't necessarily be
|
||||
// able to identify the exact matching entry, since mOutputParams doesn't
|
||||
// include the port identifiers and the same node could be connected on
|
||||
// multiple ports.
|
||||
nsTArray<nsRefPtr<AudioParam> > mOutputParams;
|
||||
uint32_t mChannelCount;
|
||||
ChannelCountMode mChannelCountMode;
|
||||
ChannelInterpretation mChannelInterpretation;
|
||||
|
@ -9,11 +9,36 @@
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/AudioParamBinding.h"
|
||||
#include "AudioNodeEngine.h"
|
||||
#include "AudioNodeStream.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(AudioParam, mNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioParam)
|
||||
tmp->DisconnectFromGraphAndDestroyStream();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioParam)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(AudioParam)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(AudioParam)
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt)
|
||||
AudioParam::Release()
|
||||
{
|
||||
if (mRefCnt.get() == 1) {
|
||||
// We are about to be deleted, disconnect the object from the graph before
|
||||
// the derived type is destroyed.
|
||||
DisconnectFromGraphAndDestroyStream();
|
||||
}
|
||||
NS_IMPL_CC_NATIVE_RELEASE_BODY(AudioParam)
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AudioParam, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AudioParam, Release)
|
||||
@ -31,6 +56,7 @@ AudioParam::AudioParam(AudioNode* aNode,
|
||||
|
||||
AudioParam::~AudioParam()
|
||||
{
|
||||
MOZ_ASSERT(mInputNodes.IsEmpty());
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@ -39,6 +65,81 @@ AudioParam::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
||||
return AudioParamBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
void
|
||||
AudioParam::DisconnectFromGraphAndDestroyStream()
|
||||
{
|
||||
// Addref this temporarily so the refcount bumping below doesn't destroy us
|
||||
// prematurely
|
||||
nsRefPtr<AudioParam> kungFuDeathGrip = this;
|
||||
|
||||
while (!mInputNodes.IsEmpty()) {
|
||||
uint32_t i = mInputNodes.Length() - 1;
|
||||
nsRefPtr<AudioNode> input = mInputNodes[i].mInputNode;
|
||||
mInputNodes.RemoveElementAt(i);
|
||||
input->RemoveOutputParam(this);
|
||||
}
|
||||
|
||||
if (mNodeStreamPort) {
|
||||
mNodeStreamPort->Destroy();
|
||||
mNodeStreamPort = nullptr;
|
||||
}
|
||||
|
||||
if (mStream) {
|
||||
mStream->Destroy();
|
||||
mStream = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
MediaStream*
|
||||
AudioParam::Stream()
|
||||
{
|
||||
if (mStream) {
|
||||
return mStream;
|
||||
}
|
||||
|
||||
AudioNodeEngine* engine = new AudioNodeEngine(nullptr);
|
||||
nsRefPtr<AudioNodeStream> stream = mNode->Context()->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
|
||||
|
||||
// Force the input to have only one channel, and make it down-mix using
|
||||
// the speaker rules if needed.
|
||||
stream->SetChannelMixingParametersImpl(1, ChannelCountMode::Explicit, ChannelInterpretation::Speakers);
|
||||
// Mark as an AudioParam helper stream
|
||||
stream->SetAudioParamHelperStream();
|
||||
|
||||
mStream = stream.forget();
|
||||
|
||||
// Setup the AudioParam's stream as an input to the owner AudioNode's stream
|
||||
MediaStream* nodeStream = mNode->Stream();
|
||||
MOZ_ASSERT(nodeStream->AsProcessedStream());
|
||||
ProcessedMediaStream* ps = static_cast<ProcessedMediaStream*>(nodeStream);
|
||||
mNodeStreamPort = ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT);
|
||||
|
||||
// Let the MSG's copy of AudioParamTimeline know about the change in the stream
|
||||
mCallback(mNode);
|
||||
|
||||
return mStream;
|
||||
}
|
||||
|
||||
float
|
||||
AudioParamTimeline::AudioNodeInputValue(size_t aCounter) const
|
||||
{
|
||||
MOZ_ASSERT(mStream);
|
||||
|
||||
// If we have a chunk produced by the AudioNode inputs to the AudioParam,
|
||||
// get its value now. We use aCounter to tell us which frame of the last
|
||||
// AudioChunk to look at.
|
||||
float audioNodeInputValue = 0.0f;
|
||||
const AudioChunk& lastAudioNodeChunk =
|
||||
static_cast<AudioNodeStream*>(mStream.get())->LastChunk();
|
||||
if (!lastAudioNodeChunk.IsNull()) {
|
||||
MOZ_ASSERT(lastAudioNodeChunk.GetDuration() == WEBAUDIO_BLOCK_SIZE);
|
||||
audioNodeInputValue =
|
||||
static_cast<const float*>(lastAudioNodeChunk.mChannelData[0])[aCounter];
|
||||
}
|
||||
|
||||
return audioNodeInputValue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,8 @@ public:
|
||||
float aDefaultValue);
|
||||
virtual ~AudioParam();
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AudioParam)
|
||||
NS_IMETHOD_(nsrefcnt) AddRef(void);
|
||||
NS_IMETHOD_(nsrefcnt) Release(void);
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AudioParam)
|
||||
|
||||
AudioContext* GetParentObject() const
|
||||
@ -104,10 +105,44 @@ public:
|
||||
return mDefaultValue;
|
||||
}
|
||||
|
||||
AudioNode* Node() const
|
||||
{
|
||||
return mNode;
|
||||
}
|
||||
|
||||
const nsTArray<AudioNode::InputNode>& InputNodes() const
|
||||
{
|
||||
return mInputNodes;
|
||||
}
|
||||
|
||||
void RemoveInputNode(uint32_t aIndex)
|
||||
{
|
||||
mInputNodes.RemoveElementAt(aIndex);
|
||||
}
|
||||
|
||||
AudioNode::InputNode* AppendInputNode()
|
||||
{
|
||||
return mInputNodes.AppendElement();
|
||||
}
|
||||
|
||||
void DisconnectFromGraphAndDestroyStream();
|
||||
|
||||
// May create the stream if it doesn't exist
|
||||
MediaStream* Stream();
|
||||
|
||||
protected:
|
||||
nsCycleCollectingAutoRefCnt mRefCnt;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
|
||||
private:
|
||||
nsRefPtr<AudioNode> mNode;
|
||||
// For every InputNode, there is a corresponding entry in mOutputParams of the
|
||||
// InputNode's mInputNode.
|
||||
nsTArray<AudioNode::InputNode> mInputNodes;
|
||||
CallbackType mCallback;
|
||||
const float mDefaultValue;
|
||||
// The input port used to connect the AudioParam's stream to its node's stream
|
||||
nsRefPtr<MediaInputPort> mNodeStreamPort;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -7,17 +7,66 @@
|
||||
#ifndef AudioParamTimeline_h_
|
||||
#define AudioParamTimeline_h_
|
||||
|
||||
// This header is intended to make it possible to use AudioParamTimeline
|
||||
// from multiple places without dealing with #include hell!
|
||||
|
||||
#include "AudioEventTimeline.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "AudioSegment.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
||||
typedef AudioEventTimeline<ErrorResult> AudioParamTimeline;
|
||||
// This helper class is used to represent the part of the AudioParam
|
||||
// class that gets sent to AudioNodeEngine instances. In addition to
|
||||
// AudioEventTimeline methods, it holds a pointer to an optional
|
||||
// MediaStream which represents the AudioNode inputs to the AudioParam.
|
||||
// This MediaStream is managed by the AudioParam subclass on the main
|
||||
// thread, and can only be obtained from the AudioNodeEngine instances
|
||||
// consuming this class.
|
||||
class AudioParamTimeline : public AudioEventTimeline<ErrorResult>
|
||||
{
|
||||
typedef AudioEventTimeline<ErrorResult> BaseClass;
|
||||
|
||||
public:
|
||||
explicit AudioParamTimeline(float aDefaultValue)
|
||||
: BaseClass(aDefaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
MediaStream* Stream() const
|
||||
{
|
||||
return mStream;
|
||||
}
|
||||
|
||||
bool HasSimpleValue() const
|
||||
{
|
||||
return BaseClass::HasSimpleValue() && !mStream;
|
||||
}
|
||||
|
||||
// Get the value of the AudioParam at time aTime + aCounter.
|
||||
// aCounter here is an offset to aTime if we try to get the value in ticks,
|
||||
// otherwise it should always be zero. aCounter is meant to be used when
|
||||
// getting the value of an a-rate AudioParam for each tick inside an
|
||||
// AudioNodeEngine implementation.
|
||||
template<class TimeType>
|
||||
float GetValueAtTime(TimeType aTime, size_t aCounter = 0) const
|
||||
{
|
||||
MOZ_ASSERT(aCounter < WEBAUDIO_BLOCK_SIZE);
|
||||
MOZ_ASSERT(!aCounter || !HasSimpleValue());
|
||||
|
||||
// Mix the value of the AudioParam itself with that of the AudioNode inputs.
|
||||
return BaseClass::GetValueAtTime(static_cast<TimeType>(aTime + aCounter)) +
|
||||
(mStream ? AudioNodeInputValue(aCounter) : 0.0f);
|
||||
}
|
||||
|
||||
private:
|
||||
float AudioNodeInputValue(size_t aCounter) const;
|
||||
|
||||
protected:
|
||||
// This is created lazily when needed.
|
||||
nsRefPtr<MediaStream> mStream;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ public:
|
||||
TrackTicks tick = aStream->GetCurrentPosition();
|
||||
for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
|
||||
computedDelay[counter] = std::max(0.0, std::min(mMaxDelay,
|
||||
double(mDelay.GetValueAtTime<TrackTicks>(tick + counter))));
|
||||
double(mDelay.GetValueAtTime(tick, counter))));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,8 +76,8 @@ public:
|
||||
// XXX we need to add a method to AudioEventTimeline to compute this buffer directly.
|
||||
float computedGain[WEBAUDIO_BLOCK_SIZE];
|
||||
for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
|
||||
TrackTicks tick = aStream->GetCurrentPosition() + counter;
|
||||
computedGain[counter] = mGain.GetValueAtTime<TrackTicks>(tick) * aInput.mVolume;
|
||||
TrackTicks tick = aStream->GetCurrentPosition();
|
||||
computedGain[counter] = mGain.GetValueAtTime(tick, counter) * aInput.mVolume;
|
||||
}
|
||||
|
||||
// Apply the gain to the output buffer
|
||||
|
@ -482,9 +482,6 @@ PannerNode::FindConnectedSources()
|
||||
mSources.Clear();
|
||||
std::set<AudioNode*> cycleSet;
|
||||
FindConnectedSources(this, mSources, cycleSet);
|
||||
for (unsigned i = 0; i < mSources.Length(); i++) {
|
||||
mSources[i]->RegisterPannerNode(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -46,6 +46,15 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Connect(AudioParam& aDestination, uint32_t aOutput,
|
||||
ErrorResult& aRv) MOZ_OVERRIDE
|
||||
{
|
||||
AudioNode::Connect(aDestination, aOutput, aRv);
|
||||
if (!aRv.Failed()) {
|
||||
mPlayingRef.Take(this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Disconnect(uint32_t aOutput, ErrorResult& aRv) MOZ_OVERRIDE
|
||||
{
|
||||
AudioNode::Disconnect(aOutput, aRv);
|
||||
|
@ -20,11 +20,14 @@ MOCHITEST_FILES := \
|
||||
test_bug866570.html \
|
||||
test_bug866737.html \
|
||||
test_bug867089.html \
|
||||
test_bug867203.html \
|
||||
test_analyserNode.html \
|
||||
test_AudioBuffer.html \
|
||||
test_AudioContext.html \
|
||||
test_AudioListener.html \
|
||||
test_AudioParam.html \
|
||||
test_audioParamExponentialRamp.html \
|
||||
test_audioParamLinearRamp.html \
|
||||
test_audioBufferSourceNode.html \
|
||||
test_audioBufferSourceNodeLazyLoopParam.html \
|
||||
test_audioBufferSourceNodeLoop.html \
|
||||
@ -40,6 +43,7 @@ MOCHITEST_FILES := \
|
||||
test_dynamicsCompressorNode.html \
|
||||
test_gainNode.html \
|
||||
test_mixingRules.html \
|
||||
test_nodeToParamConnection.html \
|
||||
test_pannerNode.html \
|
||||
test_scriptProcessorNode.html \
|
||||
test_scriptProcessorNodeChannelCount.html \
|
||||
|
@ -0,0 +1,63 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.exponentialRampToValue</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
SpecialPowers.setBoolPref("media.webaudio.enabled", true);
|
||||
|
||||
var context = new AudioContext();
|
||||
|
||||
var V0 = 0.1;
|
||||
var V1 = 0.9;
|
||||
var T0 = 0;
|
||||
var T1 = 2048 / context.sampleRate;
|
||||
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
var t = i / context.sampleRate;
|
||||
expectedBuffer.getChannelData(0)[i] = V0 * Math.pow(V1 / V0, (t - T0) / (T1 - T0));
|
||||
}
|
||||
|
||||
var destination = context.destination;
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.setValueAtTime(V0, 0);
|
||||
gain.gain.exponentialRampToValueAtTime(V1, 2048/context.sampleRate);
|
||||
|
||||
var sp = context.createScriptProcessor(2048, 1);
|
||||
source.connect(gain);
|
||||
gain.connect(sp);
|
||||
sp.connect(destination);
|
||||
|
||||
source.start(0);
|
||||
sp.onaudioprocess = function(e) {
|
||||
is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
|
||||
compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
|
||||
|
||||
sp.onaudioprocess = null;
|
||||
|
||||
SpecialPowers.clearUserPref("media.webaudio.enabled");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
63
content/media/webaudio/test/test_audioParamLinearRamp.html
Normal file
63
content/media/webaudio/test/test_audioParamLinearRamp.html
Normal file
@ -0,0 +1,63 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam.linearRampToValue</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
SpecialPowers.setBoolPref("media.webaudio.enabled", true);
|
||||
|
||||
var context = new AudioContext();
|
||||
|
||||
var V0 = 0.1;
|
||||
var V1 = 0.9;
|
||||
var T0 = 0;
|
||||
var T1 = 2048 / context.sampleRate;
|
||||
|
||||
var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
}
|
||||
var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
var t = i / context.sampleRate;
|
||||
expectedBuffer.getChannelData(0)[i] = V0 + (V1 - V0) * ((t - T0) / (T1 - T0));
|
||||
}
|
||||
|
||||
var destination = context.destination;
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
|
||||
var gain = context.createGain();
|
||||
gain.gain.setValueAtTime(V0, 0);
|
||||
gain.gain.linearRampToValueAtTime(V1, 2048/context.sampleRate);
|
||||
|
||||
var sp = context.createScriptProcessor(2048, 1);
|
||||
source.connect(gain);
|
||||
gain.connect(sp);
|
||||
sp.connect(destination);
|
||||
|
||||
source.start(0);
|
||||
sp.onaudioprocess = function(e) {
|
||||
is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
|
||||
compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
|
||||
|
||||
sp.onaudioprocess = null;
|
||||
|
||||
SpecialPowers.clearUserPref("media.webaudio.enabled");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
36
content/media/webaudio/test/test_bug867203.html
Normal file
36
content/media/webaudio/test/test_bug867203.html
Normal file
@ -0,0 +1,36 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Crashtest for bug 867203</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SpecialPowers.setBoolPref("media.webaudio.enabled", true);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
var ctx = new AudioContext();
|
||||
|
||||
var panner1 = ctx.createPanner();
|
||||
panner1.setVelocity(1, 1, 1);
|
||||
ctx.listener.setVelocity(1, 1, 1);
|
||||
(function() {
|
||||
ctx.createBufferSource().connect(panner1);
|
||||
})();
|
||||
SpecialPowers.forceGC();
|
||||
SpecialPowers.forceCC();
|
||||
ctx.createPanner();
|
||||
|
||||
ok(true, "We did not crash.");
|
||||
SpecialPowers.clearUserPref("media.webaudio.enabled");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
69
content/media/webaudio/test/test_nodeToParamConnection.html
Normal file
69
content/media/webaudio/test/test_nodeToParamConnection.html
Normal file
@ -0,0 +1,69 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test connecting an AudioNode to an AudioParam</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
SpecialPowers.setBoolPref("media.webaudio.enabled", true);
|
||||
|
||||
var context = new AudioContext();
|
||||
var buffer = context.createBuffer(2, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
for (var j = 0; j < 2; ++j) {
|
||||
buffer.getChannelData(j)[i] = Math.sin(440 * 2 * (j + 1) * Math.PI * i / context.sampleRate);
|
||||
}
|
||||
}
|
||||
var sourceBuffer = context.createBuffer(2, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
sourceBuffer.getChannelData(0)[i] = 1;
|
||||
sourceBuffer.getChannelData(1)[i] = -1;
|
||||
}
|
||||
var expectedBuffer = context.createBuffer(2, 2048, context.sampleRate);
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
expectedBuffer.getChannelData(0)[i] = 1 + (buffer.getChannelData(0)[i] + buffer.getChannelData(1)[i]) / 2;
|
||||
expectedBuffer.getChannelData(1)[i] = -(1 + (buffer.getChannelData(0)[i] + buffer.getChannelData(1)[i]) / 2);
|
||||
}
|
||||
|
||||
var destination = context.destination;
|
||||
|
||||
var paramSource = context.createBufferSource();
|
||||
paramSource.buffer = buffer;
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = sourceBuffer;
|
||||
|
||||
var gain = context.createGain();
|
||||
|
||||
paramSource.connect(gain.gain);
|
||||
source.connect(gain);
|
||||
|
||||
var sp = context.createScriptProcessor(2048);
|
||||
gain.connect(sp);
|
||||
sp.connect(destination);
|
||||
|
||||
paramSource.start(0);
|
||||
source.start(0);
|
||||
sp.onaudioprocess = function(e) {
|
||||
is(e.inputBuffer.numberOfChannels, 2, "Correct input channel count");
|
||||
compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
|
||||
compareBuffers(e.inputBuffer.getChannelData(1), expectedBuffer.getChannelData(1));
|
||||
|
||||
sp.onaudioprocess = null;
|
||||
|
||||
SpecialPowers.clearUserPref("media.webaudio.enabled");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -24,7 +24,7 @@ function expectTypeError(func) {
|
||||
}
|
||||
|
||||
function fuzzyCompare(a, b) {
|
||||
return Math.abs(a - b) < 5e-5;
|
||||
return Math.abs(a - b) < 9e-3;
|
||||
}
|
||||
|
||||
function compareBuffers(buf1, buf2,
|
||||
|
@ -8,8 +8,8 @@ MODULE = 'content'
|
||||
XPIDL_MODULE = 'dom_webspeechrecognition'
|
||||
|
||||
XPIDL_SOURCES = [
|
||||
'nsIDOMSpeechRecognitionEvent.idl',
|
||||
'nsIDOMSpeechRecognitionError.idl',
|
||||
'nsIDOMSpeechRecognitionEvent.idl',
|
||||
'nsISpeechRecognitionService.idl'
|
||||
]
|
||||
|
||||
|
@ -15,17 +15,16 @@ EXPORTS += [
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'nsSVGAnimatedTransformList.h',
|
||||
'SVGAElement.h',
|
||||
'SVGAltGlyphElement.h',
|
||||
'SVGAngle.h',
|
||||
'SVGAnimateElement.h',
|
||||
'SVGAnimateMotionElement.h',
|
||||
'SVGAnimateTransformElement.h',
|
||||
'SVGAnimatedAngle.h',
|
||||
'SVGAnimatedBoolean.h',
|
||||
'SVGAnimatedLength.h',
|
||||
'SVGAnimatedTransformList.h',
|
||||
'SVGAnimateElement.h',
|
||||
'SVGAnimateMotionElement.h',
|
||||
'SVGAnimateTransformElement.h',
|
||||
'SVGAnimationElement.h',
|
||||
'SVGCircleElement.h',
|
||||
'SVGClipPathElement.h',
|
||||
@ -58,38 +57,39 @@ EXPORTS.mozilla.dom += [
|
||||
'SVGGElement.h',
|
||||
'SVGGradientElement.h',
|
||||
'SVGGraphicsElement.h',
|
||||
'SVGImageElement.h',
|
||||
'SVGIRect.h',
|
||||
'SVGImageElement.h',
|
||||
'SVGLineElement.h',
|
||||
'SVGMPathElement.h',
|
||||
'SVGMarkerElement.h',
|
||||
'SVGMaskElement.h',
|
||||
'SVGMatrix.h',
|
||||
'SVGMetadataElement.h',
|
||||
'SVGMPathElement.h',
|
||||
'SVGPathElement.h',
|
||||
'SVGPatternElement.h',
|
||||
'SVGPolygonElement.h',
|
||||
'SVGPolylineElement.h',
|
||||
'SVGRectElement.h',
|
||||
'SVGRect.h',
|
||||
'SVGRectElement.h',
|
||||
'SVGSVGElement.h',
|
||||
'SVGScriptElement.h',
|
||||
'SVGSetElement.h',
|
||||
'SVGStopElement.h',
|
||||
'SVGStyleElement.h',
|
||||
'SVGSVGElement.h',
|
||||
'SVGSwitchElement.h',
|
||||
'SVGSymbolElement.h',
|
||||
'SVGTSpanElement.h',
|
||||
'SVGTests.h',
|
||||
'SVGTextContentElement.h',
|
||||
'SVGTextElement.h',
|
||||
'SVGTextPathElement.h',
|
||||
'SVGTextPositioningElement.h',
|
||||
'SVGTitleElement.h',
|
||||
'SVGTransformableElement.h',
|
||||
'SVGTransform.h',
|
||||
'SVGTSpanElement.h',
|
||||
'SVGTransformableElement.h',
|
||||
'SVGUseElement.h',
|
||||
'SVGViewElement.h',
|
||||
'SVGZoomEvent.h',
|
||||
'nsSVGAnimatedTransformList.h',
|
||||
]
|
||||
|
||||
|
@ -1063,10 +1063,10 @@ FindObjectClass(JSContext* cx, JSObject* aGlobalObject)
|
||||
{
|
||||
NS_ASSERTION(!sObjectClass,
|
||||
"Double set of sObjectClass");
|
||||
JSObject *obj, *proto = aGlobalObject;
|
||||
JS::Rooted<JSObject*> obj(cx), proto(cx, aGlobalObject);
|
||||
do {
|
||||
obj = proto;
|
||||
js::GetObjectProto(cx, obj, &proto);
|
||||
js::GetObjectProto(cx, obj, proto.address());
|
||||
} while (proto);
|
||||
|
||||
sObjectClass = js::GetObjectJSClass(obj);
|
||||
@ -1206,7 +1206,7 @@ WrapNative(JSContext *cx, JSObject *scope, nsISupports *native,
|
||||
// implement nsWrapperCache, and nativeWrapperCache must be |native|'s
|
||||
// nsWrapperCache.
|
||||
static inline nsresult
|
||||
WrapNativeParent(JSContext *cx, JSObject *scope, nsISupports *native,
|
||||
WrapNativeParent(JSContext *cx, JS::Handle<JSObject*> scope, nsISupports *native,
|
||||
nsWrapperCache *nativeWrapperCache, JSObject **parentObj)
|
||||
{
|
||||
// In the common case, |native| is a wrapper cache with an existing wrapper
|
||||
@ -1217,7 +1217,7 @@ WrapNativeParent(JSContext *cx, JSObject *scope, nsISupports *native,
|
||||
cache == nativeWrapperCache, "What happened here?");
|
||||
#endif
|
||||
|
||||
JSObject* obj = nativeWrapperCache->GetWrapper();
|
||||
JS::Rooted<JSObject*> obj(cx, nativeWrapperCache->GetWrapper());
|
||||
if (obj) {
|
||||
#ifdef DEBUG
|
||||
jsval debugVal;
|
||||
@ -1230,16 +1230,16 @@ WrapNativeParent(JSContext *cx, JSObject *scope, nsISupports *native,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
jsval v;
|
||||
nsresult rv = WrapNative(cx, scope, native, nativeWrapperCache, false, &v);
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
nsresult rv = WrapNative(cx, scope, native, nativeWrapperCache, false, v.address());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
*parentObj = JSVAL_TO_OBJECT(v);
|
||||
*parentObj = v.toObjectOrNull();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template<class P>
|
||||
static inline nsresult
|
||||
WrapNativeParent(JSContext *cx, JSObject *scope, P *parent,
|
||||
WrapNativeParent(JSContext *cx, JS::Handle<JSObject*> scope, P *parent,
|
||||
JSObject **parentObj)
|
||||
{
|
||||
return WrapNativeParent(cx, scope, ToSupports(parent), parent, parentObj);
|
||||
@ -2658,11 +2658,11 @@ nsresult
|
||||
nsDOMClassInfo::ResolveConstructor(JSContext *cx, JSObject *obj,
|
||||
JSObject **objp)
|
||||
{
|
||||
JSObject *global = ::JS_GetGlobalForObject(cx, obj);
|
||||
JS::Rooted<JSObject*> global(cx, ::JS_GetGlobalForObject(cx, obj));
|
||||
|
||||
jsval val;
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
JSAutoRequest ar(cx);
|
||||
if (!::JS_LookupProperty(cx, global, mData->mName, &val)) {
|
||||
if (!::JS_LookupProperty(cx, global, mData->mName, val.address())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
@ -2731,9 +2731,9 @@ nsDOMClassInfo::CheckAccess(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
|
||||
if ((mode_type == JSACC_WATCH || mode_type == JSACC_PROTO) && sSecMan) {
|
||||
nsresult rv;
|
||||
JSObject *real_obj;
|
||||
JS::Rooted<JSObject*> real_obj(cx);
|
||||
if (wrapper) {
|
||||
rv = wrapper->GetJSObject(&real_obj);
|
||||
rv = wrapper->GetJSObject(real_obj.address());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else {
|
||||
@ -2837,7 +2837,7 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * proto)
|
||||
nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * aProto)
|
||||
{
|
||||
uint32_t flags = (mData->mScriptableFlags & DONT_ENUM_STATIC_PROPS)
|
||||
? 0
|
||||
@ -2848,6 +2848,7 @@ nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * proto)
|
||||
count++;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> proto(cx, aProto);
|
||||
if (!xpc::DOM_DefineQuickStubs(cx, proto, flags, count, mData->mInterfaces)) {
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
@ -2893,7 +2894,7 @@ nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * proto)
|
||||
// HTMLElement.prototype.foopy = function () { ... } Now, calling
|
||||
// document.body.foopy() needs to ensure that looking up foopy on
|
||||
// document.body's prototype will find the right function.
|
||||
JSObject *global = ::JS_GetGlobalForObject(cx, proto);
|
||||
JS::Rooted<JSObject*> global(cx, ::JS_GetGlobalForObject(cx, proto));
|
||||
|
||||
// Only do this if the global object is a window.
|
||||
// XXX Is there a better way to check this?
|
||||
@ -3141,9 +3142,9 @@ ChildWindowGetter(JSContext *cx, JSHandleObject obj, JSHandleId id,
|
||||
return true;
|
||||
|
||||
// Wrap the child for JS.
|
||||
jsval v;
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
nsresult rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), child,
|
||||
/* aAllowWrapping = */ true, &v);
|
||||
/* aAllowWrapping = */ true, v.address());
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
vp.set(v);
|
||||
return true;
|
||||
@ -3198,8 +3199,8 @@ nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSHandleObject obj,
|
||||
}
|
||||
}
|
||||
|
||||
JSObject *proto;
|
||||
if (!::JS_GetPrototype(cx, obj, &proto)) {
|
||||
JS::Rooted<JSObject*> proto(cx);
|
||||
if (!::JS_GetPrototype(cx, obj, proto.address())) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
JSBool hasProp;
|
||||
@ -3235,13 +3236,13 @@ nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSHandleObject obj,
|
||||
}
|
||||
|
||||
if (result) {
|
||||
jsval v;
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
nsresult rv = WrapNative(cx, obj, result, cache, true, &v,
|
||||
nsresult rv = WrapNative(cx, obj, result, cache, true, v.address(),
|
||||
getter_AddRefs(holder));
|
||||
NS_ENSURE_SUCCESS(rv, JS_FALSE);
|
||||
|
||||
if (!JS_WrapValue(cx, &v) ||
|
||||
if (!JS_WrapValue(cx, v.address()) ||
|
||||
!JS_DefinePropertyById(cx, obj, id, v, JS_PropertyStub, JS_StrictPropertyStub, 0)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -3256,12 +3257,12 @@ nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSHandleObject obj,
|
||||
JSBool
|
||||
nsWindowSH::InvalidateGlobalScopePolluter(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *proto;
|
||||
JS::Rooted<JSObject*> proto(cx);
|
||||
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
for (;;) {
|
||||
if (!::JS_GetPrototype(cx, obj, &proto)) {
|
||||
if (!::JS_GetPrototype(cx, obj, proto.address())) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (!proto) {
|
||||
@ -3270,8 +3271,8 @@ nsWindowSH::InvalidateGlobalScopePolluter(JSContext *cx, JSObject *obj)
|
||||
|
||||
if (JS_GetClass(proto) == &sGlobalScopePolluterClass) {
|
||||
|
||||
JSObject *proto_proto;
|
||||
if (!::JS_GetPrototype(cx, proto, &proto_proto)) {
|
||||
JS::Rooted<JSObject*> proto_proto(cx);
|
||||
if (!::JS_GetPrototype(cx, proto, proto_proto.address())) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
@ -3290,22 +3291,22 @@ nsWindowSH::InvalidateGlobalScopePolluter(JSContext *cx, JSObject *obj)
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsWindowSH::InstallGlobalScopePolluter(JSContext *cx, JSObject *obj)
|
||||
nsWindowSH::InstallGlobalScopePolluter(JSContext *cx, JS::Handle<JSObject*> obj)
|
||||
{
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
JSObject *gsp = ::JS_NewObjectWithUniqueType(cx, &sGlobalScopePolluterClass, nullptr, obj);
|
||||
JS::Rooted<JSObject*> gsp(cx, ::JS_NewObjectWithUniqueType(cx, &sGlobalScopePolluterClass, nullptr, obj));
|
||||
if (!gsp) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
JSObject *o = obj, *proto;
|
||||
JS::Rooted<JSObject*> o(cx, obj), proto(cx);
|
||||
|
||||
// Find the place in the prototype chain where we want this global
|
||||
// scope polluter (right before Object.prototype).
|
||||
|
||||
for (;;) {
|
||||
if (!::JS_GetPrototype(cx, o, &proto)) {
|
||||
if (!::JS_GetPrototype(cx, o, proto.address())) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (!proto) {
|
||||
@ -3353,8 +3354,9 @@ ResolveGlobalName(const nsAString& aName, void* aClosure)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowSH::Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, bool *_retval)
|
||||
JSObject *aObj, bool *_retval)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(cx, aObj);
|
||||
if (!xpc::WrapperFactory::IsXrayWrapper(obj)) {
|
||||
*_retval = JS_EnumerateStandardClasses(cx, obj);
|
||||
if (!*_retval) {
|
||||
@ -3390,8 +3392,9 @@ FindConstructorFunc(const nsDOMClassInfoData *aDOMClassInfoData)
|
||||
static nsresult
|
||||
BaseStubConstructor(nsIWeakReference* aWeakOwner,
|
||||
const nsGlobalNameStruct *name_struct, JSContext *cx,
|
||||
JSObject *obj, unsigned argc, jsval *argv, jsval *rval)
|
||||
JSObject *aObj, unsigned argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(cx, aObj);
|
||||
MOZ_ASSERT(obj);
|
||||
|
||||
nsresult rv;
|
||||
@ -3440,8 +3443,8 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner,
|
||||
} else {
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = do_QueryInterface(native);
|
||||
|
||||
JSObject* object = nullptr;
|
||||
wrappedJS->GetJSObject(&object);
|
||||
JS::Rooted<JSObject*> object(cx);
|
||||
wrappedJS->GetJSObject(object.address());
|
||||
if (!object) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
@ -3452,9 +3455,9 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner,
|
||||
JSAutoRequest ar(cx);
|
||||
JSAutoCompartment ac(cx, object);
|
||||
|
||||
JS::Value thisValue = JSVAL_VOID;
|
||||
JS::Value funval;
|
||||
if (!JS_GetProperty(cx, object, "constructor", &funval) || !funval.isObject()) {
|
||||
JS::Rooted<JS::Value> thisValue(cx, JS::UndefinedValue());
|
||||
JS::Rooted<JS::Value> funval(cx);
|
||||
if (!JS_GetProperty(cx, object, "constructor", funval.address()) || !funval.isObject()) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
@ -3463,7 +3466,7 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner,
|
||||
thisValue.setObject(*object);
|
||||
|
||||
{
|
||||
JSObject* thisObject = &thisValue.toObject();
|
||||
JS::Rooted<JSObject*> thisObject(cx, &thisValue.toObject());
|
||||
|
||||
// wrap parameters in the target compartment
|
||||
// we also pass in the calling window as the first argument
|
||||
@ -3500,7 +3503,7 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner,
|
||||
}
|
||||
|
||||
static nsresult
|
||||
DefineInterfaceConstants(JSContext *cx, JSObject *obj, const nsIID *aIID)
|
||||
DefineInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj, const nsIID *aIID)
|
||||
{
|
||||
nsCOMPtr<nsIInterfaceInfoManager>
|
||||
iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID));
|
||||
@ -3767,7 +3770,7 @@ public:
|
||||
return ok ? NS_OK : NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult ResolveInterfaceConstants(JSContext *cx, JSObject *obj);
|
||||
nsresult ResolveInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj);
|
||||
|
||||
private:
|
||||
const nsGlobalNameStruct *GetNameStruct()
|
||||
@ -3934,7 +3937,7 @@ nsDOMConstructor::HasInstance(nsIXPConnectWrappedNative *wrapper,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSObject *dom_obj = JSVAL_TO_OBJECT(v);
|
||||
JS::Rooted<JSObject*> dom_obj(cx, v.toObjectOrNull());
|
||||
NS_ASSERTION(dom_obj, "nsDOMConstructor::HasInstance couldn't get object");
|
||||
|
||||
// This might not be the right object, if there are wrappers. Unwrap if we can.
|
||||
@ -3957,8 +3960,8 @@ nsDOMConstructor::HasInstance(nsIXPConnectWrappedNative *wrapper,
|
||||
if (!name_struct) {
|
||||
// This isn't a normal DOM object, see if this constructor lives on its
|
||||
// prototype chain.
|
||||
jsval val;
|
||||
if (!JS_GetProperty(cx, obj, "prototype", &val)) {
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
if (!JS_GetProperty(cx, obj, "prototype", val.address())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
@ -3966,11 +3969,11 @@ nsDOMConstructor::HasInstance(nsIXPConnectWrappedNative *wrapper,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSObject *dot_prototype = JSVAL_TO_OBJECT(val);
|
||||
JS::Rooted<JSObject*> dot_prototype(cx, val.toObjectOrNull());
|
||||
|
||||
JSObject *proto = dom_obj;
|
||||
JS::Rooted<JSObject*> proto(cx, dom_obj);
|
||||
for (;;) {
|
||||
if (!JS_GetPrototype(cx, proto, &proto)) {
|
||||
if (!JS_GetPrototype(cx, proto, proto.address())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
if (!proto) {
|
||||
@ -4091,7 +4094,7 @@ nsDOMConstructor::HasInstance(nsIXPConnectWrappedNative *wrapper,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMConstructor::ResolveInterfaceConstants(JSContext *cx, JSObject *obj)
|
||||
nsDOMConstructor::ResolveInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj)
|
||||
{
|
||||
const nsGlobalNameStruct *class_name_struct = GetNameStruct();
|
||||
if (!class_name_struct)
|
||||
@ -4188,9 +4191,9 @@ GetXPCProto(nsIXPConnect *aXPConnect, JSContext *cx, nsGlobalWindow *aWin,
|
||||
aProto);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
JSObject *proto_obj;
|
||||
(*aProto)->GetJSObject(&proto_obj);
|
||||
if (!JS_WrapObject(cx, &proto_obj)) {
|
||||
JS::Rooted<JSObject*> proto_obj(cx);
|
||||
(*aProto)->GetJSObject(proto_obj.address());
|
||||
if (!JS_WrapObject(cx, proto_obj.address())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@ -4219,10 +4222,10 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
jsval v;
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
|
||||
rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
|
||||
false, &v, getter_AddRefs(holder));
|
||||
false, v.address(), getter_AddRefs(holder));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (install) {
|
||||
@ -4230,8 +4233,8 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
JSObject *class_obj;
|
||||
holder->GetJSObject(&class_obj);
|
||||
JS::Rooted<JSObject*> class_obj(cx);
|
||||
holder->GetJSObject(class_obj.address());
|
||||
NS_ASSERTION(class_obj, "The return value lied");
|
||||
|
||||
const nsIID *primary_iid = &NS_GET_IID(nsISupports);
|
||||
@ -4309,20 +4312,20 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
|
||||
}
|
||||
|
||||
{
|
||||
JSObject *winobj = aWin->FastGetGlobalJSObject();
|
||||
JS::Rooted<JSObject*> winobj(cx, aWin->FastGetGlobalJSObject());
|
||||
|
||||
JSObject *proto = nullptr;
|
||||
JS::Rooted<JSObject*> proto(cx);
|
||||
|
||||
if (class_parent_name) {
|
||||
JSAutoCompartment ac(cx, winobj);
|
||||
|
||||
JS::Value val;
|
||||
if (!JS_LookupProperty(cx, winobj, CutPrefix(class_parent_name), &val)) {
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
if (!JS_LookupProperty(cx, winobj, CutPrefix(class_parent_name), val.address())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (val.isObject()) {
|
||||
if (!JS_LookupProperty(cx, &val.toObject(), "prototype", &val)) {
|
||||
if (!JS_LookupProperty(cx, &val.toObject(), "prototype", val.address())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
@ -4334,15 +4337,15 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
|
||||
|
||||
if (dot_prototype) {
|
||||
JSAutoCompartment ac(cx, dot_prototype);
|
||||
JSObject *xpc_proto_proto;
|
||||
if (!::JS_GetPrototype(cx, dot_prototype, &xpc_proto_proto)) {
|
||||
JS::Rooted<JSObject*> xpc_proto_proto(cx);
|
||||
if (!::JS_GetPrototype(cx, dot_prototype, xpc_proto_proto.address())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (proto &&
|
||||
(!xpc_proto_proto ||
|
||||
JS_GetClass(xpc_proto_proto) == sObjectClass)) {
|
||||
if (!JS_WrapObject(cx, &proto) ||
|
||||
if (!JS_WrapObject(cx, proto.address()) ||
|
||||
!JS_SetPrototype(cx, dot_prototype, proto)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
@ -4365,7 +4368,7 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
|
||||
JSAutoCompartment ac(cx, class_obj);
|
||||
|
||||
// Per ECMA, the prototype property is {DontEnum, DontDelete, ReadOnly}
|
||||
if (!JS_WrapValue(cx, &v) ||
|
||||
if (!JS_WrapValue(cx, v.address()) ||
|
||||
!JS_DefineProperty(cx, class_obj, "prototype", v,
|
||||
JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JSPROP_PERMANENT | JSPROP_READONLY)) {
|
||||
@ -4463,7 +4466,7 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
||||
}
|
||||
|
||||
bool enabled;
|
||||
JSObject* interfaceObject = define(cx, global, id, &enabled);
|
||||
JS::Rooted<JSObject*> interfaceObject(cx, define(cx, global, id, &enabled));
|
||||
if (enabled) {
|
||||
if (!interfaceObject) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -4472,7 +4475,7 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
||||
if (defineOnXray) {
|
||||
// This really should be handled by the Xray for the window.
|
||||
ac.destroy();
|
||||
if (!JS_WrapObject(cx, &interfaceObject) ||
|
||||
if (!JS_WrapObject(cx, interfaceObject.address()) ||
|
||||
!JS_DefinePropertyById(cx, obj, id,
|
||||
JS::ObjectValue(*interfaceObject), JS_PropertyStub,
|
||||
JS_StrictPropertyStub, 0)) {
|
||||
@ -4499,16 +4502,16 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
jsval v;
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
|
||||
false, &v, getter_AddRefs(holder));
|
||||
false, v.address(), getter_AddRefs(holder));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = constructor->Install(cx, obj, v);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
JSObject *class_obj;
|
||||
holder->GetJSObject(&class_obj);
|
||||
JS::Rooted<JSObject*> class_obj(cx);
|
||||
holder->GetJSObject(class_obj.address());
|
||||
NS_ASSERTION(class_obj, "The return value lied");
|
||||
|
||||
// ... and define the constants from the DOM interface on that
|
||||
@ -4536,8 +4539,8 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
||||
getter_AddRefs(proto_holder));
|
||||
|
||||
if (NS_SUCCEEDED(rv) && obj != aWin->GetGlobalJSObject()) {
|
||||
JSObject* dot_prototype;
|
||||
rv = proto_holder->GetJSObject(&dot_prototype);
|
||||
JS::Rooted<JSObject*> dot_prototype(cx);
|
||||
rv = proto_holder->GetJSObject(dot_prototype.address());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
const nsDOMClassInfoData *ci_data;
|
||||
@ -4603,10 +4606,10 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
||||
getter_AddRefs(constructor));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
jsval val;
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
|
||||
false, &val, getter_AddRefs(holder));
|
||||
false, val.address(), getter_AddRefs(holder));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = constructor->Install(cx, obj, val);
|
||||
@ -4628,12 +4631,12 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
||||
nsCOMPtr<nsISupports> native(do_CreateInstance(name_struct->mCID, &rv));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
jsval prop_val = JSVAL_VOID; // Property value.
|
||||
JS::Rooted<JS::Value> prop_val(cx, JS::UndefinedValue()); // Property value.
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi(do_QueryInterface(native));
|
||||
if (gpi) {
|
||||
rv = gpi->Init(aWin, &prop_val);
|
||||
rv = gpi->Init(aWin, prop_val.address());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
@ -4649,13 +4652,13 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
||||
scope = aWin->GetGlobalJSObject();
|
||||
}
|
||||
|
||||
rv = WrapNative(cx, scope, native, true, &prop_val,
|
||||
rv = WrapNative(cx, scope, native, true, prop_val.address(),
|
||||
getter_AddRefs(holder));
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!JS_WrapValue(cx, &prop_val)) {
|
||||
if (!JS_WrapValue(cx, prop_val.address())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
@ -4717,7 +4720,7 @@ LocationSetterGuts(JSContext *cx, JSObject *obj, jsval *vp)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Grab the value we're being set to before we stomp on |vp|
|
||||
JSString *val = ::JS_ValueToString(cx, *vp);
|
||||
JS::Rooted<JSString*> val(cx, ::JS_ValueToString(cx, *vp));
|
||||
NS_ENSURE_TRUE(val, NS_ERROR_UNEXPECTED);
|
||||
|
||||
// Make sure |val| stays alive below
|
||||
@ -4821,14 +4824,14 @@ DefineComponentsShim(JSContext *cx, JS::HandleObject global)
|
||||
Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true);
|
||||
|
||||
// Create a fake Components object.
|
||||
JSObject *components = JS_NewObject(cx, nullptr, nullptr, global);
|
||||
JS::Rooted<JSObject*> components(cx, JS_NewObject(cx, nullptr, nullptr, global));
|
||||
NS_ENSURE_TRUE(components, NS_ERROR_OUT_OF_MEMORY);
|
||||
bool ok = JS_DefineProperty(cx, global, "Components", JS::ObjectValue(*components),
|
||||
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// Create a fake interfaces object.
|
||||
JSObject *interfaces = JS_NewObject(cx, nullptr, nullptr, global);
|
||||
JS::Rooted<JSObject*> interfaces(cx, JS_NewObject(cx, nullptr, nullptr, global));
|
||||
NS_ENSURE_TRUE(interfaces, NS_ERROR_OUT_OF_MEMORY);
|
||||
ok = JS_DefineProperty(cx, components, "interfaces", JS::ObjectValue(*interfaces),
|
||||
JS_PropertyStub, JS_StrictPropertyStub,
|
||||
@ -4844,8 +4847,8 @@ DefineComponentsShim(JSContext *cx, JS::HandleObject global)
|
||||
const char *domName = kInterfaceShimMap[i].domName;
|
||||
|
||||
// Look up the appopriate interface object on the global.
|
||||
JS::Value v = JS::UndefinedValue();
|
||||
ok = JS_GetProperty(cx, global, domName, &v);
|
||||
JS::Rooted<JS::Value> v(cx, JS::UndefinedValue());
|
||||
ok = JS_GetProperty(cx, global, domName, v.address());
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
|
||||
if (!v.isObject()) {
|
||||
NS_WARNING("Unable to find interface object on global");
|
||||
@ -4890,7 +4893,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
if (!xpc::WrapperFactory::IsXrayWrapper(obj)) {
|
||||
JSBool did_resolve = JS_FALSE;
|
||||
JSBool ok = JS_TRUE;
|
||||
JS::Value exn = JSVAL_VOID;
|
||||
JS::Rooted<JS::Value> exn(cx, JSVAL_VOID);
|
||||
|
||||
{
|
||||
// Resolve standard classes on my_context's JSContext (or on cx,
|
||||
@ -4909,7 +4912,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
// Trust the JS engine (or the script security manager) to set
|
||||
// the exception in the JS engine.
|
||||
|
||||
if (!JS_GetPendingException(my_cx, &exn)) {
|
||||
if (!JS_GetPendingException(my_cx, exn.address())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
@ -4959,16 +4962,16 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Make sure we wrap the location object in the window's scope.
|
||||
JSObject *scope = nullptr;
|
||||
wrapper->GetJSObject(&scope);
|
||||
JS::Rooted<JSObject*> scope(cx);
|
||||
wrapper->GetJSObject(scope.address());
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
jsval v;
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
rv = WrapNative(cx, scope, location, &NS_GET_IID(nsIDOMLocation), true,
|
||||
&v, getter_AddRefs(holder));
|
||||
v.address(), getter_AddRefs(holder));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
JSBool ok = JS_WrapValue(cx, &v) &&
|
||||
JSBool ok = JS_WrapValue(cx, v.address()) &&
|
||||
JS_DefinePropertyById(cx, obj, id, v, JS_PropertyStub,
|
||||
LocationSetterUnwrapper,
|
||||
JSPROP_PERMANENT | JSPROP_ENUMERATE);
|
||||
@ -4987,10 +4990,10 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
nsresult rv = win->GetScriptableTop(getter_AddRefs(top));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
jsval v;
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
rv = WrapNative(cx, obj, top, &NS_GET_IID(nsIDOMWindow), true,
|
||||
&v, getter_AddRefs(holder));
|
||||
v.address(), getter_AddRefs(holder));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Hold on to the top window object as a global property so we
|
||||
@ -5034,10 +5037,10 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
// compatibility, this should spit out an message on the JS
|
||||
// console.
|
||||
|
||||
JSObject* windowObj = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
|
||||
JS::Rooted<JSObject*> windowObj(cx, js::CheckedUnwrap(obj, /* stopAtOuter = */ false));
|
||||
NS_ENSURE_TRUE(windowObj, NS_ERROR_DOM_SECURITY_ERR);
|
||||
|
||||
JSObject *funObj;
|
||||
JSObject* funObj = nullptr;
|
||||
{
|
||||
JSAutoCompartment ac(cx, windowObj);
|
||||
JSFunction *fun = ::JS_NewFunction(cx, ContentWindowGetter, 0, 0,
|
||||
@ -5084,10 +5087,10 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
rv = win->GetNavigator(getter_AddRefs(navigator));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
jsval v;
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
rv = WrapNative(cx, obj, navigator, &NS_GET_IID(nsIDOMNavigator), true,
|
||||
&v, getter_AddRefs(holder));
|
||||
v.address(), getter_AddRefs(holder));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Hold on to the navigator object as a global property so we
|
||||
@ -5105,10 +5108,10 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
|
||||
if (sDocument_id == id) {
|
||||
nsCOMPtr<nsIDocument> document = win->GetDoc();
|
||||
JS::Value v;
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), document, document,
|
||||
&NS_GET_IID(nsIDOMDocument), &v, getter_AddRefs(holder),
|
||||
&NS_GET_IID(nsIDOMDocument), v.address(), getter_AddRefs(holder),
|
||||
false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@ -5121,7 +5124,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
// Unless our object is a native wrapper, in which case we have to
|
||||
// define it ourselves.
|
||||
|
||||
*_retval = JS_WrapValue(cx, &v) &&
|
||||
*_retval = JS_WrapValue(cx, v.address()) &&
|
||||
JS_DefineProperty(cx, obj, "document", v,
|
||||
JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JSPROP_READONLY | JSPROP_ENUMERATE);
|
||||
@ -5164,21 +5167,21 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
// defined on our prototype chain. This way we can access this
|
||||
// expando w/o ever getting back into XPConnect.
|
||||
if (flags & JSRESOLVE_ASSIGNING) {
|
||||
JSObject *realObj;
|
||||
wrapper->GetJSObject(&realObj);
|
||||
JS::Rooted<JSObject*> realObj(cx);
|
||||
wrapper->GetJSObject(realObj.address());
|
||||
|
||||
if (obj == realObj) {
|
||||
JSObject *proto;
|
||||
if (!js::GetObjectProto(cx, obj, &proto)) {
|
||||
JS::Rooted<JSObject*> proto(cx);
|
||||
if (!js::GetObjectProto(cx, obj, proto.address())) {
|
||||
*_retval = JS_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
if (proto) {
|
||||
JSObject *pobj = NULL;
|
||||
JS::Rooted<JSObject*> pobj(cx);
|
||||
jsval val;
|
||||
|
||||
if (!::JS_LookupPropertyWithFlagsById(cx, proto, id, flags,
|
||||
&pobj, &val)) {
|
||||
pobj.address(), &val)) {
|
||||
*_retval = JS_FALSE;
|
||||
|
||||
return NS_OK;
|
||||
@ -5246,14 +5249,14 @@ nsWindowSH::OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
JSObject *winObj = win->FastGetGlobalJSObject();
|
||||
JS::Rooted<JSObject*> winObj(cx, win->FastGetGlobalJSObject());
|
||||
MOZ_ASSERT(winObj);
|
||||
|
||||
// Note that while |wrapper| is same-compartment with cx, the outer window
|
||||
// might not be. If we're running script in an inactive scope and evalute
|
||||
// |this|, the outer window is actually a cross-compartment wrapper. So we
|
||||
// need to wrap here.
|
||||
if (!JS_WrapObject(cx, &winObj)) {
|
||||
if (!JS_WrapObject(cx, winObj.address())) {
|
||||
*_retval = nullptr;
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
@ -5395,7 +5398,7 @@ nsNavigatorSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
nsCOMPtr<nsISupports> native(do_CreateInstance(name_struct->mCID, &rv));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
jsval prop_val = JSVAL_VOID; // Property value.
|
||||
JS::Rooted<JS::Value> prop_val(cx, JS::UndefinedValue()); // Property value.
|
||||
|
||||
nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi(do_QueryInterface(native));
|
||||
|
||||
@ -5404,19 +5407,19 @@ nsNavigatorSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
nsIDOMWindow *window = static_cast<Navigator*>(navigator.get())->GetWindow();
|
||||
NS_ENSURE_TRUE(window, NS_ERROR_UNEXPECTED);
|
||||
|
||||
rv = gpi->Init(window, &prop_val);
|
||||
rv = gpi->Init(window, prop_val.address());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(prop_val) && !JSVAL_IS_NULL(prop_val)) {
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
rv = WrapNative(cx, obj, native, true, &prop_val,
|
||||
rv = WrapNative(cx, obj, native, true, prop_val.address(),
|
||||
getter_AddRefs(holder));
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (!JS_WrapValue(cx, &prop_val)) {
|
||||
if (!JS_WrapValue(cx, prop_val.address())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
@ -5461,9 +5464,10 @@ nsNavigatorSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
|
||||
// DOM Node helper
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNodeSH::PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj,
|
||||
nsNodeSH::PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *aGlobalObj,
|
||||
JSObject **parentObj)
|
||||
{
|
||||
JS::Rooted<JSObject*> globalObj(cx, aGlobalObj);
|
||||
nsINode *node = static_cast<nsINode*>(nativeObj);
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -5572,9 +5576,10 @@ nsNodeSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNodeSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, jsid id, uint32_t flags,
|
||||
JSObject *aObj, jsid id, uint32_t flags,
|
||||
JSObject **objp, bool *_retval)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(cx, aObj);
|
||||
if (id == sOnload_id || id == sOnerror_id) {
|
||||
// Make sure that this node can't go away while waiting for a
|
||||
// network load that could fire an event handler.
|
||||
@ -5870,8 +5875,8 @@ nsGenericArraySH::GetLength(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
{
|
||||
*length = 0;
|
||||
|
||||
jsval lenval;
|
||||
if (!JS_GetProperty(cx, obj, "length", &lenval)) {
|
||||
JS::Rooted<JS::Value> lenval(cx);
|
||||
if (!JS_GetProperty(cx, obj, "length", lenval.address())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
@ -5908,9 +5913,9 @@ nsGenericArraySH::Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
|
||||
sCurrentlyEnumerating = true;
|
||||
|
||||
jsval len_val;
|
||||
JS::Rooted<JS::Value> len_val(cx);
|
||||
JSAutoRequest ar(cx);
|
||||
JSBool ok = ::JS_GetProperty(cx, obj, "length", &len_val);
|
||||
JSBool ok = ::JS_GetProperty(cx, obj, "length", len_val.address());
|
||||
|
||||
if (ok && JSVAL_IS_INT(len_val)) {
|
||||
int32_t length = JSVAL_TO_INT(len_val);
|
||||
@ -5995,10 +6000,10 @@ nsNamedArraySH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
!ObjectIsNativeWrapper(cx, obj)) {
|
||||
|
||||
{
|
||||
JSObject *realObj;
|
||||
JS::Rooted<JSObject*> realObj(cx);
|
||||
|
||||
if (wrapper) {
|
||||
wrapper->GetJSObject(&realObj);
|
||||
wrapper->GetJSObject(realObj.address());
|
||||
} else {
|
||||
realObj = obj;
|
||||
}
|
||||
@ -6092,11 +6097,11 @@ nsDocumentSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
rv = doc->GetLocation(getter_AddRefs(location));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
jsval v;
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), location,
|
||||
&NS_GET_IID(nsIDOMLocation), true, &v,
|
||||
&NS_GET_IID(nsIDOMLocation), true, v.address(),
|
||||
getter_AddRefs(holder));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@ -6147,11 +6152,11 @@ nsDocumentSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
nsIDocument* currentDoc = win->GetExtantDoc();
|
||||
|
||||
if (SameCOMIdentity(doc, currentDoc)) {
|
||||
jsval winVal;
|
||||
JS::Rooted<JS::Value> winVal(cx);
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
nsresult rv = WrapNative(cx, obj, win, &NS_GET_IID(nsIDOMWindow), false,
|
||||
&winVal, getter_AddRefs(holder));
|
||||
winVal.address(), getter_AddRefs(holder));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_NAMED_LITERAL_STRING(doc_str, "document");
|
||||
@ -6258,11 +6263,11 @@ nsHTMLDocumentSH::GetDocumentAllNodeList(JSContext *cx, JSObject *obj,
|
||||
// in a reserved slot (0) on the document.all JSObject.
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
jsval collection = JS_GetReservedSlot(obj, 0);
|
||||
JS::Rooted<JS::Value> collection(cx, JS_GetReservedSlot(obj, 0));
|
||||
|
||||
if (!JSVAL_IS_PRIMITIVE(collection)) {
|
||||
// We already have a node list in our reserved slot, use it.
|
||||
JSObject *obj = JSVAL_TO_OBJECT(collection);
|
||||
JS::Rooted<JSObject*> obj(cx, JSVAL_TO_OBJECT(collection));
|
||||
nsIHTMLCollection* htmlCollection;
|
||||
rv = mozilla::dom::UnwrapObject<nsIHTMLCollection>(cx, obj, htmlCollection);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
@ -6290,7 +6295,7 @@ nsHTMLDocumentSH::GetDocumentAllNodeList(JSContext *cx, JSObject *obj,
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
nsresult tmp = WrapNative(cx, JS_GetGlobalForScopeChain(cx),
|
||||
static_cast<nsINodeList*>(list), list, false,
|
||||
&collection, getter_AddRefs(holder));
|
||||
collection.address(), getter_AddRefs(holder));
|
||||
if (NS_FAILED(tmp)) {
|
||||
rv = tmp;
|
||||
}
|
||||
@ -6314,7 +6319,7 @@ JSBool
|
||||
nsHTMLDocumentSH::DocumentAllGetProperty(JSContext *cx, JSHandleObject obj_,
|
||||
JSHandleId id, JSMutableHandleValue vp)
|
||||
{
|
||||
JSObject *obj = obj_;
|
||||
JS::Rooted<JSObject*> obj(cx, obj_);
|
||||
|
||||
// document.all.item and .namedItem get their value in the
|
||||
// newResolve hook, so nothing to do for those properties here. And
|
||||
@ -6325,7 +6330,7 @@ nsHTMLDocumentSH::DocumentAllGetProperty(JSContext *cx, JSHandleObject obj_,
|
||||
}
|
||||
|
||||
while (js::GetObjectJSClass(obj) != &sHTMLDocumentAllClass) {
|
||||
if (!js::GetObjectProto(cx, obj, &obj)) {
|
||||
if (!js::GetObjectProto(cx, obj, obj.address())) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
@ -6492,7 +6497,7 @@ nsHTMLDocumentSH::CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp)
|
||||
}
|
||||
|
||||
// Convert all types to string.
|
||||
JSString *str = ::JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
|
||||
JS::Rooted<JSString*> str(cx, ::JS_ValueToString(cx, JS_ARGV(cx, vp)[0]));
|
||||
if (!str) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -6575,8 +6580,8 @@ nsHTMLDocumentSH::DocumentAllHelperNewResolve(JSContext *cx, JSHandleObject obj,
|
||||
{
|
||||
if (nsDOMClassInfo::sAll_id == id) {
|
||||
// document.all is resolved for the first time. Define it.
|
||||
JSObject *helper;
|
||||
if (!GetDocumentAllHelper(cx, obj, &helper)) {
|
||||
JS::Rooted<JSObject*> helper(cx);
|
||||
if (!GetDocumentAllHelper(cx, obj, helper.address())) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
@ -6602,8 +6607,8 @@ nsHTMLDocumentSH::DocumentAllTagsNewResolve(JSContext *cx, JSHandleObject obj,
|
||||
if (JSID_IS_STRING(id)) {
|
||||
nsDocument *doc = GetDocument(obj);
|
||||
|
||||
JSObject *proto;
|
||||
if (!::JS_GetPrototype(cx, obj, &proto)) {
|
||||
JS::Rooted<JSObject*> proto(cx);
|
||||
if (!::JS_GetPrototype(cx, obj, proto.address())) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (MOZ_UNLIKELY(!proto)) {
|
||||
@ -6623,11 +6628,11 @@ nsHTMLDocumentSH::DocumentAllTagsNewResolve(JSContext *cx, JSHandleObject obj,
|
||||
doc->GetElementsByTagName(nsDependentJSString(id));
|
||||
|
||||
if (tags) {
|
||||
jsval v;
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
nsresult rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx),
|
||||
static_cast<nsINodeList*>(tags), tags, true,
|
||||
&v, getter_AddRefs(holder));
|
||||
v.address(), getter_AddRefs(holder));
|
||||
if (NS_FAILED(rv)) {
|
||||
xpc::Throw(cx, rv);
|
||||
|
||||
@ -6676,11 +6681,11 @@ ResolveAll(JSContext* cx, nsIDocument* doc, JSObject* obj)
|
||||
// Our helper's prototype now has an "all" property, remove
|
||||
// the helper out of the prototype chain to prevent
|
||||
// shadowing of the now defined "all" property.
|
||||
JSObject *tmp = obj, *tmpProto = tmp;
|
||||
JS::Rooted<JSObject*> tmp(cx, obj), tmpProto(cx, tmp);
|
||||
|
||||
do {
|
||||
tmp = tmpProto;
|
||||
if (!::JS_GetPrototype(cx, tmp, &tmpProto)) {
|
||||
if (!::JS_GetPrototype(cx, tmp, tmpProto.address())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
} while (tmpProto != helper);
|
||||
@ -6775,9 +6780,10 @@ nsHTMLDocumentSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLDocumentSH::GetProperty(nsIXPConnectWrappedNative *wrapper,
|
||||
JSContext *cx, JSObject *obj, jsid id,
|
||||
JSContext *cx, JSObject *aObj, jsid id,
|
||||
jsval *vp, bool *_retval)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(cx, aObj);
|
||||
nsCOMPtr<nsISupports> result;
|
||||
|
||||
JSAutoRequest ar(cx);
|
||||
@ -6801,10 +6807,11 @@ nsHTMLDocumentSH::GetProperty(nsIXPConnectWrappedNative *wrapper,
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLFormElementSH::NewResolve(nsIXPConnectWrappedNative *wrapper,
|
||||
JSContext *cx, JSObject *obj, jsid id,
|
||||
JSContext *cx, JSObject *aObj, jsid id,
|
||||
uint32_t flags, JSObject **objp,
|
||||
bool *_retval)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(cx, aObj);
|
||||
// For native wrappers, do not resolve random names on form
|
||||
if ((!(JSRESOLVE_ASSIGNING & flags)) && JSID_IS_STRING(id) &&
|
||||
(!ObjectIsNativeWrapper(cx, obj) ||
|
||||
@ -7169,21 +7176,21 @@ nsStorage2SH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSObject *realObj;
|
||||
wrapper->GetJSObject(&realObj);
|
||||
JS::Rooted<JSObject*> realObj(cx);
|
||||
wrapper->GetJSObject(realObj.address());
|
||||
|
||||
JSAutoCompartment ac(cx, realObj);
|
||||
|
||||
// First check to see if the property is defined on our prototype,
|
||||
// after converting id to a string if it's an integer.
|
||||
|
||||
JSString *jsstr = IdToString(cx, id);
|
||||
JS::Rooted<JSString*> jsstr(cx, IdToString(cx, id));
|
||||
if (!jsstr) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSObject *proto;
|
||||
if (!::JS_GetPrototype(cx, realObj, &proto)) {
|
||||
JS::Rooted<JSObject*> proto(cx);
|
||||
if (!::JS_GetPrototype(cx, realObj, proto.address())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
JSBool hasProp;
|
||||
@ -7417,7 +7424,7 @@ nsDOMConstructorSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSObject *nativePropsObj = xpc::XrayUtils::GetNativePropertiesObject(cx, obj);
|
||||
JS::Rooted<JSObject*> nativePropsObj(cx, xpc::XrayUtils::GetNativePropertiesObject(cx, obj));
|
||||
nsDOMConstructor *wrapped =
|
||||
static_cast<nsDOMConstructor *>(wrapper->Native());
|
||||
nsresult rv = wrapped->ResolveInterfaceConstants(cx, nativePropsObj);
|
||||
@ -7441,9 +7448,10 @@ nsDOMConstructorSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMConstructorSH::Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
||||
JSObject *obj, uint32_t argc, jsval *argv, jsval *vp,
|
||||
JSObject *aObj, uint32_t argc, jsval *argv, jsval *vp,
|
||||
bool *_retval)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(cx, aObj);
|
||||
MOZ_ASSERT(obj);
|
||||
|
||||
nsDOMConstructor *wrapped =
|
||||
|
@ -375,8 +375,9 @@ public:
|
||||
JS::MutableHandle<JSObject*> objp);
|
||||
static JSBool GlobalScopePolluterGetProperty(JSContext *cx, JSHandleObject obj,
|
||||
JSHandleId id, JSMutableHandleValue vp);
|
||||
static JSBool InvalidateGlobalScopePolluter(JSContext *cx, JSObject *obj);
|
||||
static nsresult InstallGlobalScopePolluter(JSContext *cx, JSObject *obj);
|
||||
static JSBool InvalidateGlobalScopePolluter(JSContext *cx, JSObject* obj);
|
||||
static nsresult InstallGlobalScopePolluter(JSContext *cx,
|
||||
JS::Handle<JSObject*> obj);
|
||||
static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
|
||||
{
|
||||
return new nsWindowSH(aData);
|
||||
|
@ -2467,7 +2467,8 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
||||
// alive etc.
|
||||
|
||||
if ((!reUseInnerWindow || aDocument != oldDoc) && !aState) {
|
||||
nsWindowSH::InstallGlobalScopePolluter(cx, newInnerWindow->mJSObject);
|
||||
JS::Rooted<JSObject*> obj(cx, newInnerWindow->mJSObject);
|
||||
nsWindowSH::InstallGlobalScopePolluter(cx, obj);
|
||||
}
|
||||
|
||||
aDocument->SetScriptGlobalObject(newInnerWindow);
|
||||
|
@ -5,8 +5,8 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDOMContactManager.idl',
|
||||
'nsIContactProperties.idl',
|
||||
'nsIDOMContactManager.idl',
|
||||
'nsIDOMMozContactChangeEvent.idl',
|
||||
]
|
||||
|
||||
|
@ -25,6 +25,9 @@ XPIDL_SOURCES += [
|
||||
'nsIDOMEvent.idl',
|
||||
'nsIDOMEventListener.idl',
|
||||
'nsIDOMEventTarget.idl',
|
||||
'nsIDOMGamepadAxisMoveEvent.idl',
|
||||
'nsIDOMGamepadButtonEvent.idl',
|
||||
'nsIDOMGamepadEvent.idl',
|
||||
'nsIDOMHashChangeEvent.idl',
|
||||
'nsIDOMKeyEvent.idl',
|
||||
'nsIDOMMessageEvent.idl',
|
||||
@ -50,9 +53,6 @@ XPIDL_SOURCES += [
|
||||
'nsIDOMUIEvent.idl',
|
||||
'nsIDOMUserProximityEvent.idl',
|
||||
'nsIDOMWheelEvent.idl',
|
||||
'nsIDOMGamepadButtonEvent.idl',
|
||||
'nsIDOMGamepadAxisMoveEvent.idl',
|
||||
'nsIDOMGamepadEvent.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'dom_events'
|
||||
|
@ -5,27 +5,27 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS.mozilla.dom.mobilemessage += [
|
||||
'Constants.h',
|
||||
'MobileMessageCallback.h',
|
||||
'MobileMessageService.h',
|
||||
'SmsChild.h',
|
||||
'SmsParent.h',
|
||||
'MobileMessageService.h',
|
||||
'SmsServicesFactory.h',
|
||||
'Constants.h',
|
||||
'Types.h',
|
||||
'MobileMessageCallback.h',
|
||||
]
|
||||
|
||||
if not CONFIG['MOZ_B2G_RIL']:
|
||||
EXPORTS.mozilla.dom.mobilemessage += [
|
||||
'MobileMessageDatabaseService.h',
|
||||
'MmsService.h',
|
||||
'MobileMessageDatabaseService.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'SmsManager.h',
|
||||
'MobileMessageManager.h',
|
||||
'SmsMessage.h',
|
||||
'MmsMessage.h',
|
||||
'SmsSegmentInfo.h',
|
||||
'MobileMessageManager.h',
|
||||
'SmsFilter.h',
|
||||
'SmsManager.h',
|
||||
'SmsMessage.h',
|
||||
'SmsSegmentInfo.h',
|
||||
]
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
MODULE = 'dom'
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'Notification.h',
|
||||
'DesktopNotification.h',
|
||||
'Notification.h',
|
||||
]
|
||||
|
||||
|
@ -26,10 +26,8 @@ interface AudioNode : EventTarget {
|
||||
|
||||
[Throws]
|
||||
void connect(AudioNode destination, optional unsigned long output = 0, optional unsigned long input = 0);
|
||||
|
||||
// [Throws]
|
||||
// void connect(AudioParam destination, optional unsigned long output = 0);
|
||||
|
||||
[Throws]
|
||||
void connect(AudioParam destination, optional unsigned long output = 0);
|
||||
[Throws]
|
||||
void disconnect(optional unsigned long output = 0);
|
||||
|
||||
|
@ -60,8 +60,8 @@ if CONFIG['BUILD_CAIRO_SVG']:
|
||||
|
||||
if CONFIG['MOZ_X11']:
|
||||
EXPORTS.cairo += [
|
||||
'cairo-xlib.h',
|
||||
'cairo-xlib-xrender.h',
|
||||
'cairo-xlib.h',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_ENABLE_CAIRO_FT']:
|
||||
|
@ -4,6 +4,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/layers/Compositor.h"
|
||||
#include "mozilla/layers/Effects.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
@ -15,5 +16,41 @@ Compositor::GetBackend()
|
||||
return sBackend;
|
||||
}
|
||||
|
||||
void
|
||||
Compositor::DrawDiagnostics(const gfx::Color& aColor,
|
||||
const gfx::Rect& rect,
|
||||
const gfx::Rect& aClipRect,
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const gfx::Point& aOffset)
|
||||
{
|
||||
if (!mDrawColoredBorders) {
|
||||
return;
|
||||
}
|
||||
EffectChain effects;
|
||||
effects.mPrimaryEffect = new EffectSolidColor(aColor);
|
||||
int lWidth = 1;
|
||||
float opacity = 0.8;
|
||||
// left
|
||||
this->DrawQuad(gfx::Rect(rect.x, rect.y,
|
||||
lWidth, rect.height),
|
||||
aClipRect, effects, opacity,
|
||||
aTransform, aOffset);
|
||||
// top
|
||||
this->DrawQuad(gfx::Rect(rect.x + lWidth, rect.y,
|
||||
rect.width - 2 * lWidth, lWidth),
|
||||
aClipRect, effects, opacity,
|
||||
aTransform, aOffset);
|
||||
// right
|
||||
this->DrawQuad(gfx::Rect(rect.x + rect.width - lWidth, rect.y,
|
||||
lWidth, rect.height),
|
||||
aClipRect, effects, opacity,
|
||||
aTransform, aOffset);
|
||||
// bottom
|
||||
this->DrawQuad(gfx::Rect(rect.x + lWidth, rect.y + rect.height-lWidth,
|
||||
rect.width - 2 * lWidth, lWidth),
|
||||
aClipRect, effects, opacity,
|
||||
aTransform, aOffset);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
@ -170,6 +170,7 @@ class Compositor : public RefCounted<Compositor>
|
||||
public:
|
||||
Compositor()
|
||||
: mCompositorID(0)
|
||||
, mDrawColoredBorders(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(Compositor);
|
||||
}
|
||||
@ -322,6 +323,22 @@ public:
|
||||
*/
|
||||
virtual bool SupportsPartialTextureUpdate() = 0;
|
||||
|
||||
void EnableColoredBorders()
|
||||
{
|
||||
mDrawColoredBorders = true;
|
||||
}
|
||||
void DisableColoredBorders()
|
||||
{
|
||||
mDrawColoredBorders = false;
|
||||
}
|
||||
|
||||
void DrawDiagnostics(const gfx::Color& color,
|
||||
const gfx::Rect& visibleRect,
|
||||
const gfx::Rect& aClipRect,
|
||||
const gfx::Matrix4x4& transform,
|
||||
const gfx::Point& aOffset);
|
||||
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
virtual const char* Name() const = 0;
|
||||
#endif // MOZ_DUMP_PAINTING
|
||||
@ -380,6 +397,7 @@ public:
|
||||
protected:
|
||||
uint32_t mCompositorID;
|
||||
static LayersBackend sBackend;
|
||||
bool mDrawColoredBorders;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
|
@ -36,6 +36,10 @@ ColorLayerComposite::RenderLayer(const nsIntPoint& aOffset,
|
||||
|
||||
mCompositor->DrawQuad(rect, clipRect, effects, opacity,
|
||||
transform, gfx::Point(aOffset.x, aOffset.y));
|
||||
mCompositor->DrawDiagnostics(gfx::Color(0.0, 1.0, 1.0, 1.0),
|
||||
rect, clipRect,
|
||||
transform, gfx::Point(aOffset.x, aOffset.y));
|
||||
|
||||
}
|
||||
|
||||
} /* layers */
|
||||
|
@ -174,6 +174,9 @@ ContentHostBase::Composite(EffectChain& aEffectChain,
|
||||
Float(tileRegionRect.width) / texRect.width,
|
||||
Float(tileRegionRect.height) / texRect.height);
|
||||
GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform, aOffset);
|
||||
GetCompositor()->DrawDiagnostics(gfx::Color(0.0,1.0,0.0,1.0),
|
||||
rect, aClipRect, aTransform, aOffset);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -103,6 +103,8 @@ ImageHostSingle::Composite(EffectChain& aEffectChain,
|
||||
gfx::Rect rect(tileRect.x, tileRect.y, tileRect.width, tileRect.height);
|
||||
GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain,
|
||||
aOpacity, aTransform, aOffset);
|
||||
GetCompositor()->DrawDiagnostics(gfx::Color(0.5,0.0,0.0,1.0),
|
||||
rect, aClipRect, aTransform, aOffset);
|
||||
} while (it->NextTile());
|
||||
it->EndTileIteration();
|
||||
} else {
|
||||
@ -127,6 +129,8 @@ ImageHostSingle::Composite(EffectChain& aEffectChain,
|
||||
|
||||
GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain,
|
||||
aOpacity, aTransform, aOffset);
|
||||
GetCompositor()->DrawDiagnostics(gfx::Color(1.0,0.1,0.1,1.0),
|
||||
rect, aClipRect, aTransform, aOffset);
|
||||
}
|
||||
|
||||
mTextureHost->Unlock();
|
||||
|
@ -205,6 +205,8 @@ TiledContentHost::RenderTile(const TiledTexture& aTile,
|
||||
textureRect.width / aTextureBounds.width,
|
||||
textureRect.height / aTextureBounds.height);
|
||||
mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, aOpacity, aTransform, aOffset);
|
||||
mCompositor->DrawDiagnostics(gfx::Color(0.0,0.5,0.0,1.0),
|
||||
graphicsRect, aClipRect, aTransform, aOffset);
|
||||
}
|
||||
|
||||
aTile.mTextureHost->Unlock();
|
||||
|
@ -232,6 +232,7 @@ struct OpRemoveChild { PLayer container; PLayer childLayer; };
|
||||
struct OpRepositionChild { PLayer container; PLayer childLayer; PLayer after; };
|
||||
struct OpRaiseToTopChild { PLayer container; PLayer childLayer; };
|
||||
|
||||
struct OpSetColoredBorders { bool enabled; };
|
||||
|
||||
// Paint (buffer update)
|
||||
struct OpPaintTiledLayerBuffer {
|
||||
@ -291,6 +292,7 @@ union Edit {
|
||||
OpCreateRefLayer;
|
||||
|
||||
OpSetLayerAttributes;
|
||||
OpSetColoredBorders;
|
||||
|
||||
OpSetRoot;
|
||||
OpInsertAfter;
|
||||
|
@ -238,7 +238,7 @@ LayerTransactionParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
|
||||
break;
|
||||
}
|
||||
|
||||
// Attributes
|
||||
// Attributes
|
||||
case Edit::TOpSetLayerAttributes: {
|
||||
MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes"));
|
||||
|
||||
@ -329,6 +329,14 @@ LayerTransactionParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Edit::TOpSetColoredBorders: {
|
||||
if (edit.get_OpSetColoredBorders().enabled()) {
|
||||
mLayerManager->GetCompositor()->EnableColoredBorders();
|
||||
} else {
|
||||
mLayerManager->GetCompositor()->DisableColoredBorders();
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Tree ops
|
||||
case Edit::TOpSetRoot: {
|
||||
MOZ_LAYERS_LOG(("[ParentSide] SetRoot"));
|
||||
|
@ -165,6 +165,7 @@ CompositableForwarder::IdentifyTextureHost(const TextureFactoryIdentifier& aIden
|
||||
ShadowLayerForwarder::ShadowLayerForwarder()
|
||||
: mShadowManager(NULL)
|
||||
, mIsFirstPaint(false)
|
||||
, mDrawColoredBorders(false)
|
||||
{
|
||||
mTxn = new Transaction();
|
||||
}
|
||||
@ -360,6 +361,11 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies)
|
||||
NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to");
|
||||
NS_ABORT_IF_FALSE(!mTxn->Finished(), "forgot BeginTransaction?");
|
||||
|
||||
if (mDrawColoredBorders != gfxPlatform::DrawLayerBorders()) {
|
||||
mDrawColoredBorders = gfxPlatform::DrawLayerBorders();
|
||||
mTxn->AddEdit(OpSetColoredBorders(mDrawColoredBorders));
|
||||
}
|
||||
|
||||
AutoTxnEnd _(mTxn);
|
||||
|
||||
if (mTxn->Empty() && !mTxn->RotationChanged()) {
|
||||
|
@ -432,6 +432,7 @@ private:
|
||||
Transaction* mTxn;
|
||||
|
||||
bool mIsFirstPaint;
|
||||
bool mDrawColoredBorders;
|
||||
};
|
||||
|
||||
class CompositableClient;
|
||||
|
@ -12,35 +12,35 @@ EXPORTS += [
|
||||
'BasicLayers.h',
|
||||
'BasicLayersImpl.h',
|
||||
'BasicThebesLayer.h',
|
||||
'CompositableHost.h',
|
||||
'CopyableCanvasLayer.h',
|
||||
'ClientLayerManager.h',
|
||||
'ClientCanvasLayer.h',
|
||||
'ClientContainerLayer.h',
|
||||
'ClientLayerManager.h',
|
||||
'ClientThebesLayer.h',
|
||||
'ClientTiledThebesLayer.h',
|
||||
'ClientCanvasLayer.h',
|
||||
'Composer2D.h',
|
||||
'GonkIOSurfaceImage.h',
|
||||
'FrameMetrics.h',
|
||||
'CompositableHost.h',
|
||||
'CompositorChild.h',
|
||||
'CompositorParent.h',
|
||||
'CopyableCanvasLayer.h',
|
||||
'FrameMetrics.h',
|
||||
'GonkIOSurfaceImage.h',
|
||||
'ImageContainer.h',
|
||||
'ImageLayers.h',
|
||||
'ImageLayerOGL.h',
|
||||
'ImageHost.h',
|
||||
'ImageLayerOGL.h',
|
||||
'ImageLayers.h',
|
||||
'ImageTypes.h',
|
||||
'Layers.h',
|
||||
'LayersLogging.h',
|
||||
'LayerManagerOGLShaders.h',
|
||||
'LayerManagerOGL.h',
|
||||
'LayerManagerOGLProgram.h',
|
||||
'LayerManagerOGLShaders.h',
|
||||
'LayerSorter.h',
|
||||
'LayerTreeInvalidation.h',
|
||||
'Layers.h',
|
||||
'LayersLogging.h',
|
||||
'ReadbackLayer.h',
|
||||
'ShadowLayersManager.h',
|
||||
'SharedTextureImage.h',
|
||||
'TexturePoolOGL.h',
|
||||
'ShmemYCbCrImage.h',
|
||||
'TexturePoolOGL.h',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
@ -71,41 +71,41 @@ EXPORTS.mozilla.layers += [
|
||||
'CanvasLayerComposite.h',
|
||||
'ColorLayerComposite.h',
|
||||
'CompositableClient.h',
|
||||
'CompositableForwarder.h',
|
||||
'CompositableTransactionParent.h',
|
||||
'CompositingRenderTargetOGL.h',
|
||||
'Compositor.h',
|
||||
'CompositorChild.h',
|
||||
'CompositorCocoaWidgetHelper.h',
|
||||
'CompositorOGL.h',
|
||||
'CompositorParent.h',
|
||||
'CompositorTypes.h',
|
||||
'ContainerLayerComposite.h',
|
||||
'ContentHost.h',
|
||||
'CompositingRenderTargetOGL.h',
|
||||
'CompositorCocoaWidgetHelper.h',
|
||||
'CompositorChild.h',
|
||||
'CompositorParent.h',
|
||||
'CompositableForwarder.h',
|
||||
'ContentClient.h',
|
||||
'ContentHost.h',
|
||||
'Effects.h',
|
||||
'GeckoContentController.h',
|
||||
'GestureEventListener.h',
|
||||
'ISurfaceAllocator.h',
|
||||
'ImageBridgeChild.h',
|
||||
'ImageBridgeParent.h',
|
||||
'ImageClient.h',
|
||||
'ImageLayerComposite.h',
|
||||
'ImageHost.h',
|
||||
'ISurfaceAllocator.h',
|
||||
'ImageLayerComposite.h',
|
||||
'LayerManagerComposite.h',
|
||||
'LayersTypes.h',
|
||||
'LayerTransactionChild.h',
|
||||
'LayerTransactionParent.h',
|
||||
'LayersTypes.h',
|
||||
'RenderTrace.h',
|
||||
'ShadowLayers.h',
|
||||
'ShadowLayersManager.h',
|
||||
'RenderTrace.h',
|
||||
'SharedRGBImage.h',
|
||||
'SharedPlanarYCbCrImage.h',
|
||||
'SharedRGBImage.h',
|
||||
'ShmemYCbCrImage.h',
|
||||
'TaskThrottler.h',
|
||||
'TextureClient.h',
|
||||
'TextureHost.h',
|
||||
'TextureClientOGL.h',
|
||||
'TextureHost.h',
|
||||
'TextureHostOGL.h',
|
||||
'ThebesLayerComposite.h',
|
||||
'TiledContentClient.h',
|
||||
|
@ -140,18 +140,18 @@ typedef struct _D3DKMTQS_QUERY_SEGMENT
|
||||
|
||||
typedef struct _D3DKMTQS
|
||||
{
|
||||
__in D3DKMTQS_TYPE Type;
|
||||
__in LUID AdapterLuid;
|
||||
__in_opt HANDLE hProcess;
|
||||
__out D3DKMTQS_RESULT QueryResult;
|
||||
D3DKMTQS_TYPE Type;
|
||||
LUID AdapterLuid;
|
||||
HANDLE hProcess;
|
||||
D3DKMTQS_RESULT QueryResult;
|
||||
|
||||
union
|
||||
{
|
||||
__in D3DKMTQS_QUERY_SEGMENT QuerySegment;
|
||||
__in D3DKMTQS_QUERY_SEGMENT QueryProcessSegment;
|
||||
D3DKMTQS_QUERY_SEGMENT QuerySegment;
|
||||
D3DKMTQS_QUERY_SEGMENT QueryProcessSegment;
|
||||
};
|
||||
} D3DKMTQS;
|
||||
|
||||
extern "C" {
|
||||
typedef __checkReturn NTSTATUS (APIENTRY *PFND3DKMTQS)(__in const D3DKMTQS *);
|
||||
typedef __checkReturn NTSTATUS (APIENTRY *PFND3DKMTQS)(const D3DKMTQS *);
|
||||
}
|
||||
|
@ -96,6 +96,8 @@ static int gCMSIntent = -2;
|
||||
static void ShutdownCMS();
|
||||
static void MigratePrefs();
|
||||
|
||||
static bool sDrawLayerBorders = false;
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
@ -400,6 +402,10 @@ gfxPlatform::Init()
|
||||
|
||||
gPlatform->mOrientationSyncMillis = Preferences::GetUint("layers.orientation.sync.timeout", (uint32_t)0);
|
||||
|
||||
mozilla::Preferences::AddBoolVarCache(&sDrawLayerBorders,
|
||||
"layers.draw-borders",
|
||||
false);
|
||||
|
||||
CreateCMSOutputProfile();
|
||||
}
|
||||
|
||||
@ -1103,6 +1109,13 @@ gfxPlatform::IsLangCJK(eFontPrefLang aLang)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
gfxPlatform::DrawLayerBorders()
|
||||
{
|
||||
return sDrawLayerBorders;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gfxPlatform::GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang)
|
||||
{
|
||||
|
@ -530,6 +530,8 @@ public:
|
||||
|
||||
uint32_t GetOrientationSyncMillis() const;
|
||||
|
||||
static bool DrawLayerBorders();
|
||||
|
||||
protected:
|
||||
gfxPlatform();
|
||||
virtual ~gfxPlatform();
|
||||
|
@ -168,7 +168,7 @@ typedef HRESULT(WINAPI*CreateDXGIFactory1Func)(
|
||||
class GPUAdapterMultiReporter : public nsIMemoryMultiReporter {
|
||||
|
||||
// Callers must Release the DXGIAdapter after use or risk mem-leak
|
||||
static bool GetDXGIAdapter(__out IDXGIAdapter **DXGIAdapter)
|
||||
static bool GetDXGIAdapter(IDXGIAdapter **DXGIAdapter)
|
||||
{
|
||||
ID3D10Device1 *D2D10Device;
|
||||
IDXGIDevice *DXGIDevice;
|
||||
|
@ -80,8 +80,8 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk2':
|
||||
|
||||
if CONFIG['MOZ_X11']:
|
||||
EXPORTS += [
|
||||
'gfxXlibSurface.h',
|
||||
'gfxXlibNativeRenderer.h',
|
||||
'gfxXlibSurface.h',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_PANGO']:
|
||||
|
@ -213,6 +213,7 @@ VPATH += $(srcdir)/ion
|
||||
VPATH += $(srcdir)/ion/shared
|
||||
|
||||
CPPSRCS += MIR.cpp \
|
||||
BytecodeAnalysis.cpp \
|
||||
BaselineCompiler.cpp \
|
||||
BaselineIC.cpp \
|
||||
BaselineFrame.cpp \
|
||||
|
@ -28,6 +28,13 @@ function indirectCallCannotGC(caller, name)
|
||||
if (/CallDestroyScriptHook/.test(caller))
|
||||
return true;
|
||||
|
||||
// template method called during marking and hence cannot GC
|
||||
if (name == "op" &&
|
||||
/^bool js::WeakMap<Key, Value, HashPolicy>::keyNeedsMark\(JSObject\*\)/.test(caller))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ function processCSU(csu, body)
|
||||
if (type.Kind == "CSU") {
|
||||
// Ignore nesting in classes which are AutoGCRooters. We only consider
|
||||
// types with fields that may not be properly rooted.
|
||||
if (type.Name == "JS::AutoGCRooter")
|
||||
if (type.Name == "JS::AutoGCRooter" || type.Name == "JS::CustomAutoRooter")
|
||||
return;
|
||||
addNestedStructure(csu, type.Name);
|
||||
}
|
||||
|
@ -29,6 +29,9 @@ BaselineCompiler::BaselineCompiler(JSContext *cx, HandleScript script)
|
||||
bool
|
||||
BaselineCompiler::init()
|
||||
{
|
||||
if (!analysis_.init())
|
||||
return false;
|
||||
|
||||
if (!labels_.init(script->length))
|
||||
return false;
|
||||
|
||||
@ -64,8 +67,12 @@ BaselineCompiler::compile()
|
||||
IonSpew(IonSpew_BaselineScripts, "Baseline compiling script %s:%d (%p)",
|
||||
script->filename(), script->lineno, script.get());
|
||||
|
||||
if (!script->ensureRanAnalysis(cx))
|
||||
return Method_Error;
|
||||
// Only need to analyze scripts which are marked |argumensHasVarBinding|, to
|
||||
// compute |needsArgsObj| flag.
|
||||
if (script->argumentsHasVarBinding()) {
|
||||
if (!script->ensureRanAnalysis(cx))
|
||||
return Method_Error;
|
||||
}
|
||||
|
||||
// Pin analysis info during compilation.
|
||||
types::AutoEnterAnalysis autoEnterAnalysis(cx);
|
||||
@ -519,7 +526,7 @@ BaselineCompiler::emitSPSPop()
|
||||
Label noPop;
|
||||
masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
|
||||
Imm32(BaselineFrame::HAS_PUSHED_SPS_FRAME), &noPop);
|
||||
masm.spsPopFrame(&cx->runtime->spsProfiler, R1.scratchReg());
|
||||
masm.spsPopFrameSafe(&cx->runtime->spsProfiler, R1.scratchReg());
|
||||
masm.bind(&noPop);
|
||||
}
|
||||
|
||||
@ -537,10 +544,10 @@ BaselineCompiler::emitBody()
|
||||
IonSpew(IonSpew_BaselineOp, "Compiling op @ %d: %s",
|
||||
int(pc - script->code), js_CodeName[op]);
|
||||
|
||||
analyze::Bytecode *code = script->analysis()->maybeCode(pc);
|
||||
BytecodeInfo *info = analysis_.maybeInfo(pc);
|
||||
|
||||
// Skip unreachable ops.
|
||||
if (!code) {
|
||||
if (!info) {
|
||||
if (op == JSOP_STOP)
|
||||
break;
|
||||
pc += GetBytecodeLength(pc);
|
||||
@ -549,9 +556,9 @@ BaselineCompiler::emitBody()
|
||||
}
|
||||
|
||||
// Fully sync the stack if there are incoming jumps.
|
||||
if (code->jumpTarget) {
|
||||
if (info->jumpTarget) {
|
||||
frame.syncStack(0);
|
||||
frame.setStackDepth(code->stackDepth);
|
||||
frame.setStackDepth(info->stackDepth);
|
||||
}
|
||||
|
||||
// Always sync in debug mode.
|
||||
@ -2419,7 +2426,7 @@ BaselineCompiler::emit_JSOP_ARGUMENTS()
|
||||
frame.syncStack(0);
|
||||
|
||||
Label done;
|
||||
if (!script->needsArgsObj()) {
|
||||
if (!script->argumentsHasVarBinding() || !script->needsArgsObj()) {
|
||||
// We assume the script does not need an arguments object. However, this
|
||||
// assumption can be invalidated later, see argumentsOptimizationFailed
|
||||
// in JSScript. Because we can't invalidate baseline JIT code, we set a
|
||||
|
@ -13,9 +13,11 @@
|
||||
#include "jsinfer.h"
|
||||
#include "jsinterp.h"
|
||||
|
||||
#include "IonAllocPolicy.h"
|
||||
#include "BaselineJIT.h"
|
||||
#include "BaselineIC.h"
|
||||
#include "FixedList.h"
|
||||
#include "BytecodeAnalysis.h"
|
||||
|
||||
#if defined(JS_CPU_X86)
|
||||
# include "x86/BaselineCompiler-x86.h"
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "BaselineFrameInfo.h"
|
||||
#include "IonSpewer.h"
|
||||
#include "shared/BaselineCompiler-shared.h"
|
||||
|
||||
#include "jsanalyze.h"
|
||||
#include "jsinferinlines.h"
|
||||
@ -147,8 +148,8 @@ void
|
||||
FrameInfo::assertValidState(jsbytecode *pc)
|
||||
{
|
||||
// Check stack depth.
|
||||
analyze::Bytecode *code = script->analysis()->maybeCode(pc);
|
||||
JS_ASSERT_IF(code, stackDepth() == code->stackDepth);
|
||||
BytecodeInfo *info = compiler.analysis().maybeInfo(pc);
|
||||
JS_ASSERT_IF(info, stackDepth() == info->stackDepth);
|
||||
|
||||
// Start at the bottom, find the first value that's not synced.
|
||||
uint32_t i = 0;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "BaselineJIT.h"
|
||||
#include "BaselineFrame.h"
|
||||
#include "BaselineRegisters.h"
|
||||
#include "BytecodeAnalysis.h"
|
||||
#include "IonMacroAssembler.h"
|
||||
#include "FixedList.h"
|
||||
|
||||
@ -158,8 +159,11 @@ class StackValue
|
||||
|
||||
enum StackAdjustment { AdjustStack, DontAdjustStack };
|
||||
|
||||
class BaselineCompilerShared;
|
||||
|
||||
class FrameInfo
|
||||
{
|
||||
BaselineCompilerShared &compiler;
|
||||
RootedScript script;
|
||||
MacroAssembler &masm;
|
||||
|
||||
@ -167,8 +171,10 @@ class FrameInfo
|
||||
size_t spIndex;
|
||||
|
||||
public:
|
||||
FrameInfo(JSContext *cx, HandleScript script, MacroAssembler &masm)
|
||||
: script(cx, script),
|
||||
FrameInfo(JSContext *cx, BaselineCompilerShared &compiler, HandleScript script,
|
||||
MacroAssembler &masm)
|
||||
: compiler(compiler),
|
||||
script(cx, script),
|
||||
masm(masm),
|
||||
stack(),
|
||||
spIndex(0)
|
||||
|
138
js/src/ion/BytecodeAnalysis.cpp
Normal file
138
js/src/ion/BytecodeAnalysis.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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 "ion/BytecodeAnalysis.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsopcodeinlines.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::ion;
|
||||
|
||||
BytecodeAnalysis::BytecodeAnalysis(JSScript *script)
|
||||
: script_(script),
|
||||
infos_()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeAnalysis::init()
|
||||
{
|
||||
if (!infos_.growByUninitialized(script_->length))
|
||||
return false;
|
||||
|
||||
jsbytecode *end = script_->code + script_->length;
|
||||
|
||||
// Clear all BytecodeInfo.
|
||||
mozilla::PodZero(infos_.begin(), infos_.length());
|
||||
infos_[0].init(/*stackDepth=*/0);
|
||||
|
||||
for (jsbytecode *pc = script_->code; pc < end; pc += GetBytecodeLength(pc)) {
|
||||
JSOp op = JSOp(*pc);
|
||||
unsigned offset = pc - script_->code;
|
||||
|
||||
IonSpew(IonSpew_BaselineOp, "Analyzing op @ %d (end=%d): %s",
|
||||
int(pc - script_->code), int(end - script_->code), js_CodeName[op]);
|
||||
|
||||
// If this bytecode info has not yet been initialized, it's not reachable.
|
||||
if (!infos_[offset].initialized)
|
||||
continue;
|
||||
|
||||
|
||||
unsigned stackDepth = infos_[offset].stackDepth;
|
||||
#ifdef DEBUG
|
||||
for (jsbytecode *chkpc = pc + 1; chkpc < (pc + GetBytecodeLength(pc)); chkpc++)
|
||||
JS_ASSERT(!infos_[chkpc - script_->code].initialized);
|
||||
#endif
|
||||
|
||||
// Treat decompose ops as no-ops which do not adjust the stack. We will
|
||||
// pick up the stack depths as we go through the decomposed version.
|
||||
if (!(js_CodeSpec[op].format & JOF_DECOMPOSE)) {
|
||||
unsigned nuses = GetUseCount(script_, offset);
|
||||
unsigned ndefs = GetDefCount(script_, offset);
|
||||
|
||||
JS_ASSERT(stackDepth >= nuses);
|
||||
stackDepth -= nuses;
|
||||
stackDepth += ndefs;
|
||||
|
||||
// If stack depth exceeds max allowed by analysis, fail fast.
|
||||
JS_ASSERT(stackDepth <= BytecodeInfo::MAX_STACK_DEPTH);
|
||||
}
|
||||
|
||||
if (op == JSOP_TABLESWITCH) {
|
||||
unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
|
||||
jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
|
||||
int32_t low = GET_JUMP_OFFSET(pc2);
|
||||
pc2 += JUMP_OFFSET_LEN;
|
||||
int32_t high = GET_JUMP_OFFSET(pc2);
|
||||
pc2 += JUMP_OFFSET_LEN;
|
||||
|
||||
infos_[defaultOffset].init(stackDepth);
|
||||
infos_[defaultOffset].jumpTarget = true;
|
||||
|
||||
for (int32_t i = low; i <= high; i++) {
|
||||
unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
|
||||
if (targetOffset != offset) {
|
||||
infos_[targetOffset].init(stackDepth);
|
||||
infos_[targetOffset].jumpTarget = true;
|
||||
}
|
||||
pc2 += JUMP_OFFSET_LEN;
|
||||
}
|
||||
} else if (op == JSOP_TRY) {
|
||||
JSTryNote *tn = script_->trynotes()->vector;
|
||||
JSTryNote *tnlimit = tn + script_->trynotes()->length;
|
||||
for (; tn < tnlimit; tn++) {
|
||||
unsigned startOffset = script_->mainOffset + tn->start;
|
||||
if (startOffset == offset + 1) {
|
||||
unsigned catchOffset = startOffset + tn->length;
|
||||
|
||||
if (tn->kind != JSTRY_ITER) {
|
||||
infos_[catchOffset].init(stackDepth);
|
||||
infos_[catchOffset].jumpTarget = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool jump = IsJumpOpcode(op);
|
||||
if (jump) {
|
||||
// Case instructions do not push the lvalue back when branching.
|
||||
unsigned newStackDepth = stackDepth;
|
||||
if (op == JSOP_CASE)
|
||||
newStackDepth--;
|
||||
|
||||
unsigned targetOffset = offset + GET_JUMP_OFFSET(pc);
|
||||
|
||||
// If this is a a backedge to an un-analyzed segment, analyze from there.
|
||||
bool jumpBack = (targetOffset < offset) && !infos_[targetOffset].initialized;
|
||||
|
||||
infos_[targetOffset].init(newStackDepth);
|
||||
infos_[targetOffset].jumpTarget = true;
|
||||
|
||||
if (jumpBack)
|
||||
pc = script_->code + targetOffset;
|
||||
}
|
||||
|
||||
// Handle any fallthrough from this opcode.
|
||||
if (BytecodeFallsThrough(op)) {
|
||||
jsbytecode *nextpc = pc + GetBytecodeLength(pc);
|
||||
JS_ASSERT(nextpc < end);
|
||||
unsigned nextOffset = nextpc - script_->code;
|
||||
|
||||
infos_[nextOffset].init(stackDepth);
|
||||
|
||||
if (jump)
|
||||
infos_[nextOffset].jumpFallthrough = true;
|
||||
|
||||
// Treat the fallthrough of a branch instruction as a jump target.
|
||||
if (jump)
|
||||
infos_[nextOffset].jumpTarget = true;
|
||||
else
|
||||
infos_[nextOffset].fallthrough = true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user