mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b2g-inbound. a=merge
CLOSED TREE
This commit is contained in:
commit
20495ab522
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 965151 needed a CLOBBER
|
||||
Bug 1186748 needed a CLOBBER
|
||||
|
@ -98,9 +98,17 @@ ifdef GKMEDIAS_SHARED_LIBRARY
|
||||
DEFINES += -DGKMEDIAS_SHARED_LIBRARY
|
||||
endif
|
||||
|
||||
DEFINES += -DMOZ_ICU_VERSION=$(MOZ_ICU_VERSION)
|
||||
ifdef MOZ_NATIVE_ICU
|
||||
DEFINES += -DMOZ_NATIVE_ICU
|
||||
endif
|
||||
ifdef MOZ_SHARED_ICU
|
||||
DEFINES += -DMOZ_SHARED_ICU
|
||||
endif
|
||||
ifdef MOZ_JEMALLOC3
|
||||
DEFINES += -DMOZ_JEMALLOC3
|
||||
endif
|
||||
DEFINES += -DMOZ_ICU_DBG_SUFFIX=$(MOZ_ICU_DBG_SUFFIX)
|
||||
|
||||
ifdef MOZ_WIDGET_GTK
|
||||
DEFINES += -DMOZ_GTK=1
|
||||
|
@ -94,6 +94,23 @@
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#ifndef MOZ_NATIVE_ICU
|
||||
#ifdef MOZ_SHARED_ICU
|
||||
#ifdef XP_WIN
|
||||
@BINPATH@/icudt@MOZ_ICU_DBG_SUFFIX@@MOZ_ICU_VERSION@.dll
|
||||
@BINPATH@/icuin@MOZ_ICU_DBG_SUFFIX@@MOZ_ICU_VERSION@.dll
|
||||
@BINPATH@/icuuc@MOZ_ICU_DBG_SUFFIX@@MOZ_ICU_VERSION@.dll
|
||||
#elif defined(XP_MACOSX)
|
||||
@BINPATH@/libicudata.@MOZ_ICU_VERSION@.dylib
|
||||
@BINPATH@/libicui18n.@MOZ_ICU_VERSION@.dylib
|
||||
@BINPATH@/libicuuc.@MOZ_ICU_VERSION@.dylib
|
||||
#elif defined(XP_UNIX)
|
||||
@BINPATH@/libicudata.so.@MOZ_ICU_VERSION@
|
||||
@BINPATH@/libicui18n.so.@MOZ_ICU_VERSION@
|
||||
@BINPATH@/libicuuc.so.@MOZ_ICU_VERSION@
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#ifdef MOZ_SHARED_MOZGLUE
|
||||
@BINPATH@/@DLL_PREFIX@mozglue@DLL_SUFFIX@
|
||||
#endif
|
||||
|
@ -452,6 +452,12 @@ pref("browser.tabs.drawInTitlebar", true);
|
||||
// false return to the adjacent tab (old default)
|
||||
pref("browser.tabs.selectOwnerOnClose", true);
|
||||
|
||||
#ifdef RELEASE_BUILD
|
||||
pref("browser.tabs.showAudioPlayingIcon", false);
|
||||
#else
|
||||
pref("browser.tabs.showAudioPlayingIcon", true);
|
||||
#endif
|
||||
|
||||
pref("browser.ctrlTab.previews", false);
|
||||
|
||||
// By default, do not export HTML at shutdown.
|
||||
|
@ -96,6 +96,10 @@ ContentSearchUIController.prototype = {
|
||||
|
||||
set engines(val) {
|
||||
this._engines = val;
|
||||
if (!this._table.hidden) {
|
||||
this._setUpOneOffButtons();
|
||||
return;
|
||||
}
|
||||
this._pendingOneOffRefresh = true;
|
||||
},
|
||||
|
||||
@ -123,9 +127,6 @@ ContentSearchUIController.prototype = {
|
||||
let allElts = [...this._suggestionsList.children,
|
||||
...this._oneOffButtons,
|
||||
document.getElementById("contentSearchSettingsButton")];
|
||||
// If we are selecting a suggestion and a one-off is selected, don't deselect it.
|
||||
let excludeIndex = idx < this.numSuggestions && this.selectedButtonIndex > -1 ?
|
||||
this.numSuggestions + this.selectedButtonIndex : -1;
|
||||
for (let i = 0; i < allElts.length; ++i) {
|
||||
let elt = allElts[i];
|
||||
let ariaSelectedElt = i < this.numSuggestions ? elt.firstChild : elt;
|
||||
@ -134,43 +135,16 @@ ContentSearchUIController.prototype = {
|
||||
ariaSelectedElt.setAttribute("aria-selected", "true");
|
||||
this.input.setAttribute("aria-activedescendant", ariaSelectedElt.id);
|
||||
}
|
||||
else if (i != excludeIndex) {
|
||||
else {
|
||||
elt.classList.remove("selected");
|
||||
ariaSelectedElt.setAttribute("aria-selected", "false");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get selectedButtonIndex() {
|
||||
let elts = [...this._oneOffButtons,
|
||||
document.getElementById("contentSearchSettingsButton")];
|
||||
for (let i = 0; i < elts.length; ++i) {
|
||||
if (elts[i].classList.contains("selected")) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
set selectedButtonIndex(idx) {
|
||||
let elts = [...this._oneOffButtons,
|
||||
document.getElementById("contentSearchSettingsButton")];
|
||||
for (let i = 0; i < elts.length; ++i) {
|
||||
let elt = elts[i];
|
||||
if (i == idx) {
|
||||
elt.classList.add("selected");
|
||||
elt.setAttribute("aria-selected", "true");
|
||||
}
|
||||
else {
|
||||
elt.classList.remove("selected");
|
||||
elt.setAttribute("aria-selected", "false");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get selectedEngineName() {
|
||||
let selectedElt = this._oneOffsTable.querySelector(".selected");
|
||||
if (selectedElt) {
|
||||
let selectedElt = this._table.querySelector(".selected");
|
||||
if (selectedElt && selectedElt.engineName) {
|
||||
return selectedElt.engineName;
|
||||
}
|
||||
return this.defaultEngine.name;
|
||||
@ -220,7 +194,7 @@ ContentSearchUIController.prototype = {
|
||||
},
|
||||
|
||||
_onCommand: function(aEvent) {
|
||||
if (this.selectedButtonIndex == this._oneOffButtons.length) {
|
||||
if (this.selectedIndex == this.numSuggestions + this._oneOffButtons.length) {
|
||||
// Settings button was selected.
|
||||
this._sendMsg("ManageEngines");
|
||||
return;
|
||||
@ -290,58 +264,19 @@ ContentSearchUIController.prototype = {
|
||||
|
||||
_onKeypress: function (event) {
|
||||
let selectedIndexDelta = 0;
|
||||
let selectedSuggestionDelta = 0;
|
||||
let selectedOneOffDelta = 0;
|
||||
|
||||
switch (event.keyCode) {
|
||||
case event.DOM_VK_UP:
|
||||
if (this._table.hidden) {
|
||||
return;
|
||||
if (!this._table.hidden) {
|
||||
selectedIndexDelta = -1;
|
||||
}
|
||||
if (event.getModifierState("Accel")) {
|
||||
if (event.shiftKey) {
|
||||
selectedSuggestionDelta = -1;
|
||||
break;
|
||||
}
|
||||
this._cycleCurrentEngine(true);
|
||||
break;
|
||||
}
|
||||
if (event.altKey) {
|
||||
selectedOneOffDelta = -1;
|
||||
break;
|
||||
}
|
||||
selectedIndexDelta = -1;
|
||||
break;
|
||||
case event.DOM_VK_DOWN:
|
||||
if (this._table.hidden) {
|
||||
this._getSuggestions();
|
||||
return;
|
||||
}
|
||||
if (event.getModifierState("Accel")) {
|
||||
if (event.shiftKey) {
|
||||
selectedSuggestionDelta = 1;
|
||||
break;
|
||||
}
|
||||
this._cycleCurrentEngine(false);
|
||||
break;
|
||||
else {
|
||||
selectedIndexDelta = 1;
|
||||
}
|
||||
if (event.altKey) {
|
||||
selectedOneOffDelta = 1;
|
||||
break;
|
||||
}
|
||||
selectedIndexDelta = 1;
|
||||
break;
|
||||
case event.DOM_VK_TAB:
|
||||
if (this._table.hidden) {
|
||||
return;
|
||||
}
|
||||
// Shift+tab when either the first or no one-off is selected, as well as
|
||||
// tab when the settings button is selected, should change focus as normal.
|
||||
if ((this.selectedButtonIndex <= 0 && event.shiftKey) ||
|
||||
this.selectedButtonIndex == this._oneOffButtons.length && !event.shiftKey) {
|
||||
return;
|
||||
}
|
||||
selectedOneOffDelta = event.shiftKey ? -1 : 1;
|
||||
break;
|
||||
case event.DOM_VK_RIGHT:
|
||||
// Allow normal caret movement until the caret is at the end of the input.
|
||||
@ -362,97 +297,37 @@ ContentSearchUIController.prototype = {
|
||||
}
|
||||
this._stickyInputValue = this.input.value;
|
||||
this._hideSuggestions();
|
||||
return;
|
||||
break;
|
||||
case event.DOM_VK_RETURN:
|
||||
this._onCommand(event);
|
||||
return;
|
||||
break;
|
||||
case event.DOM_VK_DELETE:
|
||||
if (this.selectedIndex >= 0) {
|
||||
this.deleteSuggestionAtIndex(this.selectedIndex);
|
||||
}
|
||||
return;
|
||||
break;
|
||||
case event.DOM_VK_ESCAPE:
|
||||
if (!this._table.hidden) {
|
||||
this._hideSuggestions();
|
||||
}
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
let currentIndex = this.selectedIndex;
|
||||
if (selectedIndexDelta) {
|
||||
let newSelectedIndex = currentIndex + selectedIndexDelta;
|
||||
// Update the selection.
|
||||
let newSelectedIndex = this.selectedIndex + selectedIndexDelta;
|
||||
if (newSelectedIndex < -1) {
|
||||
newSelectedIndex = this.numSuggestions + this._oneOffButtons.length;
|
||||
}
|
||||
// If are moving up from the first one off, we have to deselect the one off
|
||||
// manually because the selectedIndex setter tries to exclude the selected
|
||||
// one-off (which is desirable for accel+shift+up/down).
|
||||
if (currentIndex == this.numSuggestions && selectedIndexDelta == -1) {
|
||||
this.selectedButtonIndex = -1;
|
||||
}
|
||||
this.selectAndUpdateInput(newSelectedIndex);
|
||||
}
|
||||
|
||||
else if (selectedSuggestionDelta) {
|
||||
let newSelectedIndex;
|
||||
if (currentIndex >= this.numSuggestions || currentIndex == -1) {
|
||||
// No suggestion already selected, select the first/last one appropriately.
|
||||
newSelectedIndex = selectedSuggestionDelta == 1 ?
|
||||
0 : this.numSuggestions - 1;
|
||||
}
|
||||
else {
|
||||
newSelectedIndex = currentIndex + selectedSuggestionDelta;
|
||||
}
|
||||
if (newSelectedIndex >= this.numSuggestions) {
|
||||
else if (this.numSuggestions + this._oneOffButtons.length < newSelectedIndex) {
|
||||
newSelectedIndex = -1;
|
||||
}
|
||||
this.selectAndUpdateInput(newSelectedIndex);
|
||||
}
|
||||
|
||||
else if (selectedOneOffDelta) {
|
||||
let newSelectedIndex;
|
||||
let currentButton = this.selectedButtonIndex;
|
||||
if (currentButton == -1 || currentButton == this._oneOffButtons.length) {
|
||||
// No one-off already selected, select the first/last one appropriately.
|
||||
newSelectedIndex = selectedOneOffDelta == 1 ?
|
||||
0 : this._oneOffButtons.length - 1;
|
||||
}
|
||||
else {
|
||||
newSelectedIndex = currentButton + selectedOneOffDelta;
|
||||
}
|
||||
// Allow selection of the settings button via the tab key.
|
||||
if (newSelectedIndex == this._oneOffButtons.length &&
|
||||
event.keyCode != event.DOM_VK_TAB) {
|
||||
newSelectedIndex = -1;
|
||||
}
|
||||
this.selectedButtonIndex = newSelectedIndex;
|
||||
// Prevent the input's caret from moving.
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// Prevent the input's caret from moving.
|
||||
event.preventDefault();
|
||||
},
|
||||
|
||||
_currentEngineIndex: -1,
|
||||
_cycleCurrentEngine: function (aReverse) {
|
||||
if ((this._currentEngineIndex == this._oneOffButtons.length - 1 && !aReverse) ||
|
||||
(this._currentEngineIndex < 0 && aReverse)) {
|
||||
return;
|
||||
}
|
||||
this._currentEngineIndex += aReverse ? -1 : 1;
|
||||
let engine;
|
||||
if (this._currentEngineIndex == -1) {
|
||||
engine = this._originalDefaultEngine;
|
||||
} else {
|
||||
let button = this._oneOffButtons[this._currentEngineIndex];
|
||||
engine = {
|
||||
name: button.engineName,
|
||||
icon: button.firstChild.getAttribute("src"),
|
||||
};
|
||||
}
|
||||
this._sendMsg("SetCurrentEngine", engine.name);
|
||||
this.defaultEngine = engine;
|
||||
},
|
||||
|
||||
_onFocus: function () {
|
||||
@ -481,12 +356,7 @@ ContentSearchUIController.prototype = {
|
||||
},
|
||||
|
||||
_onMousemove: function (event) {
|
||||
let idx = this._indexOfTableItem(event.target);
|
||||
if (idx >= this.numSuggestions) {
|
||||
this.selectedButtonIndex = idx - this.numSuggestions;
|
||||
return;
|
||||
}
|
||||
this.selectedIndex = idx;
|
||||
this.selectedIndex = this._indexOfTableItem(event.target);
|
||||
},
|
||||
|
||||
_onMouseup: function (event) {
|
||||
@ -496,15 +366,6 @@ ContentSearchUIController.prototype = {
|
||||
this._onCommand(event);
|
||||
},
|
||||
|
||||
_onMouseout: function (event) {
|
||||
// We only deselect one-off buttons and the settings button when they are
|
||||
// moused out.
|
||||
let idx = this._indexOfTableItem(event.originalTarget);
|
||||
if (idx >= this.numSuggestions) {
|
||||
this.selectedButtonIndex = -1;
|
||||
}
|
||||
},
|
||||
|
||||
_onClick: function (event) {
|
||||
this._onMouseup(event);
|
||||
},
|
||||
@ -566,10 +427,6 @@ ContentSearchUIController.prototype = {
|
||||
}
|
||||
this._table.hidden = false;
|
||||
this.input.setAttribute("aria-expanded", "true");
|
||||
this._originalDefaultEngine = {
|
||||
name: this.defaultEngine.name,
|
||||
icon: this.defaultEngine.icon,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
@ -590,6 +447,10 @@ ContentSearchUIController.prototype = {
|
||||
name: engine.name,
|
||||
icon: this._getFaviconURIFromBuffer(engine.iconBuffer),
|
||||
};
|
||||
if (!this._table.hidden) {
|
||||
this._setUpOneOffButtons();
|
||||
return;
|
||||
}
|
||||
this._pendingOneOffRefresh = true;
|
||||
},
|
||||
|
||||
@ -711,9 +572,6 @@ ContentSearchUIController.prototype = {
|
||||
|
||||
_hideSuggestions: function () {
|
||||
this.input.setAttribute("aria-expanded", "false");
|
||||
this.selectedIndex = -1;
|
||||
this.selectedButtonIndex = -1;
|
||||
this._currentEngineIndex = -1;
|
||||
this._table.hidden = true;
|
||||
},
|
||||
|
||||
@ -747,7 +605,11 @@ ContentSearchUIController.prototype = {
|
||||
document.addEventListener("mouseup", () => { delete this._mousedown; });
|
||||
|
||||
// Deselect the selected element on mouseout if it wasn't a suggestion.
|
||||
this._table.addEventListener("mouseout", this);
|
||||
this._table.addEventListener("mouseout", () => {
|
||||
if (this.selectedIndex >= this.numSuggestions) {
|
||||
this.selectAndUpdateInput(-1);
|
||||
}
|
||||
});
|
||||
|
||||
// If a search is loaded in the same tab, ensure the suggestions dropdown
|
||||
// is hidden immediately when the page starts loading and not when it first
|
||||
|
@ -4,11 +4,6 @@
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE bindings [
|
||||
<!ENTITY % tabBrowserDTD SYSTEM "chrome://browser/locale/tabbrowser.dtd" >
|
||||
%tabBrowserDTD;
|
||||
]>
|
||||
|
||||
<bindings id="tabBrowserBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
@ -3871,11 +3866,21 @@
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
event.target.setAttribute("label",
|
||||
tab.mOverCloseButton ?
|
||||
tab.getAttribute("closetabtext") :
|
||||
tab.getAttribute("label") +
|
||||
(this.AppConstants.E10S_TESTING_ONLY && tab.linkedBrowser && tab.linkedBrowser.isRemoteBrowser ? " - e10s" : ""));
|
||||
var stringID, label;
|
||||
if (tab.mOverCloseButton) {
|
||||
stringID = "tabs.closeTab.tooltip";
|
||||
} else if (tab._overPlayingIcon) {
|
||||
stringID = tab.linkedBrowser.audioMuted ?
|
||||
"tabs.mutedAudio.tooltip" :
|
||||
"tabs.playingAudio.tooltip";
|
||||
} else {
|
||||
label = tab.getAttribute("label") +
|
||||
(this.AppConstants.E10S_TESTING_ONLY && tab.linkedBrowser && tab.linkedBrowser.isRemoteBrowser ? " - e10s" : "");
|
||||
}
|
||||
if (stringID && !label) {
|
||||
label = this.mStringBundle.getString(stringID);
|
||||
}
|
||||
event.target.setAttribute("label", label);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
@ -4237,6 +4242,40 @@
|
||||
this.setIcon(tab, icon);
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="DOMMediaPlaybackStarted">
|
||||
<![CDATA[
|
||||
if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") ||
|
||||
!event.isTrusted)
|
||||
return;
|
||||
|
||||
var browser = event.originalTarget;
|
||||
var tab = this.getTabForBrowser(browser);
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
if (!tab.hasAttribute("soundplaying")) {
|
||||
tab.setAttribute("soundplaying", true);
|
||||
this._tabAttrModified(tab, ["soundplaying"]);
|
||||
}
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="DOMMediaPlaybackStopped">
|
||||
<![CDATA[
|
||||
if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") ||
|
||||
!event.isTrusted)
|
||||
return;
|
||||
|
||||
var browser = event.originalTarget;
|
||||
var tab = this.getTabForBrowser(browser);
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
if (tab.hasAttribute("soundplaying")) {
|
||||
tab.removeAttribute("soundplaying");
|
||||
this._tabAttrModified(tab, ["soundplaying"]);
|
||||
}
|
||||
]]>
|
||||
</handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
@ -5629,7 +5668,7 @@
|
||||
<stylesheet src="chrome://browser/content/tabbrowser.css"/>
|
||||
</resources>
|
||||
|
||||
<content context="tabContextMenu" closetabtext="&closeTab.label;">
|
||||
<content context="tabContextMenu">
|
||||
<xul:stack class="tab-stack" flex="1">
|
||||
<xul:hbox xbl:inherits="pinned,selected,visuallyselected,titlechanged,fadein"
|
||||
class="tab-background">
|
||||
@ -5651,7 +5690,8 @@
|
||||
class="tab-icon-image"
|
||||
validate="never"
|
||||
role="presentation"/>
|
||||
<xul:image xbl:inherits="crashed,busy"
|
||||
<xul:image xbl:inherits="crashed,busy,soundplaying,pinned,muted"
|
||||
anonid="overlay-icon"
|
||||
class="tab-icon-overlay"
|
||||
role="presentation"/>
|
||||
<xul:label flex="1"
|
||||
@ -5659,6 +5699,10 @@
|
||||
xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected,visuallyselected"
|
||||
class="tab-text tab-label"
|
||||
role="presentation"/>
|
||||
<xul:image xbl:inherits="soundplaying,pinned,muted"
|
||||
anonid="soundplaying-icon"
|
||||
class="tab-icon-sound"
|
||||
role="presentation"/>
|
||||
<xul:toolbarbutton anonid="close-button"
|
||||
xbl:inherits="fadein,pinned,selected,visuallyselected"
|
||||
class="tab-close-button close-icon"/>
|
||||
@ -5771,6 +5815,7 @@
|
||||
</property>
|
||||
|
||||
<field name="mOverCloseButton">false</field>
|
||||
<field name="_overPlayingIcon">false</field>
|
||||
<field name="mCorrespondingMenuitem">null</field>
|
||||
|
||||
<!--
|
||||
@ -5827,6 +5872,23 @@
|
||||
tabContainer._hoveredTab = null;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_toggleMuteAudio">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let tabContainer = this.parentNode;
|
||||
let browser = this.linkedBrowser;
|
||||
if (browser.audioMuted) {
|
||||
browser.unmute();
|
||||
this.removeAttribute("muted");
|
||||
} else {
|
||||
browser.mute();
|
||||
this.setAttribute("muted", "true");
|
||||
}
|
||||
tabContainer.tabbrowser._tabAttrModified(this, ["muted"]);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
@ -5834,6 +5896,9 @@
|
||||
let anonid = event.originalTarget.getAttribute("anonid");
|
||||
if (anonid == "close-button")
|
||||
this.mOverCloseButton = true;
|
||||
else if ((anonid == "soundplaying-icon") ||
|
||||
((anonid == "overlay-icon") && this.hasAttribute("soundplaying")))
|
||||
this._overPlayingIcon = true;
|
||||
|
||||
this._mouseenter();
|
||||
]]></handler>
|
||||
@ -5841,6 +5906,9 @@
|
||||
let anonid = event.originalTarget.getAttribute("anonid");
|
||||
if (anonid == "close-button")
|
||||
this.mOverCloseButton = false;
|
||||
else if ((anonid == "soundplaying-icon") ||
|
||||
((anonid == "overlay-icon") && this.hasAttribute("soundplaying")))
|
||||
this._overPlayingIcon = false;
|
||||
|
||||
this._mouseleave();
|
||||
]]></handler>
|
||||
@ -5852,7 +5920,8 @@
|
||||
if (this.selected) {
|
||||
this.style.MozUserFocus = 'ignore';
|
||||
this.clientTop; // just using this to flush style updates
|
||||
} else if (this.mOverCloseButton) {
|
||||
} else if (this.mOverCloseButton ||
|
||||
this._overPlayingIcon) {
|
||||
// Prevent tabbox.xml from selecting the tab.
|
||||
event.stopPropagation();
|
||||
}
|
||||
@ -5861,6 +5930,19 @@
|
||||
<handler event="mouseup">
|
||||
this.style.MozUserFocus = '';
|
||||
</handler>
|
||||
<handler event="click">
|
||||
<![CDATA[
|
||||
if (event.button != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let anonid = event.originalTarget.getAttribute("anonid");
|
||||
if ((anonid == "soundplaying-icon") ||
|
||||
((anonid == "overlay-icon") && this.hasAttribute("soundplaying"))) {
|
||||
this._toggleMuteAudio();
|
||||
}
|
||||
]]>
|
||||
</handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
|
@ -7,6 +7,7 @@ support-files =
|
||||
app_subframe_bug575561.html
|
||||
authenticate.sjs
|
||||
aboutHome_content_script.js
|
||||
audio.ogg
|
||||
browser_bug479408_sample.html
|
||||
browser_bug678392-1.html
|
||||
browser_bug678392-2.html
|
||||
@ -50,6 +51,7 @@ support-files =
|
||||
file_bug906190_redirected.html
|
||||
file_bug906190.js
|
||||
file_bug906190.sjs
|
||||
file_mediaPlayback.html
|
||||
file_mixedContentFromOnunload.html
|
||||
file_mixedContentFromOnunload_test1.html
|
||||
file_mixedContentFromOnunload_test2.html
|
||||
@ -131,6 +133,7 @@ skip-if = e10s # Bug 1093153 - no about:home support yet
|
||||
[browser_addKeywordSearch.js]
|
||||
[browser_search_favicon.js]
|
||||
[browser_alltabslistener.js]
|
||||
[browser_audioTabIcon.js]
|
||||
[browser_autocomplete_a11y_label.js]
|
||||
skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir (works on its own)
|
||||
[browser_autocomplete_cursor.js]
|
||||
@ -155,7 +158,6 @@ skip-if = true # browser_bug321000.js is disabled because newline handling is sh
|
||||
[browser_bug356571.js]
|
||||
[browser_bug380960.js]
|
||||
[browser_bug386835.js]
|
||||
[browser_bug405137.js]
|
||||
[browser_bug406216.js]
|
||||
[browser_bug409481.js]
|
||||
[browser_bug409624.js]
|
||||
|
133
browser/base/content/test/general/browser_audioTabIcon.js
Normal file
133
browser/base/content/test/general/browser_audioTabIcon.js
Normal file
@ -0,0 +1,133 @@
|
||||
const PAGE = "https://example.com/browser/browser/base/content/test/general/file_mediaPlayback.html";
|
||||
|
||||
function* wait_for_tab_playing_event(tab, expectPlaying) {
|
||||
yield BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
|
||||
if (event.detail.changed.indexOf("soundplaying") >= 0) {
|
||||
is(tab.hasAttribute("soundplaying"), expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function disable_non_test_mouse(disable) {
|
||||
let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
utils.disableNonTestMouseEvents(disable);
|
||||
}
|
||||
|
||||
function* hover_icon(icon, tooltip) {
|
||||
disable_non_test_mouse(true);
|
||||
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(tooltip, "popupshown");
|
||||
EventUtils.synthesizeMouse(icon, 1, 1, {type: "mouseover"});
|
||||
EventUtils.synthesizeMouse(icon, 2, 2, {type: "mousemove"});
|
||||
EventUtils.synthesizeMouse(icon, 3, 3, {type: "mousemove"});
|
||||
EventUtils.synthesizeMouse(icon, 4, 4, {type: "mousemove"});
|
||||
return popupShownPromise;
|
||||
}
|
||||
|
||||
function leave_icon(icon) {
|
||||
EventUtils.synthesizeMouse(icon, 0, 0, {type: "mouseout"});
|
||||
EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"});
|
||||
EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"});
|
||||
EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"});
|
||||
|
||||
disable_non_test_mouse(false);
|
||||
}
|
||||
|
||||
function* test_tooltip(icon, expectedTooltip) {
|
||||
let tooltip = document.getElementById("tabbrowser-tab-tooltip");
|
||||
|
||||
yield hover_icon(icon, tooltip);
|
||||
is(tooltip.getAttribute("label"), expectedTooltip, "Correct tooltip expected");
|
||||
leave_icon(icon);
|
||||
}
|
||||
|
||||
function* test_mute_tab(tab, icon, expectMuted) {
|
||||
let mutedPromise = BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
|
||||
if (event.detail.changed.indexOf("muted") >= 0) {
|
||||
is(tab.hasAttribute("muted"), expectMuted, "The tab should " + (expectMuted ? "" : "not ") + "be muted");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
let activeTab = gBrowser.selectedTab;
|
||||
|
||||
let tooltip = document.getElementById("tabbrowser-tab-tooltip");
|
||||
|
||||
yield hover_icon(icon, tooltip);
|
||||
EventUtils.synthesizeMouseAtCenter(icon, {button: 0});
|
||||
leave_icon(icon);
|
||||
|
||||
is(gBrowser.selectedTab, activeTab, "Clicking on mute should not change the currently selected tab");
|
||||
|
||||
return mutedPromise;
|
||||
}
|
||||
|
||||
function* test_playing_icon_on_tab(tab, browser, isPinned) {
|
||||
let icon = document.getAnonymousElementByAttribute(tab, "anonid",
|
||||
isPinned ? "overlay-icon" : "soundplaying-icon");
|
||||
|
||||
yield ContentTask.spawn(browser, {}, function* () {
|
||||
let audio = content.document.querySelector("audio");
|
||||
audio.play();
|
||||
});
|
||||
|
||||
yield wait_for_tab_playing_event(tab, true);
|
||||
|
||||
yield test_tooltip(icon, "This tab is playing audio");
|
||||
|
||||
yield test_mute_tab(tab, icon, true);
|
||||
|
||||
yield test_tooltip(icon, "This tab has been muted");
|
||||
|
||||
yield test_mute_tab(tab, icon, false);
|
||||
|
||||
yield test_tooltip(icon, "This tab is playing audio");
|
||||
|
||||
yield ContentTask.spawn(browser, {}, function* () {
|
||||
let audio = content.document.querySelector("audio");
|
||||
audio.pause();
|
||||
});
|
||||
yield wait_for_tab_playing_event(tab, false);
|
||||
}
|
||||
|
||||
function* test_on_browser(browser) {
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
|
||||
// Test the icon in a normal tab.
|
||||
yield test_playing_icon_on_tab(tab, browser, false);
|
||||
|
||||
gBrowser.pinTab(tab);
|
||||
|
||||
// Test the icon in a pinned tab.
|
||||
yield test_playing_icon_on_tab(tab, browser, true);
|
||||
|
||||
gBrowser.unpinTab(tab);
|
||||
|
||||
// Retest with another browser in the foreground tab
|
||||
if (gBrowser.selectedBrowser.currentURI.spec == PAGE) {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "data:text/html,test"
|
||||
}, () => test_on_browser(browser));
|
||||
}
|
||||
}
|
||||
|
||||
add_task(function*() {
|
||||
yield new Promise((resolve) => {
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["media.useAudioChannelService", true],
|
||||
["browser.tabs.showAudioPlayingIcon", true],
|
||||
]}, resolve);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_page() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: PAGE
|
||||
}, test_on_browser);
|
||||
});
|
@ -1,5 +0,0 @@
|
||||
function test(){
|
||||
var tab = gBrowser.addTab();
|
||||
ok(tab.getAttribute("closetabtext") != "", "tab has non-empty closetabtext");
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
@ -102,7 +102,7 @@ add_task(function* rightLeftKeys() {
|
||||
// trigger suggestions again and cycle through them by pressing Down until
|
||||
// nothing is selected again.
|
||||
state = yield msg("key", "VK_RIGHT");
|
||||
checkState(state, "xfoo", [], -1);
|
||||
checkState(state, "xfoo", [], 0);
|
||||
|
||||
state = yield msg("key", { key: "VK_DOWN", waitForSuggestions: true });
|
||||
checkState(state, "xfoo", ["xfoofoo", "xfoobar"], -1);
|
||||
@ -125,202 +125,20 @@ add_task(function* rightLeftKeys() {
|
||||
yield msg("reset");
|
||||
});
|
||||
|
||||
add_task(function* tabKey() {
|
||||
yield setUp();
|
||||
yield msg("key", { key: "x", waitForSuggestions: true });
|
||||
|
||||
let state = yield msg("key", "VK_TAB");
|
||||
checkState(state, "x", ["xfoo", "xbar"], 2);
|
||||
|
||||
state = yield msg("key", "VK_TAB");
|
||||
checkState(state, "x", ["xfoo", "xbar"], 3);
|
||||
|
||||
state = yield msg("key", { key: "VK_TAB", modifiers: { shiftKey: true }});
|
||||
checkState(state, "x", ["xfoo", "xbar"], 2);
|
||||
|
||||
state = yield msg("key", { key: "VK_TAB", modifiers: { shiftKey: true }});
|
||||
checkState(state, "x", [], -1);
|
||||
|
||||
yield setUp();
|
||||
|
||||
yield msg("key", { key: "VK_DOWN", waitForSuggestions: true });
|
||||
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
state = yield msg("key", "VK_TAB");
|
||||
}
|
||||
checkState(state, "x", [], -1);
|
||||
|
||||
yield setUp();
|
||||
|
||||
yield msg("key", { key: "VK_DOWN", waitForSuggestions: true });
|
||||
state = yield msg("key", "VK_DOWN");
|
||||
checkState(state, "xfoo", ["xfoo", "xbar"], 0);
|
||||
|
||||
state = yield msg("key", "VK_TAB");
|
||||
checkState(state, "xfoo", ["xfoo", "xbar"], 0, 0);
|
||||
|
||||
state = yield msg("key", "VK_TAB");
|
||||
checkState(state, "xfoo", ["xfoo", "xbar"], 0, 1);
|
||||
|
||||
state = yield msg("key", "VK_DOWN");
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
|
||||
|
||||
state = yield msg("key", "VK_DOWN");
|
||||
checkState(state, "x", ["xfoo", "xbar"], 2);
|
||||
|
||||
state = yield msg("key", "VK_UP");
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1);
|
||||
|
||||
state = yield msg("key", "VK_TAB");
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1, 0);
|
||||
|
||||
state = yield msg("key", "VK_TAB");
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
|
||||
|
||||
state = yield msg("key", "VK_TAB");
|
||||
checkState(state, "xbar", [], -1);
|
||||
|
||||
yield msg("reset");
|
||||
});
|
||||
|
||||
add_task(function* cycleSuggestions() {
|
||||
yield setUp();
|
||||
yield msg("key", { key: "x", waitForSuggestions: true });
|
||||
|
||||
let cycle = Task.async(function* (aSelectedButtonIndex) {
|
||||
let modifiers = {
|
||||
shiftKey: true,
|
||||
accelKey: true,
|
||||
};
|
||||
|
||||
let state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
|
||||
checkState(state, "xfoo", ["xfoo", "xbar"], 0, aSelectedButtonIndex);
|
||||
|
||||
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1, aSelectedButtonIndex);
|
||||
|
||||
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
|
||||
checkState(state, "x", ["xfoo", "xbar"], -1, aSelectedButtonIndex);
|
||||
|
||||
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
|
||||
checkState(state, "xfoo", ["xfoo", "xbar"], 0, aSelectedButtonIndex);
|
||||
|
||||
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
|
||||
checkState(state, "x", ["xfoo", "xbar"], -1, aSelectedButtonIndex);
|
||||
|
||||
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1, aSelectedButtonIndex);
|
||||
|
||||
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
|
||||
checkState(state, "xfoo", ["xfoo", "xbar"], 0, aSelectedButtonIndex);
|
||||
|
||||
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
|
||||
checkState(state, "x", ["xfoo", "xbar"], -1, aSelectedButtonIndex);
|
||||
});
|
||||
|
||||
yield cycle();
|
||||
|
||||
// Repeat with a one-off selected.
|
||||
let state = yield msg("key", "VK_TAB");
|
||||
checkState(state, "x", ["xfoo", "xbar"], 2);
|
||||
yield cycle(0);
|
||||
|
||||
// Repeat with the settings button selected.
|
||||
state = yield msg("key", "VK_TAB");
|
||||
checkState(state, "x", ["xfoo", "xbar"], 3);
|
||||
yield cycle(1);
|
||||
|
||||
yield msg("reset");
|
||||
});
|
||||
|
||||
add_task(function* cycleOneOffs() {
|
||||
yield setUp();
|
||||
yield msg("key", { key: "x", waitForSuggestions: true });
|
||||
|
||||
yield msg("addDuplicateOneOff");
|
||||
|
||||
let state = yield msg("key", "VK_DOWN");
|
||||
state = yield msg("key", "VK_DOWN");
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1);
|
||||
|
||||
let modifiers = {
|
||||
altKey: true,
|
||||
};
|
||||
|
||||
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1, 0);
|
||||
|
||||
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
|
||||
|
||||
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1);
|
||||
|
||||
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
|
||||
|
||||
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1, 0);
|
||||
|
||||
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1);
|
||||
|
||||
// If the settings button is selected, pressing alt+up/down should select the
|
||||
// last/first one-off respectively (and deselect the settings button).
|
||||
yield msg("key", "VK_TAB");
|
||||
yield msg("key", "VK_TAB");
|
||||
state = yield msg("key", "VK_TAB"); // Settings button selected.
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1, 2);
|
||||
|
||||
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
|
||||
|
||||
state = yield msg("key", "VK_TAB");
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1, 2);
|
||||
|
||||
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
|
||||
checkState(state, "xbar", ["xfoo", "xbar"], 1, 0);
|
||||
|
||||
yield msg("removeLastOneOff");
|
||||
yield msg("reset");
|
||||
});
|
||||
|
||||
add_task(function* mouse() {
|
||||
yield setUp();
|
||||
|
||||
let state = yield msg("key", { key: "x", waitForSuggestions: true });
|
||||
checkState(state, "x", ["xfoo", "xbar"], -1);
|
||||
|
||||
state = yield msg("mousemove", 0);
|
||||
checkState(state, "x", ["xfoo", "xbar"], 0);
|
||||
|
||||
state = yield msg("mousemove", 1);
|
||||
checkState(state, "x", ["xfoo", "xbar"], 1);
|
||||
|
||||
state = yield msg("mousemove", 2);
|
||||
checkState(state, "x", ["xfoo", "xbar"], 1, 0);
|
||||
|
||||
state = yield msg("mousemove", 3);
|
||||
checkState(state, "x", ["xfoo", "xbar"], 1, 1);
|
||||
for (let i = 0; i < 4; ++i) {
|
||||
state = yield msg("mousemove", i);
|
||||
checkState(state, "x", ["xfoo", "xbar"], i);
|
||||
}
|
||||
|
||||
state = yield msg("mousemove", -1);
|
||||
checkState(state, "x", ["xfoo", "xbar"], 1);
|
||||
|
||||
yield msg("reset");
|
||||
yield setUp();
|
||||
|
||||
state = yield msg("key", { key: "x", waitForSuggestions: true });
|
||||
checkState(state, "x", ["xfoo", "xbar"], -1);
|
||||
|
||||
state = yield msg("mousemove", 0);
|
||||
checkState(state, "x", ["xfoo", "xbar"], 0);
|
||||
|
||||
state = yield msg("mousemove", 2);
|
||||
checkState(state, "x", ["xfoo", "xbar"], 0, 0);
|
||||
|
||||
state = yield msg("mousemove", -1);
|
||||
checkState(state, "x", ["xfoo", "xbar"], 0);
|
||||
|
||||
yield msg("reset");
|
||||
});
|
||||
|
||||
@ -379,33 +197,6 @@ add_task(function* formHistory() {
|
||||
yield msg("reset");
|
||||
});
|
||||
|
||||
add_task(function* cycleEngines() {
|
||||
yield setUp();
|
||||
yield msg("key", "VK_DOWN");
|
||||
|
||||
function promiseEngineChange(newEngineName) {
|
||||
let deferred = Promise.defer();
|
||||
Services.obs.addObserver(function resolver(subj, topic, data) {
|
||||
if (data != "engine-current") {
|
||||
return;
|
||||
}
|
||||
is(subj.name, newEngineName, "Engine cycled correctly");
|
||||
Services.obs.removeObserver(resolver, "browser-search-engine-modified");
|
||||
deferred.resolve();
|
||||
}, "browser-search-engine-modified", false);
|
||||
}
|
||||
|
||||
let p = promiseEngineChange(TEST_ENGINE_PREFIX + " " + TEST_ENGINE_2_BASENAME);
|
||||
yield msg("key", { key: "VK_DOWN", modifiers: { accelKey: true }});
|
||||
yield p;
|
||||
|
||||
p = promiseEngineChange(TEST_ENGINE_PREFIX + " " + TEST_ENGINE_BASENAME);
|
||||
yield msg("key", { key: "VK_UP", modifiers: { accelKey: true }});
|
||||
yield p;
|
||||
|
||||
yield msg("reset");
|
||||
});
|
||||
|
||||
add_task(function* search() {
|
||||
yield setUp();
|
||||
|
||||
@ -506,42 +297,6 @@ add_task(function* search() {
|
||||
yield promiseTab();
|
||||
yield setUp();
|
||||
|
||||
// Test selecting a suggestion, then clicking a one-off without deselecting the
|
||||
// suggestion.
|
||||
yield msg("key", { key: "x", waitForSuggestions: true });
|
||||
p = msg("waitForSearch");
|
||||
yield msg("mousemove", 1);
|
||||
yield msg("mousemove", 3);
|
||||
yield msg("click", { eltIdx: 3, modifiers: modifiers });
|
||||
mesg = yield p;
|
||||
eventData.searchString = "xfoo"
|
||||
eventData.selection = {
|
||||
index: 1,
|
||||
kind: "mouse",
|
||||
};
|
||||
SimpleTest.isDeeply(eventData, mesg, "Search event data");
|
||||
|
||||
yield promiseTab();
|
||||
yield setUp();
|
||||
|
||||
// Same as above, but with the keyboard.
|
||||
delete modifiers.button;
|
||||
yield msg("key", { key: "x", waitForSuggestions: true });
|
||||
p = msg("waitForSearch");
|
||||
yield msg("key", "VK_DOWN");
|
||||
yield msg("key", "VK_DOWN");
|
||||
yield msg("key", "VK_TAB");
|
||||
yield msg("key", { key: "VK_RETURN", modifiers: modifiers });
|
||||
mesg = yield p;
|
||||
eventData.selection = {
|
||||
index: 1,
|
||||
kind: "key",
|
||||
};
|
||||
SimpleTest.isDeeply(eventData, mesg, "Search event data");
|
||||
|
||||
yield promiseTab();
|
||||
yield setUp();
|
||||
|
||||
// Test searching when using IME composition.
|
||||
let state = yield msg("startComposition", { data: "" });
|
||||
checkState(state, "", [], -1);
|
||||
@ -553,10 +308,8 @@ add_task(function* search() {
|
||||
p = msg("waitForSearch");
|
||||
yield msg("key", { key: "VK_RETURN", modifiers: modifiers });
|
||||
mesg = yield p;
|
||||
eventData.searchString = "x"
|
||||
eventData.originalEvent = modifiers;
|
||||
eventData.engineName = TEST_ENGINE_PREFIX + " " + TEST_ENGINE_BASENAME;
|
||||
delete eventData.selection;
|
||||
SimpleTest.isDeeply(eventData, mesg, "Search event data");
|
||||
|
||||
yield promiseTab();
|
||||
@ -675,7 +428,7 @@ function msg(type, data=null) {
|
||||
}
|
||||
|
||||
function checkState(actualState, expectedInputVal, expectedSuggestions,
|
||||
expectedSelectedIdx, expectedSelectedButtonIdx) {
|
||||
expectedSelectedIdx) {
|
||||
expectedSuggestions = expectedSuggestions.map(sugg => {
|
||||
return typeof(sugg) == "object" ? sugg : {
|
||||
str: sugg,
|
||||
@ -683,10 +436,6 @@ function checkState(actualState, expectedInputVal, expectedSuggestions,
|
||||
};
|
||||
});
|
||||
|
||||
if (expectedSelectedIdx == -1 && expectedSelectedButtonIdx != undefined) {
|
||||
expectedSelectedIdx = expectedSuggestions.length + expectedSelectedButtonIdx;
|
||||
}
|
||||
|
||||
let expectedState = {
|
||||
selectedIndex: expectedSelectedIdx,
|
||||
numSuggestions: expectedSuggestions.length,
|
||||
@ -699,15 +448,6 @@ function checkState(actualState, expectedInputVal, expectedSuggestions,
|
||||
inputValue: expectedInputVal,
|
||||
ariaExpanded: expectedSuggestions.length == 0 ? "false" : "true",
|
||||
};
|
||||
if (expectedSelectedButtonIdx != undefined) {
|
||||
expectedState.selectedButtonIndex = expectedSelectedButtonIdx;
|
||||
}
|
||||
else if (expectedSelectedIdx < expectedSuggestions.length) {
|
||||
expectedState.selectedButtonIndex = -1;
|
||||
}
|
||||
else {
|
||||
expectedState.selectedButtonIndex = expectedSelectedIdx - expectedSuggestions.length;
|
||||
}
|
||||
|
||||
SimpleTest.isDeeply(actualState, expectedState, "State");
|
||||
}
|
||||
|
@ -124,27 +124,11 @@ let messageHandlers = {
|
||||
ack("addInputValueToFormHistory");
|
||||
},
|
||||
|
||||
addDuplicateOneOff: function () {
|
||||
let btn = gController._oneOffButtons[gController._oneOffButtons.length - 1];
|
||||
let newBtn = btn.cloneNode(true);
|
||||
btn.parentNode.appendChild(newBtn);
|
||||
gController._oneOffButtons.push(newBtn);
|
||||
ack("addDuplicateOneOff");
|
||||
},
|
||||
|
||||
removeLastOneOff: function () {
|
||||
gController._oneOffButtons.pop().remove();
|
||||
ack("removeLastOneOff");
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
// Reset both the input and suggestions by select all + delete. If there was
|
||||
// no text entered, this won't have any effect, so also escape to ensure the
|
||||
// suggestions table is closed.
|
||||
// Reset both the input and suggestions by select all + delete.
|
||||
gController.input.focus();
|
||||
content.synthesizeKey("a", { accelKey: true });
|
||||
content.synthesizeKey("VK_DELETE", {});
|
||||
content.synthesizeKey("VK_ESCAPE", {});
|
||||
ack("reset");
|
||||
},
|
||||
};
|
||||
@ -181,7 +165,6 @@ function waitForContentSearchEvent(messageType, cb) {
|
||||
function currentState() {
|
||||
let state = {
|
||||
selectedIndex: gController.selectedIndex,
|
||||
selectedButtonIndex: gController.selectedButtonIndex,
|
||||
numSuggestions: gController._table.hidden ? 0 : gController.numSuggestions,
|
||||
suggestionAtIndex: [],
|
||||
isFormHistorySuggestionAtIndex: [],
|
||||
|
@ -0,0 +1,2 @@
|
||||
<!DOCTYPE html>
|
||||
<audio src="audio.ogg" controls loop>
|
@ -1380,7 +1380,9 @@ html[dir="rtl"] .standalone .room-conversation-wrapper .room-inner-info-area {
|
||||
background-repeat: no-repeat;
|
||||
border: 1rem #fff solid;
|
||||
box-shadow: 0 0 5px #000;
|
||||
margin: 0;
|
||||
margin: auto;
|
||||
/* `width` here is specified by the design spec. */
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.standalone .prompt-media-message.chrome {
|
||||
|
@ -1935,6 +1935,7 @@ MarkupContainer.prototype = {
|
||||
let link = target.dataset.link;
|
||||
let type = target.dataset.type;
|
||||
this.markup._inspector.followAttributeLink(type, link);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start dragging the container after a delay.
|
||||
|
@ -25,7 +25,7 @@ add_task(function*() {
|
||||
preventDefault: function() {}
|
||||
});
|
||||
|
||||
is(el.isDragging, false, "isDragging should not be set to true immedietly");
|
||||
ok(!el.isDragging, "isDragging should not be set to true immediately");
|
||||
|
||||
info("Waiting " + (GRAB_DELAY + 1) + "ms");
|
||||
yield wait(GRAB_DELAY + 1);
|
||||
@ -42,5 +42,20 @@ add_task(function*() {
|
||||
|
||||
yield dropCompleted;
|
||||
|
||||
is(el.isDragging, false, "isDragging false after mouseUp");
|
||||
ok(!el.isDragging, "isDragging false after mouseUp");
|
||||
|
||||
info("Simulating middle click on #test");
|
||||
el._onMouseDown({
|
||||
target: el.tagLine,
|
||||
button: 1,
|
||||
pageX: rect.x,
|
||||
pageY: rect.y,
|
||||
stopPropagation: function() {},
|
||||
preventDefault: function() {}
|
||||
});
|
||||
ok(!el.isDragging, "isDragging should not be set to true immediately");
|
||||
|
||||
info("Waiting " + (GRAB_DELAY + 1) + "ms");
|
||||
yield wait(GRAB_DELAY + 1);
|
||||
ok(!el.isDragging, "isDragging never starts after middle click after mouseUp");
|
||||
});
|
||||
|
@ -111,6 +111,7 @@ support-files =
|
||||
test-property-provider.html
|
||||
test-repeated-messages.html
|
||||
test-result-format-as-string.html
|
||||
test-trackingprotection-securityerrors.html
|
||||
test-webconsole-error-observer.html
|
||||
test_bug_770099_violation.html
|
||||
test_bug_770099_violation.html^headers^
|
||||
@ -350,6 +351,8 @@ skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_split_focus.js]
|
||||
[browser_webconsole_split_persist.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
|
||||
[browser_webconsole_trackingprotection_errors.js]
|
||||
tags = trackingprotection
|
||||
[browser_webconsole_view_source.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s (expectUncaughtException)
|
||||
[browser_webconsole_reflow.js]
|
||||
|
@ -0,0 +1,48 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Load a page with tracking elements that get blocked and make sure that a
|
||||
// 'learn more' link shows up in the webconsole.
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URI = "http://tracking.example.org/browser/browser/devtools/webconsole/test/test-trackingprotection-securityerrors.html";
|
||||
const LEARN_MORE_URI = "https://developer.mozilla.org/Firefox/Privacy/Tracking_Protection";
|
||||
const PREF = "privacy.trackingprotection.enabled";
|
||||
const {UrlClassifierTestUtils} = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref(PREF);
|
||||
UrlClassifierTestUtils.cleanupTestTrackers();
|
||||
});
|
||||
|
||||
add_task(function* testMessagesAppear() {
|
||||
yield UrlClassifierTestUtils.addTestTrackers();
|
||||
Services.prefs.setBoolPref(PREF, true);
|
||||
|
||||
let { browser } = yield loadTab(TEST_URI);
|
||||
|
||||
let hud = yield openConsole();
|
||||
|
||||
let results = yield waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [
|
||||
{
|
||||
name: "Was blocked because tracking protection is enabled",
|
||||
text: "The resource at \"http://tracking.example.com/\" was blocked because tracking protection is enabled",
|
||||
category: CATEGORY_SECURITY,
|
||||
severity: SEVERITY_WARNING,
|
||||
objects: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
yield testClickOpenNewTab(hud, results[0]);
|
||||
});
|
||||
|
||||
function testClickOpenNewTab(hud, match) {
|
||||
let warningNode = match.clickableElements[0];
|
||||
ok(warningNode, "link element");
|
||||
ok(warningNode.classList.contains("learn-more-link"), "link class name");
|
||||
return simulateMessageLinkClick(warningNode, LEARN_MORE_URI);
|
||||
}
|
@ -1,5 +1,12 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!-- 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/. -->
|
||||
|
||||
<!ENTITY closeTab.label "Close Tab">
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="http://tracking.example.com/"></iframe>
|
||||
</body>
|
||||
</html>
|
@ -42,6 +42,8 @@ const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
const MIXED_CONTENT_LEARN_MORE = "https://developer.mozilla.org/docs/Security/MixedContent";
|
||||
|
||||
const TRACKING_PROTECTION_LEARN_MORE = "https://developer.mozilla.org/Firefox/Privacy/Tracking_Protection";
|
||||
|
||||
const INSECURE_PASSWORDS_LEARN_MORE = "https://developer.mozilla.org/docs/Security/InsecurePasswords";
|
||||
|
||||
const STRICT_TRANSPORT_SECURITY_LEARN_MORE = "https://developer.mozilla.org/docs/Security/HTTP_Strict_Transport_Security";
|
||||
@ -1679,6 +1681,9 @@ WebConsoleFrame.prototype = {
|
||||
case "SHA-1 Signature":
|
||||
url = WEAK_SIGNATURE_ALGORITHM_LEARN_MORE;
|
||||
break;
|
||||
case "Tracking Protection":
|
||||
url = TRACKING_PROTECTION_LEARN_MORE;
|
||||
break;
|
||||
default:
|
||||
// Unknown category. Return without adding more info node.
|
||||
return;
|
||||
|
@ -29,3 +29,7 @@ tabs.closeWarningTitle=Confirm close
|
||||
tabs.closeWarningMultiple=;You are about to close #1 tabs. Are you sure you want to continue?
|
||||
tabs.closeButtonMultiple=Close tabs
|
||||
tabs.closeWarningPromptMe=Warn me when I attempt to close multiple tabs
|
||||
|
||||
tabs.closeTab.tooltip=Close tab
|
||||
tabs.playingAudio.tooltip=This tab is playing audio
|
||||
tabs.mutedAudio.tooltip=This tab has been muted
|
||||
|
@ -94,7 +94,6 @@
|
||||
locale/browser/engineManager.properties (%chrome/browser/engineManager.properties)
|
||||
locale/browser/setDesktopBackground.dtd (%chrome/browser/setDesktopBackground.dtd)
|
||||
locale/browser/shellservice.properties (%chrome/browser/shellservice.properties)
|
||||
locale/browser/tabbrowser.dtd (%chrome/browser/tabbrowser.dtd)
|
||||
locale/browser/tabbrowser.properties (%chrome/browser/tabbrowser.properties)
|
||||
locale/browser/tabview.properties (%chrome/browser/tabview.properties)
|
||||
locale/browser/taskbar.properties (%chrome/browser/taskbar.properties)
|
||||
|
@ -243,6 +243,8 @@ browser.jar:
|
||||
skin/classic/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png)
|
||||
skin/classic/browser/tabbrowser/tab-arrow-left.png (tabbrowser/tab-arrow-left.png)
|
||||
skin/classic/browser/tabbrowser/tab-arrow-left-inverted.png (tabbrowser/tab-arrow-left-inverted.png)
|
||||
skin/classic/browser/tabbrowser/tab-audio.svg (../shared/tabbrowser/tab-audio.svg)
|
||||
skin/classic/browser/tabbrowser/tab-audio-small.svg (../shared/tabbrowser/tab-audio-small.svg)
|
||||
skin/classic/browser/tabbrowser/tab-background-end.png (tabbrowser/tab-background-end.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-middle.png (tabbrowser/tab-background-middle.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-start.png (tabbrowser/tab-background-start.png)
|
||||
|
@ -337,6 +337,8 @@ browser.jar:
|
||||
skin/classic/browser/tabbrowser/tab-arrow-right@2x.png (tabbrowser/tab-arrow-right@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-arrow-right-inverted.png (tabbrowser/tab-arrow-right-inverted.png)
|
||||
skin/classic/browser/tabbrowser/tab-arrow-right-inverted@2x.png (tabbrowser/tab-arrow-right-inverted@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-audio.svg (../shared/tabbrowser/tab-audio.svg)
|
||||
skin/classic/browser/tabbrowser/tab-audio-small.svg (../shared/tabbrowser/tab-audio-small.svg)
|
||||
skin/classic/browser/tabbrowser/tab-background-end.png (tabbrowser/tab-background-end.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-end@2x.png (tabbrowser/tab-background-end@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-middle.png (tabbrowser/tab-background-middle.png)
|
||||
|
85
browser/themes/shared/tabbrowser/tab-audio-small.svg
Normal file
85
browser/themes/shared/tabbrowser/tab-audio-small.svg
Normal file
@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
|
||||
<style>
|
||||
use:not(:target) {
|
||||
display: none;
|
||||
}
|
||||
.icon {
|
||||
fill: #4d4d4d;
|
||||
}
|
||||
.icon.hover {
|
||||
fill: #333333;
|
||||
}
|
||||
.icon.pressed {
|
||||
fill: #000;
|
||||
}
|
||||
.icon.dark {
|
||||
fill: #ccc;
|
||||
}
|
||||
.icon.dark.hover {
|
||||
fill: #b2b2b2;
|
||||
}
|
||||
.icon.dark.pressed {
|
||||
fill: #fff;
|
||||
}
|
||||
.muted {
|
||||
opacity: .7;
|
||||
stroke: #4d4d4d;
|
||||
stroke-width: 0;
|
||||
}
|
||||
.muted.hover {
|
||||
opacity: .85;
|
||||
stroke: #333333;
|
||||
}
|
||||
.muted.pressed {
|
||||
opacity: 1;
|
||||
stroke: #000;
|
||||
}
|
||||
.muted.dark {
|
||||
stroke: #ccc;
|
||||
}
|
||||
.muted.dark.hover {
|
||||
stroke: #b2b2b2;
|
||||
}
|
||||
.muted.dark.pressed {
|
||||
stroke: #fff;
|
||||
}
|
||||
</style>
|
||||
<defs>
|
||||
<clipPath id="clip-wave">
|
||||
<path d="M 11,7 l 3,-8 l 2,0 l 0,18 l -2,0 l -3,-8 z" />
|
||||
</clipPath>
|
||||
<mask id="disabled-cutout">
|
||||
<rect width="16" height="16" fill="#fff" />
|
||||
<line x1="4" y1="14" x2="14" y2="4" stroke="#000" stroke-width="2" />
|
||||
</mask>
|
||||
<g id="shape-tab-audio">
|
||||
<rect x="3" y="6" width="5" height="4" rx="1" ry="1" />
|
||||
<polygon points="5.5,6.5 9,3 9,13 5.5,9.5" />
|
||||
<path d="M 10,6.5 a 1.5 1.5 0 0,1 0,3 z" />
|
||||
<path d="M 10,4 a 4 4 0 0,1 0,8 l 0,-1 a 3 3 0 0,0 0,-6 z" clip-path="url(#clip-wave)" />
|
||||
</g>
|
||||
<g id="shape-tab-audio-muted">
|
||||
<g mask="url(#disabled-cutout)">
|
||||
<rect x="4" y="6" width="5" height="4" rx="1" ry="1" />
|
||||
<polygon points="6.5,6.5 10,3 10,13 6.5,9.5" />
|
||||
</g>
|
||||
<line x1="3" y1="14" x2="13" y2="4" stroke-width="1.5" />
|
||||
</g>
|
||||
</defs>
|
||||
<use id="tab-audio" class="icon" xlink:href="#shape-tab-audio"/>
|
||||
<use id="tab-audio-hover" class="icon hover" xlink:href="#shape-tab-audio"/>
|
||||
<use id="tab-audio-pressed" class="icon pressed" xlink:href="#shape-tab-audio"/>
|
||||
<use id="tab-audio-muted" class="icon muted" xlink:href="#shape-tab-audio-muted" />
|
||||
<use id="tab-audio-muted-hover" class="icon muted hover" xlink:href="#shape-tab-audio-muted" />
|
||||
<use id="tab-audio-muted-pressed" class="icon muted pressed" xlink:href="#shape-tab-audio-muted" />
|
||||
<use id="tab-audio-dark" class="icon dark" xlink:href="#shape-tab-audio"/>
|
||||
<use id="tab-audio-dark-hover" class="icon hover dark" xlink:href="#shape-tab-audio"/>
|
||||
<use id="tab-audio-dark-pressed" class="icon pressed dark" xlink:href="#shape-tab-audio"/>
|
||||
<use id="tab-audio-muted-dark" class="icon muted dark" xlink:href="#shape-tab-audio-muted" />
|
||||
<use id="tab-audio-muted-dark-hover" class="icon muted hover dark" xlink:href="#shape-tab-audio-muted" />
|
||||
<use id="tab-audio-muted-dark-pressed" class="icon muted pressed dark" xlink:href="#shape-tab-audio-muted" />
|
||||
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
86
browser/themes/shared/tabbrowser/tab-audio.svg
Normal file
86
browser/themes/shared/tabbrowser/tab-audio.svg
Normal file
@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16">
|
||||
<style>
|
||||
use:not(:target) {
|
||||
display: none;
|
||||
}
|
||||
.icon {
|
||||
fill: #666;
|
||||
}
|
||||
.icon.hover {
|
||||
fill: #4d4d4d;
|
||||
}
|
||||
.icon.pressed {
|
||||
fill: #000;
|
||||
}
|
||||
.icon.dark {
|
||||
fill: #999;
|
||||
}
|
||||
.icon.dark.hover {
|
||||
fill: #b2b2b2;
|
||||
}
|
||||
.icon.dark.pressed {
|
||||
fill: #fff;
|
||||
}
|
||||
.muted {
|
||||
opacity: .7;
|
||||
stroke: #666;
|
||||
stroke-width: 0;
|
||||
}
|
||||
.muted.hover {
|
||||
opacity: .85;
|
||||
stroke: #4d4d4d;
|
||||
}
|
||||
.muted.pressed {
|
||||
opacity: 1;
|
||||
stroke: #000;
|
||||
}
|
||||
.muted.dark {
|
||||
stroke: #999;
|
||||
}
|
||||
.muted.dark.hover {
|
||||
stroke: #b2b2b2;
|
||||
}
|
||||
.muted.dark.pressed {
|
||||
stroke: #fff;
|
||||
}
|
||||
</style>
|
||||
<defs>
|
||||
<clipPath id="clip-wave">
|
||||
<path d="M 10,7 l 3,-8 l 2,0 l 0,18 l -2,0 l -3,-8 z" />
|
||||
</clipPath>
|
||||
<mask id="disabled-cutout">
|
||||
<rect width="16" height="16" fill="#fff" />
|
||||
<line x1="4" y1="14" x2="14" y2="4" stroke="#000" stroke-width="2" />
|
||||
</mask>
|
||||
<g id="shape-tab-audio">
|
||||
<rect x="2" y="5" width="6" height="6" rx="2" ry="2" />
|
||||
<polygon points="4,6 9,2 9,14 4,10" />
|
||||
<path d="M 10,7 a 1 1 0 0,1 0,2 z" />
|
||||
<path d="M 10,5 a 3 3 0 0,1 0,6 l 0,-1 a 2 2 0 0,0 0,-4 z" clip-path="url(#clip-wave)" />
|
||||
<path d="M 10,3 a 5 5 0 0,1 0,10 l 0,-1 a 4 4 0 0,0 0,-8 z" clip-path="url(#clip-wave)" />
|
||||
</g>
|
||||
<g id="shape-tab-audio-muted">
|
||||
<g mask="url(#disabled-cutout)">
|
||||
<rect x="3" y="5" width="6" height="6" rx="2" ry="2" />
|
||||
<polygon points="5,6 10,2 10,14 5,10" />
|
||||
</g>
|
||||
<line x1="2" y1="13" x2="14" y2="3" stroke-width="1.5" />
|
||||
</g>
|
||||
</defs>
|
||||
<use id="tab-audio" class="icon" xlink:href="#shape-tab-audio"/>
|
||||
<use id="tab-audio-hover" class="icon hover" xlink:href="#shape-tab-audio"/>
|
||||
<use id="tab-audio-pressed" class="icon pressed" xlink:href="#shape-tab-audio"/>
|
||||
<use id="tab-audio-muted" class="icon muted" xlink:href="#shape-tab-audio-muted" />
|
||||
<use id="tab-audio-muted-hover" class="icon muted hover" xlink:href="#shape-tab-audio-muted" />
|
||||
<use id="tab-audio-muted-pressed" class="icon muted pressed" xlink:href="#shape-tab-audio-muted" />
|
||||
<use id="tab-audio-dark" class="icon dark" xlink:href="#shape-tab-audio"/>
|
||||
<use id="tab-audio-dark-hover" class="icon hover dark" xlink:href="#shape-tab-audio"/>
|
||||
<use id="tab-audio-dark-pressed" class="icon pressed dark" xlink:href="#shape-tab-audio"/>
|
||||
<use id="tab-audio-muted-dark" class="icon muted dark" xlink:href="#shape-tab-audio-muted" />
|
||||
<use id="tab-audio-muted-dark-hover" class="icon muted hover dark" xlink:href="#shape-tab-audio-muted" />
|
||||
<use id="tab-audio-muted-dark-pressed" class="icon muted pressed dark" xlink:href="#shape-tab-audio-muted" />
|
||||
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
@ -72,6 +72,7 @@
|
||||
|
||||
.tab-throbber,
|
||||
.tab-icon-image,
|
||||
.tab-icon-sound,
|
||||
.tab-close-button {
|
||||
margin-top: 1px;
|
||||
}
|
||||
@ -90,7 +91,7 @@
|
||||
.tab-icon-overlay {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-top: 10px;
|
||||
margin-top: -12px;
|
||||
-moz-margin-start: -16px;
|
||||
display: none;
|
||||
}
|
||||
@ -100,6 +101,58 @@
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/crashed.svg");
|
||||
}
|
||||
|
||||
.tab-icon-overlay[soundplaying][pinned] {
|
||||
display: -moz-box;
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio");
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.tab-icon-overlay[soundplaying][pinned]:hover {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-hover");
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.tab-icon-overlay[soundplaying][pinned]:hover:active {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-pressed");
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.tab-icon-overlay[muted][pinned] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted");
|
||||
}
|
||||
|
||||
.tab-icon-overlay[muted][pinned]:hover {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-hover");
|
||||
}
|
||||
|
||||
.tab-icon-overlay[muted][pinned]:hover:active {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-pressed");
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-dark");
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned]:hover {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-hover");
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] .tab-icon-overlay[soundplaying][pinned]:hover:active {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-pressed");
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted");
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:hover {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-hover");
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] .tab-icon-overlay[muted][pinned]:hover:active {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-small.svg#tab-audio-muted-pressed");
|
||||
}
|
||||
|
||||
.tab-throbber[busy] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
|
||||
}
|
||||
@ -119,6 +172,66 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tab-icon-sound {
|
||||
-moz-margin-start: 4px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tab-icon-sound:not([soundplaying]),
|
||||
.tab-icon-sound[pinned] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-icon-sound[soundplaying] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio");
|
||||
}
|
||||
|
||||
.tab-icon-sound[soundplaying]:hover {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-hover");
|
||||
}
|
||||
|
||||
.tab-icon-sound[soundplaying]:hover:active {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-pressed");
|
||||
}
|
||||
|
||||
.tab-icon-sound[muted] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted");
|
||||
}
|
||||
|
||||
.tab-icon-sound[muted]:hover {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-hover");
|
||||
}
|
||||
|
||||
.tab-icon-sound[muted]:hover:active {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-pressed");
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] .tab-icon-sound[soundplaying] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-dark");
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] .tab-icon-sound[soundplaying]:hover {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-dark-hover");
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] .tab-icon-sound[soundplaying]:hover:active {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-dark-pressed");
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] .tab-icon-sound[muted] {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-dark");
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] .tab-icon-sound[muted]:hover {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-dark-hover");
|
||||
}
|
||||
|
||||
#TabsToolbar[brighttext] .tab-icon-sound[muted]:hover:active {
|
||||
list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio.svg#tab-audio-muted-dark-pressed");
|
||||
}
|
||||
|
||||
.tab-background,
|
||||
.tabs-newtab-button {
|
||||
/* overlap the tab curves */
|
||||
@ -300,6 +413,8 @@
|
||||
|
||||
.tab-background-middle,
|
||||
.tabs-newtab-button,
|
||||
.tab-icon-overlay[soundplaying],
|
||||
.tab-icon-sound,
|
||||
.tab-close-button {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
@ -343,6 +343,8 @@ browser.jar:
|
||||
skin/classic/browser/tabbrowser/tab-arrow-left-XPVista7@2x.png (tabbrowser/tab-arrow-left-XPVista7@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-arrow-left-inverted.png (tabbrowser/tab-arrow-left-inverted.png)
|
||||
skin/classic/browser/tabbrowser/tab-arrow-left-inverted@2x.png (tabbrowser/tab-arrow-left-inverted@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-audio.svg (../shared/tabbrowser/tab-audio.svg)
|
||||
skin/classic/browser/tabbrowser/tab-audio-small.svg (../shared/tabbrowser/tab-audio-small.svg)
|
||||
skin/classic/browser/tabbrowser/tab-background-start.png (tabbrowser/tab-background-start.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-start@2x.png (tabbrowser/tab-background-start@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-middle.png (tabbrowser/tab-background-middle.png)
|
||||
|
@ -96,6 +96,11 @@ private:
|
||||
virtual void run(const MatchFinder::MatchResult &Result);
|
||||
};
|
||||
|
||||
class NoDuplicateRefCntMemberChecker : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
virtual void run(const MatchFinder::MatchResult &Result);
|
||||
};
|
||||
|
||||
class NeedsNoVTableTypeChecker : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
virtual void run(const MatchFinder::MatchResult &Result);
|
||||
@ -115,6 +120,7 @@ private:
|
||||
NoAddRefReleaseOnReturnChecker noAddRefReleaseOnReturnChecker;
|
||||
RefCountedInsideLambdaChecker refCountedInsideLambdaChecker;
|
||||
ExplicitOperatorBoolChecker explicitOperatorBoolChecker;
|
||||
NoDuplicateRefCntMemberChecker noDuplicateRefCntMemberChecker;
|
||||
NeedsNoVTableTypeChecker needsNoVTableTypeChecker;
|
||||
NonMemMovableChecker nonMemMovableChecker;
|
||||
MatchFinder astMatcher;
|
||||
@ -581,6 +587,48 @@ bool IsInSystemHeader(const ASTContext &AC, const T &D) {
|
||||
return SourceManager.isInSystemHeader(ExpansionLoc);
|
||||
}
|
||||
|
||||
const FieldDecl *getClassRefCntMember(const CXXRecordDecl *D) {
|
||||
for (RecordDecl::field_iterator field = D->field_begin(), e = D->field_end();
|
||||
field != e; ++field) {
|
||||
if (field->getName() == "mRefCnt") {
|
||||
return *field;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const FieldDecl *getClassRefCntMember(QualType T) {
|
||||
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
|
||||
T = arrTy->getElementType();
|
||||
CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
|
||||
return clazz ? getClassRefCntMember(clazz) : 0;
|
||||
}
|
||||
|
||||
const FieldDecl *getBaseRefCntMember(QualType T);
|
||||
|
||||
const FieldDecl *getBaseRefCntMember(const CXXRecordDecl *D) {
|
||||
const FieldDecl *refCntMember = getClassRefCntMember(D);
|
||||
if (refCntMember && isClassRefCounted(D)) {
|
||||
return refCntMember;
|
||||
}
|
||||
|
||||
for (CXXRecordDecl::base_class_const_iterator base = D->bases_begin(), e = D->bases_end();
|
||||
base != e; ++base) {
|
||||
refCntMember = getBaseRefCntMember(base->getType());
|
||||
if (refCntMember) {
|
||||
return refCntMember;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const FieldDecl *getBaseRefCntMember(QualType T) {
|
||||
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
|
||||
T = arrTy->getElementType();
|
||||
CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
|
||||
return clazz ? getBaseRefCntMember(clazz) : 0;
|
||||
}
|
||||
|
||||
bool typeHasVTable(QualType T) {
|
||||
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
|
||||
T = arrTy->getElementType();
|
||||
@ -742,6 +790,10 @@ AST_POLYMORPHIC_MATCHER_P(equalsBoundNode,
|
||||
|
||||
#endif
|
||||
|
||||
AST_MATCHER(CXXRecordDecl, hasRefCntMember) {
|
||||
return isClassRefCounted(&Node) && getClassRefCntMember(&Node);
|
||||
}
|
||||
|
||||
AST_MATCHER(QualType, hasVTable) {
|
||||
return typeHasVTable(Node);
|
||||
}
|
||||
@ -984,6 +1036,10 @@ DiagnosticsMatcher::DiagnosticsMatcher()
|
||||
hasName("operator _Bool"))).bind("node"),
|
||||
&explicitOperatorBoolChecker);
|
||||
|
||||
astMatcher.addMatcher(recordDecl(allOf(decl().bind("decl"),
|
||||
hasRefCntMember())),
|
||||
&noDuplicateRefCntMemberChecker);
|
||||
|
||||
astMatcher.addMatcher(classTemplateSpecializationDecl(
|
||||
allOf(hasAnyTemplateArgument(refersToType(hasVTable())),
|
||||
hasNeedsNoVTableTypeAttr())).bind("node"),
|
||||
@ -1177,6 +1233,32 @@ void DiagnosticsMatcher::ExplicitOperatorBoolChecker::run(
|
||||
}
|
||||
}
|
||||
|
||||
void DiagnosticsMatcher::NoDuplicateRefCntMemberChecker::run(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
|
||||
unsigned warningID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
||||
DiagnosticIDs::Error, "Refcounted record %0 has multiple mRefCnt members");
|
||||
unsigned note1ID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
||||
DiagnosticIDs::Note, "Superclass %0 also has an mRefCnt member");
|
||||
unsigned note2ID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
||||
DiagnosticIDs::Note, "Consider using the _INHERITED macros for AddRef and Release here");
|
||||
|
||||
const CXXRecordDecl *decl = Result.Nodes.getNodeAs<CXXRecordDecl>("decl");
|
||||
const FieldDecl *refCntMember = getClassRefCntMember(decl);
|
||||
assert(refCntMember && "The matcher checked to make sure we have a refCntMember");
|
||||
|
||||
// Check every superclass for whether it has a base with a refcnt member, and warn for those which do
|
||||
for (CXXRecordDecl::base_class_const_iterator base = decl->bases_begin(), e = decl->bases_end();
|
||||
base != e; ++base) {
|
||||
const FieldDecl *baseRefCntMember = getBaseRefCntMember(base->getType());
|
||||
if (baseRefCntMember) {
|
||||
Diag.Report(decl->getLocStart(), warningID) << decl;
|
||||
Diag.Report(baseRefCntMember->getLocStart(), note1ID) << baseRefCntMember->getParent();
|
||||
Diag.Report(refCntMember->getLocStart(), note2ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagnosticsMatcher::NeedsNoVTableTypeChecker::run(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
|
||||
|
38
build/clang-plugin/tests/TestNoDuplicateRefCntMember.cpp
Normal file
38
build/clang-plugin/tests/TestNoDuplicateRefCntMember.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
class C1 {};
|
||||
|
||||
class RC1 {
|
||||
public:
|
||||
virtual void AddRef();
|
||||
virtual void Release();
|
||||
|
||||
private:
|
||||
int mRefCnt; // expected-note 2 {{Superclass 'RC1' also has an mRefCnt member}}
|
||||
};
|
||||
|
||||
class RC2 : public RC1 { // expected-error {{Refcounted record 'RC2' has multiple mRefCnt members}}
|
||||
public:
|
||||
virtual void AddRef();
|
||||
virtual void Release();
|
||||
|
||||
private:
|
||||
int mRefCnt; // expected-note {{Consider using the _INHERITED macros for AddRef and Release here}}
|
||||
};
|
||||
|
||||
class C2 : public RC1 {};
|
||||
|
||||
class RC3 : public RC1 {};
|
||||
|
||||
class RC4 : public RC3, public C2 {};
|
||||
|
||||
class RC5 : public RC1 {};
|
||||
|
||||
class RC6 : public C1, public RC5 { // expected-error {{Refcounted record 'RC6' has multiple mRefCnt members}}
|
||||
public:
|
||||
virtual void AddRef();
|
||||
virtual void Release();
|
||||
|
||||
private:
|
||||
int mRefCnt; // expected-note {{Consider using the _INHERITED macros for AddRef and Release here}}
|
||||
};
|
||||
|
||||
class Predecl;
|
@ -17,6 +17,7 @@ SOURCES += [
|
||||
'TestNeedsNoVTableType.cpp',
|
||||
'TestNoAddRefReleaseOnReturn.cpp',
|
||||
'TestNoArithmeticExprInArgument.cpp',
|
||||
'TestNoDuplicateRefCntMember.cpp',
|
||||
'TestNonHeapClass.cpp',
|
||||
'TestNonMemMovable.cpp',
|
||||
'TestNoRefcountedInsideLambdas.cpp',
|
||||
|
@ -32,6 +32,14 @@ if [ -d "$topsrcdir/gtk3" ]; then
|
||||
GDK_PIXBUF_MODULEDIR=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \
|
||||
$topsrcdir/gtk3/usr/local/bin/gdk-pixbuf-query-loaders > $topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
|
||||
|
||||
# The fontconfig version in the tooltool package has known uses of
|
||||
# uninitialized memory when creating its cache, and while most users
|
||||
# will already have an existing cache, running Firefox on automation
|
||||
# will create it. Combined with valgrind, this generates irrelevant
|
||||
# errors.
|
||||
# So create the fontconfig cache beforehand.
|
||||
$TOOLTOOL_DIR/gtk3/usr/local/bin/fc-cache
|
||||
|
||||
# mock build environment doesn't have fonts in /usr/share/fonts, but
|
||||
# has some in /usr/share/X11/fonts. Add this directory to the
|
||||
# fontconfig configuration without changing the gtk3 tooltool package.
|
||||
|
@ -111,6 +111,13 @@ class MachCommands(MachCommandBase):
|
||||
'--show-possibly-lost=no',
|
||||
'--track-origins=yes',
|
||||
'--trace-children=yes',
|
||||
# The gstreamer plugin scanner can run as part of executing
|
||||
# firefox, but is an external program. In some weird cases,
|
||||
# valgrind finds errors while executing __libc_freeres when
|
||||
# it runs, but those are not relevant, as it's related to
|
||||
# executing third party code. So don't trace
|
||||
# gst-plugin-scanner.
|
||||
'--trace-children-skip=*/gst-plugin-scanner',
|
||||
'-v', # Enable verbosity to get the list of used suppressions
|
||||
]
|
||||
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "mozilla/dom/TextDecoder.h"
|
||||
#include "mozilla/dom/TouchEvent.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
@ -7973,3 +7974,22 @@ nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
|
||||
net::ReferrerPolicy referrerPolicy = aDoc->GetReferrerPolicy();
|
||||
return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
nsContentUtils::PushEnabled(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return Preferences::GetBool("dom.push.enabled", false);
|
||||
}
|
||||
|
||||
using namespace workers;
|
||||
|
||||
// Otherwise, check the pref via the WorkerPrivate
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
||||
if (!workerPrivate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return workerPrivate->PushEnabled();
|
||||
}
|
||||
|
@ -2439,6 +2439,8 @@ public:
|
||||
nsIDocument* aDoc,
|
||||
nsIHttpChannel* aChannel);
|
||||
|
||||
static bool PushEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
private:
|
||||
static bool InitializeEventTable();
|
||||
|
||||
|
@ -688,7 +688,11 @@ nsDOMMutationObserver::TakeRecords(
|
||||
for (uint32_t i = 0; i < mPendingMutationCount; ++i) {
|
||||
nsRefPtr<nsDOMMutationRecord> next;
|
||||
current->mNext.swap(next);
|
||||
*aRetVal.AppendElement() = current.forget();
|
||||
if (!mMergeAttributeRecords ||
|
||||
!MergeableAttributeRecord(aRetVal.SafeLastElement(nullptr),
|
||||
current)) {
|
||||
*aRetVal.AppendElement() = current.forget();
|
||||
}
|
||||
current.swap(next);
|
||||
}
|
||||
ClearPendingRecords();
|
||||
@ -745,6 +749,21 @@ nsDOMMutationObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
|
||||
return observer.forget();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
nsDOMMutationObserver::MergeableAttributeRecord(nsDOMMutationRecord* aOldRecord,
|
||||
nsDOMMutationRecord* aRecord)
|
||||
{
|
||||
MOZ_ASSERT(mMergeAttributeRecords);
|
||||
return
|
||||
aOldRecord &&
|
||||
aOldRecord->mType == nsGkAtoms::attributes &&
|
||||
aOldRecord->mType == aRecord->mType &&
|
||||
aOldRecord->mTarget == aRecord->mTarget &&
|
||||
aOldRecord->mAttrName == aRecord->mAttrName &&
|
||||
aOldRecord->mAttrNamespace.Equals(aRecord->mAttrNamespace);
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMMutationObserver::HandleMutation()
|
||||
{
|
||||
@ -776,7 +795,12 @@ nsDOMMutationObserver::HandleMutation()
|
||||
for (uint32_t i = 0; i < mPendingMutationCount; ++i) {
|
||||
nsRefPtr<nsDOMMutationRecord> next;
|
||||
current->mNext.swap(next);
|
||||
*mutations.AppendElement(mozilla::fallible) = current;
|
||||
if (!mMergeAttributeRecords ||
|
||||
!MergeableAttributeRecord(mutations.Length() ?
|
||||
mutations.LastElement().get() : nullptr,
|
||||
current)) {
|
||||
*mutations.AppendElement(mozilla::fallible) = current;
|
||||
}
|
||||
current.swap(next);
|
||||
}
|
||||
}
|
||||
|
@ -456,7 +456,8 @@ public:
|
||||
mozilla::dom::MutationCallback& aCb,
|
||||
bool aChrome)
|
||||
: mOwner(aOwner), mLastPendingMutation(nullptr), mPendingMutationCount(0),
|
||||
mCallback(&aCb), mWaitingForRun(false), mIsChrome(aChrome), mId(++sCount)
|
||||
mCallback(&aCb), mWaitingForRun(false), mIsChrome(aChrome),
|
||||
mMergeAttributeRecords(false), mId(++sCount)
|
||||
{
|
||||
}
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
@ -498,6 +499,22 @@ public:
|
||||
|
||||
mozilla::dom::MutationCallback* MutationCallback() { return mCallback; }
|
||||
|
||||
bool MergeAttributeRecords()
|
||||
{
|
||||
return mMergeAttributeRecords;
|
||||
}
|
||||
|
||||
void SetMergeAttributeRecords(bool aVal)
|
||||
{
|
||||
mMergeAttributeRecords = aVal;
|
||||
}
|
||||
|
||||
// If both records are for 'attributes' type and for the same target and
|
||||
// attribute name and namespace are the same, we can skip the newer record.
|
||||
// aOldRecord->mPrevValue holds the original value, if observed.
|
||||
bool MergeableAttributeRecord(nsDOMMutationRecord* aOldRecord,
|
||||
nsDOMMutationRecord* aRecord);
|
||||
|
||||
void AppendMutationRecord(already_AddRefed<nsDOMMutationRecord> aRecord)
|
||||
{
|
||||
nsRefPtr<nsDOMMutationRecord> record = aRecord;
|
||||
@ -585,6 +602,7 @@ protected:
|
||||
|
||||
bool mWaitingForRun;
|
||||
bool mIsChrome;
|
||||
bool mMergeAttributeRecords;
|
||||
|
||||
uint64_t mId;
|
||||
|
||||
|
@ -743,7 +743,7 @@ function testStyleRemoveProperty2() {
|
||||
observer.disconnect();
|
||||
m = null;
|
||||
div.removeAttribute("data-test");
|
||||
then();
|
||||
then(testAttributeRecordMerging1);
|
||||
});
|
||||
m.observe(div, { attributes: true });
|
||||
is(div.getAttribute("style"), null, "style attribute before unsuccessful removeProperty");
|
||||
@ -751,6 +751,167 @@ function testStyleRemoveProperty2() {
|
||||
div.setAttribute("data-test", "a");
|
||||
}
|
||||
|
||||
function testAttributeRecordMerging1() {
|
||||
ok(true, "testAttributeRecordMerging1");
|
||||
var m = new M(function(records, observer) {
|
||||
is(records.length, 2);
|
||||
is(records[0].type, "attributes");
|
||||
is(records[0].target, div);
|
||||
is(records[0].attributeName, "foo");
|
||||
is(records[0].attributeNamespace, null);
|
||||
is(records[0].oldValue, null);
|
||||
|
||||
is(records[1].type, "attributes");
|
||||
is(records[1].target, div.firstChild);
|
||||
is(records[1].attributeName, "foo");
|
||||
is(records[1].attributeNamespace, null);
|
||||
is(records[1].oldValue, null);
|
||||
observer.disconnect();
|
||||
div.innerHTML = "";
|
||||
div.removeAttribute("foo");
|
||||
then(testAttributeRecordMerging2);
|
||||
});
|
||||
m.observe(div, {
|
||||
attributes: true,
|
||||
subtree: true
|
||||
});
|
||||
SpecialPowers.wrap(m).mergeAttributeRecords = true;
|
||||
|
||||
div.setAttribute("foo", "bar_1");
|
||||
div.setAttribute("foo", "bar_2");
|
||||
div.innerHTML = "<div></div>";
|
||||
div.firstChild.setAttribute("foo", "bar_1");
|
||||
div.firstChild.setAttribute("foo", "bar_2");
|
||||
}
|
||||
|
||||
function testAttributeRecordMerging2() {
|
||||
ok(true, "testAttributeRecordMerging2");
|
||||
var m = new M(function(records, observer) {
|
||||
is(records.length, 2);
|
||||
is(records[0].type, "attributes");
|
||||
is(records[0].target, div);
|
||||
is(records[0].attributeName, "foo");
|
||||
is(records[0].attributeNamespace, null);
|
||||
is(records[0].oldValue, "initial");
|
||||
|
||||
is(records[1].type, "attributes");
|
||||
is(records[1].target, div.firstChild);
|
||||
is(records[1].attributeName, "foo");
|
||||
is(records[1].attributeNamespace, null);
|
||||
is(records[1].oldValue, "initial");
|
||||
observer.disconnect();
|
||||
div.innerHTML = "";
|
||||
div.removeAttribute("foo");
|
||||
then(testAttributeRecordMerging3);
|
||||
});
|
||||
|
||||
div.setAttribute("foo", "initial");
|
||||
div.innerHTML = "<div></div>";
|
||||
div.firstChild.setAttribute("foo", "initial");
|
||||
m.observe(div, {
|
||||
attributes: true,
|
||||
subtree: true,
|
||||
attributeOldValue: true
|
||||
});
|
||||
SpecialPowers.wrap(m).mergeAttributeRecords = true;
|
||||
|
||||
div.setAttribute("foo", "bar_1");
|
||||
div.setAttribute("foo", "bar_2");
|
||||
div.firstChild.setAttribute("foo", "bar_1");
|
||||
div.firstChild.setAttribute("foo", "bar_2");
|
||||
}
|
||||
|
||||
function testAttributeRecordMerging3() {
|
||||
ok(true, "testAttributeRecordMerging3");
|
||||
var m = new M(function(records, observer) {
|
||||
is(records.length, 4);
|
||||
is(records[0].type, "attributes");
|
||||
is(records[0].target, div);
|
||||
is(records[0].attributeName, "foo");
|
||||
is(records[0].attributeNamespace, null);
|
||||
is(records[0].oldValue, "initial");
|
||||
|
||||
is(records[1].type, "attributes");
|
||||
is(records[1].target, div.firstChild);
|
||||
is(records[1].attributeName, "foo");
|
||||
is(records[1].attributeNamespace, null);
|
||||
is(records[1].oldValue, "initial");
|
||||
|
||||
is(records[2].type, "attributes");
|
||||
is(records[2].target, div);
|
||||
is(records[2].attributeName, "foo");
|
||||
is(records[2].attributeNamespace, null);
|
||||
is(records[2].oldValue, "bar_1");
|
||||
|
||||
is(records[3].type, "attributes");
|
||||
is(records[3].target, div.firstChild);
|
||||
is(records[3].attributeName, "foo");
|
||||
is(records[3].attributeNamespace, null);
|
||||
is(records[3].oldValue, "bar_1");
|
||||
|
||||
observer.disconnect();
|
||||
div.innerHTML = "";
|
||||
div.removeAttribute("foo");
|
||||
then(testAttributeRecordMerging4);
|
||||
});
|
||||
|
||||
div.setAttribute("foo", "initial");
|
||||
div.innerHTML = "<div></div>";
|
||||
div.firstChild.setAttribute("foo", "initial");
|
||||
m.observe(div, {
|
||||
attributes: true,
|
||||
subtree: true,
|
||||
attributeOldValue: true
|
||||
});
|
||||
SpecialPowers.wrap(m).mergeAttributeRecords = true;
|
||||
|
||||
// No merging should happen.
|
||||
div.setAttribute("foo", "bar_1");
|
||||
div.firstChild.setAttribute("foo", "bar_1");
|
||||
div.setAttribute("foo", "bar_2");
|
||||
div.firstChild.setAttribute("foo", "bar_2");
|
||||
}
|
||||
|
||||
function testAttributeRecordMerging4() {
|
||||
ok(true, "testAttributeRecordMerging4");
|
||||
var m = new M(function(records, observer) {
|
||||
});
|
||||
|
||||
div.setAttribute("foo", "initial");
|
||||
div.innerHTML = "<div></div>";
|
||||
div.firstChild.setAttribute("foo", "initial");
|
||||
m.observe(div, {
|
||||
attributes: true,
|
||||
subtree: true,
|
||||
attributeOldValue: true
|
||||
});
|
||||
SpecialPowers.wrap(m).mergeAttributeRecords = true;
|
||||
|
||||
div.setAttribute("foo", "bar_1");
|
||||
div.setAttribute("foo", "bar_2");
|
||||
div.firstChild.setAttribute("foo", "bar_1");
|
||||
div.firstChild.setAttribute("foo", "bar_2");
|
||||
|
||||
var records = m.takeRecords();
|
||||
|
||||
is(records.length, 2);
|
||||
is(records[0].type, "attributes");
|
||||
is(records[0].target, div);
|
||||
is(records[0].attributeName, "foo");
|
||||
is(records[0].attributeNamespace, null);
|
||||
is(records[0].oldValue, "initial");
|
||||
|
||||
is(records[1].type, "attributes");
|
||||
is(records[1].target, div.firstChild);
|
||||
is(records[1].attributeName, "foo");
|
||||
is(records[1].attributeNamespace, null);
|
||||
is(records[1].oldValue, "initial");
|
||||
m.disconnect();
|
||||
div.innerHTML = "";
|
||||
div.removeAttribute("foo");
|
||||
then();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
@ -859,6 +859,9 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasRenderingContext2D)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(CanvasRenderingContext2D)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D)
|
||||
// Make sure we remove ourselves from the list of demotable contexts (raw pointers),
|
||||
// since we're logically destructed at this point.
|
||||
CanvasRenderingContext2D::RemoveDemotableContext(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement)
|
||||
for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
|
||||
ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::STROKE]);
|
||||
@ -1238,6 +1241,10 @@ void CanvasRenderingContext2D::Demote()
|
||||
std::vector<CanvasRenderingContext2D*>&
|
||||
CanvasRenderingContext2D::DemotableContexts()
|
||||
{
|
||||
// This is a list of raw pointers to cycle-collected objects. We need to ensure
|
||||
// that we remove elements from it during UNLINK (which can happen considerably before
|
||||
// the actual destructor) since the object is logically destroyed at that point
|
||||
// and will be in an inconsistant state.
|
||||
static std::vector<CanvasRenderingContext2D*> contexts;
|
||||
return contexts;
|
||||
}
|
||||
|
@ -685,6 +685,9 @@ protected:
|
||||
return CurrentState().font;
|
||||
}
|
||||
|
||||
// This function maintains a list of raw pointers to cycle-collected
|
||||
// objects. We need to ensure that no entries persist beyond unlink,
|
||||
// since the objects are logically destructed at that point.
|
||||
static std::vector<CanvasRenderingContext2D*>& DemotableContexts();
|
||||
static void DemoteOldestContextIfNecessary();
|
||||
|
||||
|
@ -58,9 +58,7 @@ static const WebGLExtensionID kNativelySupportedExtensions[] = {
|
||||
WebGLExtensionID::EXT_sRGB,
|
||||
WebGLExtensionID::OES_element_index_uint,
|
||||
WebGLExtensionID::OES_standard_derivatives,
|
||||
WebGLExtensionID::OES_texture_float,
|
||||
WebGLExtensionID::OES_texture_float_linear,
|
||||
WebGLExtensionID::OES_texture_half_float,
|
||||
WebGLExtensionID::OES_texture_half_float_linear,
|
||||
WebGLExtensionID::OES_vertex_array_object,
|
||||
WebGLExtensionID::WEBGL_depth_texture,
|
||||
|
@ -539,10 +539,10 @@ HasAcceleratedLayers(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
|
||||
}
|
||||
|
||||
static already_AddRefed<GLContext>
|
||||
CreateHeadlessNativeGL(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
bool requireCompatProfile, WebGLContext* webgl)
|
||||
CreateHeadlessNativeGL(CreateContextFlags flags, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
WebGLContext* webgl)
|
||||
{
|
||||
if (!forceEnabled &&
|
||||
if (!(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE) &&
|
||||
IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_OPENGL))
|
||||
{
|
||||
webgl->GenerateWarning("Refused to create native OpenGL context"
|
||||
@ -550,7 +550,7 @@ CreateHeadlessNativeGL(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<GLContext> gl = gl::GLContextProvider::CreateHeadless(requireCompatProfile);
|
||||
nsRefPtr<GLContext> gl = gl::GLContextProvider::CreateHeadless(flags);
|
||||
if (!gl) {
|
||||
webgl->GenerateWarning("Error during native OpenGL init.");
|
||||
return nullptr;
|
||||
@ -564,14 +564,13 @@ CreateHeadlessNativeGL(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
// right now, we get ANGLE implicitly by using EGL on Windows.
|
||||
// Eventually, we want to be able to pick ANGLE-EGL or native EGL.
|
||||
static already_AddRefed<GLContext>
|
||||
CreateHeadlessANGLE(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
bool requireCompatProfile, WebGLContext* webgl)
|
||||
CreateHeadlessANGLE(CreateContextFlags flags, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
WebGLContext* webgl)
|
||||
{
|
||||
nsRefPtr<GLContext> gl;
|
||||
|
||||
#ifdef XP_WIN
|
||||
gl = gl::GLContextProviderEGL::CreateHeadless(requireCompatProfile,
|
||||
forceEnabled);
|
||||
gl = gl::GLContextProviderEGL::CreateHeadless(flags);
|
||||
if (!gl) {
|
||||
webgl->GenerateWarning("Error during ANGLE OpenGL init.");
|
||||
return nullptr;
|
||||
@ -583,13 +582,12 @@ CreateHeadlessANGLE(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
}
|
||||
|
||||
static already_AddRefed<GLContext>
|
||||
CreateHeadlessEGL(bool forceEnabled, bool requireCompatProfile,
|
||||
WebGLContext* webgl)
|
||||
CreateHeadlessEGL(CreateContextFlags flags, WebGLContext* webgl)
|
||||
{
|
||||
nsRefPtr<GLContext> gl;
|
||||
|
||||
#ifdef ANDROID
|
||||
gl = gl::GLContextProviderEGL::CreateHeadless(requireCompatProfile);
|
||||
gl = gl::GLContextProviderEGL::CreateHeadless(flags);
|
||||
if (!gl) {
|
||||
webgl->GenerateWarning("Error during EGL OpenGL init.");
|
||||
return nullptr;
|
||||
@ -601,7 +599,7 @@ CreateHeadlessEGL(bool forceEnabled, bool requireCompatProfile,
|
||||
}
|
||||
|
||||
static already_AddRefed<GLContext>
|
||||
CreateHeadlessGL(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
CreateHeadlessGL(CreateContextFlags flags, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
WebGLContext* webgl)
|
||||
{
|
||||
bool preferEGL = PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
|
||||
@ -610,21 +608,21 @@ CreateHeadlessGL(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
||||
if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL"))
|
||||
disableANGLE = true;
|
||||
|
||||
bool requireCompatProfile = webgl->IsWebGL2() ? false : true;
|
||||
if (!webgl->IsWebGL2()) {
|
||||
flags |= CreateContextFlags::REQUIRE_COMPAT_PROFILE;
|
||||
}
|
||||
|
||||
nsRefPtr<GLContext> gl;
|
||||
|
||||
if (preferEGL)
|
||||
gl = CreateHeadlessEGL(forceEnabled, requireCompatProfile, webgl);
|
||||
gl = CreateHeadlessEGL(flags, webgl);
|
||||
|
||||
if (!gl && !disableANGLE) {
|
||||
gl = CreateHeadlessANGLE(forceEnabled, gfxInfo, requireCompatProfile,
|
||||
webgl);
|
||||
gl = CreateHeadlessANGLE(flags, gfxInfo, webgl);
|
||||
}
|
||||
|
||||
if (!gl) {
|
||||
gl = CreateHeadlessNativeGL(forceEnabled, gfxInfo,
|
||||
requireCompatProfile, webgl);
|
||||
gl = CreateHeadlessNativeGL(flags, gfxInfo, webgl);
|
||||
}
|
||||
|
||||
return gl.forget();
|
||||
@ -749,7 +747,10 @@ WebGLContext::CreateOffscreenGL(bool forceEnabled)
|
||||
}
|
||||
#endif
|
||||
|
||||
gl = CreateHeadlessGL(forceEnabled, gfxInfo, this);
|
||||
CreateContextFlags flags = forceEnabled ? CreateContextFlags::FORCE_ENABLE_HARDWARE :
|
||||
CreateContextFlags::NONE;
|
||||
|
||||
gl = CreateHeadlessGL(flags, gfxInfo, this);
|
||||
|
||||
do {
|
||||
if (!gl)
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "WebGLShader.h"
|
||||
#include "WebGLProgram.h"
|
||||
#include "WebGLUniformLocation.h"
|
||||
#include "WebGLFormats.h"
|
||||
#include "WebGLFramebuffer.h"
|
||||
#include "WebGLRenderbuffer.h"
|
||||
#include "WebGLShaderPrecisionFormat.h"
|
||||
@ -2270,42 +2271,16 @@ WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target,
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert DEPTH_STENCIL to sized type for testing
|
||||
GLenum sizedInternalFormat = internalFormat;
|
||||
if (sizedInternalFormat == LOCAL_GL_DEPTH_STENCIL)
|
||||
sizedInternalFormat = LOCAL_GL_DEPTH24_STENCIL8;
|
||||
|
||||
bool isFormatValid = false;
|
||||
switch (internalFormat) {
|
||||
case LOCAL_GL_RGBA4:
|
||||
case LOCAL_GL_RGB5_A1:
|
||||
case LOCAL_GL_RGB565:
|
||||
case LOCAL_GL_DEPTH_COMPONENT16:
|
||||
case LOCAL_GL_STENCIL_INDEX8:
|
||||
case LOCAL_GL_DEPTH_STENCIL:
|
||||
isFormatValid = true;
|
||||
break;
|
||||
|
||||
case LOCAL_GL_SRGB8_ALPHA8_EXT:
|
||||
if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB))
|
||||
isFormatValid = true;
|
||||
break;
|
||||
|
||||
case LOCAL_GL_RGB16F:
|
||||
case LOCAL_GL_RGBA16F:
|
||||
if (IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float) &&
|
||||
IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float))
|
||||
{
|
||||
isFormatValid = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCAL_GL_RGB32F:
|
||||
case LOCAL_GL_RGBA32F:
|
||||
if (IsExtensionEnabled(WebGLExtensionID::OES_texture_float) &&
|
||||
IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float))
|
||||
{
|
||||
isFormatValid = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
const webgl::FormatInfo* info = webgl::GetInfoBySizedFormat(sizedInternalFormat);
|
||||
if (info) {
|
||||
const webgl::FormatUsageInfo* usage = mFormatUsage->GetUsage(info);
|
||||
isFormatValid = usage && usage->asRenderbuffer;
|
||||
}
|
||||
|
||||
if (!isFormatValid) {
|
||||
|
@ -7,13 +7,34 @@
|
||||
#include "GLContext.h"
|
||||
#include "mozilla/dom/WebGLRenderingContextBinding.h"
|
||||
#include "WebGLContext.h"
|
||||
#include "WebGLFormats.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using mozilla::webgl::EffectiveFormat;
|
||||
|
||||
WebGLExtensionColorBufferFloat::WebGLExtensionColorBufferFloat(WebGLContext* webgl)
|
||||
: WebGLExtensionBase(webgl)
|
||||
{
|
||||
MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
|
||||
|
||||
webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
|
||||
|
||||
auto updateUsage = [authority](EffectiveFormat effectiveFormat) {
|
||||
webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
|
||||
MOZ_ASSERT(usage);
|
||||
usage->asRenderbuffer = usage->isRenderable = true;
|
||||
};
|
||||
|
||||
// Ensure require formats are initialized.
|
||||
WebGLExtensionTextureFloat::InitWebGLFormats(authority);
|
||||
|
||||
// Update usage to allow asRenderbuffer and isRenderable
|
||||
updateUsage(EffectiveFormat::RGBA32F);
|
||||
updateUsage(EffectiveFormat::RGB32F);
|
||||
updateUsage(EffectiveFormat::Luminance32FAlpha32F);
|
||||
updateUsage(EffectiveFormat::Luminance32F);
|
||||
updateUsage(EffectiveFormat::Alpha32F);
|
||||
}
|
||||
|
||||
WebGLExtensionColorBufferFloat::~WebGLExtensionColorBufferFloat()
|
||||
|
@ -7,13 +7,34 @@
|
||||
#include "GLContext.h"
|
||||
#include "mozilla/dom/WebGLRenderingContextBinding.h"
|
||||
#include "WebGLContext.h"
|
||||
#include "WebGLFormats.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using mozilla::webgl::EffectiveFormat;
|
||||
|
||||
WebGLExtensionColorBufferHalfFloat::WebGLExtensionColorBufferHalfFloat(WebGLContext* webgl)
|
||||
: WebGLExtensionBase(webgl)
|
||||
{
|
||||
MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
|
||||
|
||||
webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
|
||||
|
||||
auto updateUsage = [authority](EffectiveFormat effectiveFormat) {
|
||||
webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
|
||||
MOZ_ASSERT(usage);
|
||||
usage->asRenderbuffer = usage->isRenderable = true;
|
||||
};
|
||||
|
||||
// Ensure require formats are initialized.
|
||||
WebGLExtensionTextureHalfFloat::InitWebGLFormats(authority);
|
||||
|
||||
// Update usage to allow asRenderbuffer and isRenderable
|
||||
updateUsage(EffectiveFormat::RGBA16F);
|
||||
updateUsage(EffectiveFormat::RGB16F);
|
||||
updateUsage(EffectiveFormat::Luminance16FAlpha16F);
|
||||
updateUsage(EffectiveFormat::Luminance16F);
|
||||
updateUsage(EffectiveFormat::Alpha16F);
|
||||
}
|
||||
|
||||
WebGLExtensionColorBufferHalfFloat::~WebGLExtensionColorBufferHalfFloat()
|
||||
|
@ -8,9 +8,13 @@
|
||||
#include "GLContext.h"
|
||||
#include "mozilla/dom/WebGLRenderingContextBinding.h"
|
||||
#include "WebGLContext.h"
|
||||
#include "WebGLFormats.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using mozilla::webgl::EffectiveFormat;
|
||||
|
||||
|
||||
WebGLExtensionSRGB::WebGLExtensionSRGB(WebGLContext* webgl)
|
||||
: WebGLExtensionBase(webgl)
|
||||
{
|
||||
@ -23,6 +27,21 @@ WebGLExtensionSRGB::WebGLExtensionSRGB(WebGLContext* webgl)
|
||||
gl->MakeCurrent();
|
||||
gl->fEnable(LOCAL_GL_FRAMEBUFFER_SRGB_EXT);
|
||||
}
|
||||
|
||||
webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
|
||||
|
||||
auto addFormatIfMissing = [authority](EffectiveFormat effectiveFormat,
|
||||
GLenum unpackFormat, GLenum unpackType,
|
||||
bool asRenderbuffer)
|
||||
{
|
||||
if (!authority->GetUsage(effectiveFormat)) {
|
||||
authority->AddFormat(effectiveFormat, asRenderbuffer, asRenderbuffer, true, true);
|
||||
authority->AddUnpackOption(unpackFormat, unpackType, effectiveFormat);
|
||||
}
|
||||
};
|
||||
|
||||
addFormatIfMissing(EffectiveFormat::SRGB8 , LOCAL_GL_SRGB , LOCAL_GL_UNSIGNED_BYTE, false);
|
||||
addFormatIfMissing(EffectiveFormat::SRGB8_ALPHA8, LOCAL_GL_SRGB_ALPHA, LOCAL_GL_UNSIGNED_BYTE, true);
|
||||
}
|
||||
|
||||
WebGLExtensionSRGB::~WebGLExtensionSRGB()
|
||||
|
@ -6,12 +6,55 @@
|
||||
|
||||
#include "mozilla/dom/WebGLRenderingContextBinding.h"
|
||||
#include "WebGLContext.h"
|
||||
#include "WebGLFormats.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using mozilla::webgl::EffectiveFormat;
|
||||
|
||||
void
|
||||
WebGLExtensionTextureFloat::InitWebGLFormats(webgl::FormatUsageAuthority* authority)
|
||||
{
|
||||
MOZ_ASSERT(authority);
|
||||
|
||||
auto addFormatIfMissing = [authority](EffectiveFormat effectiveFormat)
|
||||
{
|
||||
if (!authority->GetUsage(effectiveFormat)) {
|
||||
authority->AddFormat(effectiveFormat, false, false, false, false);
|
||||
}
|
||||
};
|
||||
|
||||
// Populate authority with any missing effective formats.
|
||||
addFormatIfMissing(EffectiveFormat::RGBA32F);
|
||||
addFormatIfMissing(EffectiveFormat::RGB32F);
|
||||
addFormatIfMissing(EffectiveFormat::Luminance32FAlpha32F);
|
||||
addFormatIfMissing(EffectiveFormat::Luminance32F);
|
||||
addFormatIfMissing(EffectiveFormat::Alpha32F);
|
||||
}
|
||||
|
||||
WebGLExtensionTextureFloat::WebGLExtensionTextureFloat(WebGLContext* webgl)
|
||||
: WebGLExtensionBase(webgl)
|
||||
{
|
||||
webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
|
||||
|
||||
auto updateUsage = [authority](EffectiveFormat effectiveFormat,
|
||||
GLenum unpackFormat, GLenum unpackType)
|
||||
{
|
||||
webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
|
||||
MOZ_ASSERT(usage);
|
||||
usage->asTexture = true;
|
||||
authority->AddUnpackOption(unpackFormat, unpackType, effectiveFormat);
|
||||
};
|
||||
|
||||
// Ensure require formats are initialized.
|
||||
InitWebGLFormats(authority);
|
||||
|
||||
// Update usage to allow asTexture and add unpack
|
||||
updateUsage(EffectiveFormat::RGBA32F , LOCAL_GL_RGBA , LOCAL_GL_FLOAT);
|
||||
updateUsage(EffectiveFormat::RGB32F , LOCAL_GL_RGB , LOCAL_GL_FLOAT);
|
||||
updateUsage(EffectiveFormat::Luminance32FAlpha32F, LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_FLOAT);
|
||||
updateUsage(EffectiveFormat::Luminance32F , LOCAL_GL_LUMINANCE , LOCAL_GL_FLOAT);
|
||||
updateUsage(EffectiveFormat::Alpha32F , LOCAL_GL_ALPHA , LOCAL_GL_FLOAT);
|
||||
}
|
||||
|
||||
WebGLExtensionTextureFloat::~WebGLExtensionTextureFloat()
|
||||
|
@ -6,12 +6,36 @@
|
||||
|
||||
#include "mozilla/dom/WebGLRenderingContextBinding.h"
|
||||
#include "WebGLContext.h"
|
||||
#include "WebGLFormats.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using mozilla::webgl::EffectiveFormat;
|
||||
|
||||
WebGLExtensionTextureFloatLinear::WebGLExtensionTextureFloatLinear(WebGLContext* webgl)
|
||||
: WebGLExtensionBase(webgl)
|
||||
{
|
||||
// This update requires that the authority already be populated by
|
||||
// WebGLExtensionTextureFloat. Enabling extensions to control
|
||||
// features is a mess in WebGL
|
||||
|
||||
webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
|
||||
|
||||
auto updateUsage = [authority](EffectiveFormat effectiveFormat) {
|
||||
webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
|
||||
MOZ_ASSERT(usage);
|
||||
usage->isFilterable = true;
|
||||
};
|
||||
|
||||
// Ensure require formats are initialized.
|
||||
WebGLExtensionTextureFloat::InitWebGLFormats(authority);
|
||||
|
||||
// Update usage to allow isFilterable
|
||||
updateUsage(EffectiveFormat::RGBA32F);
|
||||
updateUsage(EffectiveFormat::RGB32F);
|
||||
updateUsage(EffectiveFormat::Luminance32FAlpha32F);
|
||||
updateUsage(EffectiveFormat::Luminance32F);
|
||||
updateUsage(EffectiveFormat::Alpha32F);
|
||||
}
|
||||
|
||||
WebGLExtensionTextureFloatLinear::~WebGLExtensionTextureFloatLinear()
|
||||
|
@ -6,12 +6,54 @@
|
||||
|
||||
#include "mozilla/dom/WebGLRenderingContextBinding.h"
|
||||
#include "WebGLContext.h"
|
||||
#include "WebGLFormats.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using mozilla::webgl::EffectiveFormat;
|
||||
|
||||
void
|
||||
WebGLExtensionTextureHalfFloat::InitWebGLFormats(webgl::FormatUsageAuthority* authority)
|
||||
{
|
||||
MOZ_ASSERT(authority);
|
||||
|
||||
auto addFormatIfMissing = [authority](EffectiveFormat effectiveFormat)
|
||||
{
|
||||
if (!authority->GetUsage(effectiveFormat)) {
|
||||
authority->AddFormat(effectiveFormat, false, false, false, false);
|
||||
}
|
||||
};
|
||||
|
||||
// Populate authority with any missing effective formats.
|
||||
addFormatIfMissing(EffectiveFormat::RGBA16F);
|
||||
addFormatIfMissing(EffectiveFormat::RGB16F);
|
||||
addFormatIfMissing(EffectiveFormat::Luminance16FAlpha16F);
|
||||
addFormatIfMissing(EffectiveFormat::Luminance16F);
|
||||
addFormatIfMissing(EffectiveFormat::Alpha16F);
|
||||
}
|
||||
|
||||
WebGLExtensionTextureHalfFloat::WebGLExtensionTextureHalfFloat(WebGLContext* webgl)
|
||||
: WebGLExtensionBase(webgl)
|
||||
{
|
||||
webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
|
||||
|
||||
auto updateUsage = [authority](EffectiveFormat effectiveFormat,
|
||||
GLenum unpackFormat, GLenum unpackType)
|
||||
{
|
||||
webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
|
||||
MOZ_ASSERT(usage);
|
||||
usage->asTexture = true;
|
||||
authority->AddUnpackOption(unpackFormat, unpackType, effectiveFormat);
|
||||
};
|
||||
|
||||
InitWebGLFormats(authority);
|
||||
|
||||
// Update usage to allow asTexture and add unpack
|
||||
updateUsage(EffectiveFormat::RGBA16F , LOCAL_GL_RGBA , LOCAL_GL_HALF_FLOAT_OES);
|
||||
updateUsage(EffectiveFormat::RGB16F , LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT_OES);
|
||||
updateUsage(EffectiveFormat::Luminance16FAlpha16F, LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT_OES);
|
||||
updateUsage(EffectiveFormat::Luminance16F , LOCAL_GL_LUMINANCE , LOCAL_GL_HALF_FLOAT_OES);
|
||||
updateUsage(EffectiveFormat::Alpha16F , LOCAL_GL_ALPHA , LOCAL_GL_HALF_FLOAT_OES);
|
||||
}
|
||||
|
||||
WebGLExtensionTextureHalfFloat::~WebGLExtensionTextureHalfFloat()
|
||||
|
@ -6,12 +6,36 @@
|
||||
|
||||
#include "mozilla/dom/WebGLRenderingContextBinding.h"
|
||||
#include "WebGLContext.h"
|
||||
#include "WebGLFormats.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using mozilla::webgl::EffectiveFormat;
|
||||
|
||||
WebGLExtensionTextureHalfFloatLinear::WebGLExtensionTextureHalfFloatLinear(WebGLContext* webgl)
|
||||
: WebGLExtensionBase(webgl)
|
||||
{
|
||||
// This update requires that the authority already be populated by
|
||||
// WebGLExtensionTextureHalfFloat. Enabling extensions to control
|
||||
// features is a mess in WebGL
|
||||
|
||||
webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get();
|
||||
|
||||
auto updateUsage = [authority](EffectiveFormat effectiveFormat) {
|
||||
webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat);
|
||||
MOZ_ASSERT(usage);
|
||||
usage->isFilterable = true;
|
||||
};
|
||||
|
||||
// Ensure require formats are initialized.
|
||||
WebGLExtensionTextureHalfFloat::InitWebGLFormats(authority);
|
||||
|
||||
// Update usage to allow isFilterable
|
||||
updateUsage(EffectiveFormat::RGBA16F);
|
||||
updateUsage(EffectiveFormat::RGB16F);
|
||||
updateUsage(EffectiveFormat::Luminance16FAlpha16F);
|
||||
updateUsage(EffectiveFormat::Luminance16F);
|
||||
updateUsage(EffectiveFormat::Alpha16F);
|
||||
}
|
||||
|
||||
WebGLExtensionTextureHalfFloatLinear::~WebGLExtensionTextureHalfFloatLinear()
|
||||
|
@ -20,6 +20,10 @@ template<typename T>
|
||||
class Sequence;
|
||||
} // namespace dom
|
||||
|
||||
namespace webgl {
|
||||
class FormatUsageAuthority;
|
||||
} // namespace webgl
|
||||
|
||||
class WebGLContext;
|
||||
class WebGLShader;
|
||||
class WebGLQuery;
|
||||
@ -210,6 +214,8 @@ class WebGLExtensionTextureFloat
|
||||
: public WebGLExtensionBase
|
||||
{
|
||||
public:
|
||||
static void InitWebGLFormats(webgl::FormatUsageAuthority* authority);
|
||||
|
||||
explicit WebGLExtensionTextureFloat(WebGLContext*);
|
||||
virtual ~WebGLExtensionTextureFloat();
|
||||
|
||||
@ -230,6 +236,8 @@ class WebGLExtensionTextureHalfFloat
|
||||
: public WebGLExtensionBase
|
||||
{
|
||||
public:
|
||||
static void InitWebGLFormats(webgl::FormatUsageAuthority* authority);
|
||||
|
||||
explicit WebGLExtensionTextureHalfFloat(WebGLContext*);
|
||||
virtual ~WebGLExtensionTextureHalfFloat();
|
||||
|
||||
|
@ -268,6 +268,16 @@ InitFormatInfoMap()
|
||||
// OES_compressed_ETC1_RGB8_texture
|
||||
AddFormatInfo(FOO(ETC1_RGB8), 0, UnsizedFormat::RGB, ComponentType::NormUInt);
|
||||
|
||||
// OES_texture_float
|
||||
AddFormatInfo(FOO(Luminance32FAlpha32F), 2, UnsizedFormat::LA, ComponentType::Float);
|
||||
AddFormatInfo(FOO(Luminance32F ), 1, UnsizedFormat::L , ComponentType::Float);
|
||||
AddFormatInfo(FOO(Alpha32F ), 1, UnsizedFormat::A , ComponentType::Float);
|
||||
|
||||
// OES_texture_half_float
|
||||
AddFormatInfo(FOO(Luminance16FAlpha16F), 2, UnsizedFormat::LA, ComponentType::Float);
|
||||
AddFormatInfo(FOO(Luminance16F ), 1, UnsizedFormat::L , ComponentType::Float);
|
||||
AddFormatInfo(FOO(Alpha16F ), 1, UnsizedFormat::A , ComponentType::Float);
|
||||
|
||||
#undef FOO
|
||||
}
|
||||
|
||||
@ -296,14 +306,24 @@ InitUnpackTupleMap()
|
||||
AddUnpackTuple(LOCAL_GL_LUMINANCE , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8 );
|
||||
AddUnpackTuple(LOCAL_GL_ALPHA , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Alpha8 );
|
||||
|
||||
AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_FLOAT , EffectiveFormat::RGB32F );
|
||||
AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_FLOAT , EffectiveFormat::RGBA32F);
|
||||
AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT, EffectiveFormat::RGB16F );
|
||||
AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT, EffectiveFormat::RGBA16F);
|
||||
AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_FLOAT, EffectiveFormat::RGB32F );
|
||||
AddUnpackTuple(LOCAL_GL_RGBA , LOCAL_GL_FLOAT, EffectiveFormat::RGBA32F);
|
||||
AddUnpackTuple(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_FLOAT, EffectiveFormat::Luminance32FAlpha32F);
|
||||
AddUnpackTuple(LOCAL_GL_LUMINANCE , LOCAL_GL_FLOAT, EffectiveFormat::Luminance32F);
|
||||
AddUnpackTuple(LOCAL_GL_ALPHA , LOCAL_GL_FLOAT, EffectiveFormat::Alpha32F);
|
||||
|
||||
AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT, EffectiveFormat::RGB16F );
|
||||
AddUnpackTuple(LOCAL_GL_RGBA , LOCAL_GL_HALF_FLOAT, EffectiveFormat::RGBA16F);
|
||||
AddUnpackTuple(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT, EffectiveFormat::Luminance16FAlpha16F);
|
||||
AddUnpackTuple(LOCAL_GL_LUMINANCE , LOCAL_GL_HALF_FLOAT, EffectiveFormat::Luminance16F);
|
||||
AddUnpackTuple(LOCAL_GL_ALPHA , LOCAL_GL_HALF_FLOAT, EffectiveFormat::Alpha16F);
|
||||
|
||||
// Everyone's favorite problem-child:
|
||||
AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGB16F );
|
||||
AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGBA16F);
|
||||
AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGB16F );
|
||||
AddUnpackTuple(LOCAL_GL_RGBA , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGBA16F);
|
||||
AddUnpackTuple(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::Luminance16FAlpha16F);
|
||||
AddUnpackTuple(LOCAL_GL_LUMINANCE , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::Luminance16F);
|
||||
AddUnpackTuple(LOCAL_GL_ALPHA , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::Alpha16F);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -747,7 +767,7 @@ FormatUsageAuthority::CreateForWebGL2()
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FormatUsageInfo*
|
||||
FormatUsageAuthority::GetInfo(EffectiveFormat format)
|
||||
FormatUsageAuthority::GetUsage(EffectiveFormat format)
|
||||
{
|
||||
auto itr = mInfoMap.find(format);
|
||||
|
||||
@ -777,7 +797,7 @@ FormatUsageAuthority::AddUnpackOption(GLenum unpackFormat, GLenum unpackType,
|
||||
EffectiveFormat effectiveFormat)
|
||||
{
|
||||
const UnpackTuple unpack = { unpackFormat, unpackType };
|
||||
FormatUsageInfo* usage = GetInfo(effectiveFormat);
|
||||
FormatUsageInfo* usage = GetUsage(effectiveFormat);
|
||||
MOZ_RELEASE_ASSERT(usage);
|
||||
if (!usage)
|
||||
return;
|
||||
@ -789,7 +809,5 @@ FormatUsageAuthority::AddUnpackOption(GLenum unpackFormat, GLenum unpackType,
|
||||
MOZ_ALWAYS_TRUE(didInsert);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // namespace webgl
|
||||
} // namespace mozilla
|
||||
|
@ -131,6 +131,16 @@ enum class EffectiveFormat : EffectiveFormatValueT {
|
||||
// OES_compressed_ETC1_RGB8_texture
|
||||
ETC1_RGB8,
|
||||
|
||||
// OES_texture_float
|
||||
Luminance32FAlpha32F,
|
||||
Luminance32F,
|
||||
Alpha32F,
|
||||
|
||||
// OES_texture_half_float
|
||||
Luminance16FAlpha16F,
|
||||
Luminance16F,
|
||||
Alpha16F,
|
||||
|
||||
MAX,
|
||||
};
|
||||
|
||||
@ -241,16 +251,14 @@ public:
|
||||
void AddUnpackOption(GLenum unpackFormat, GLenum unpackType,
|
||||
EffectiveFormat effectiveFormat);
|
||||
|
||||
FormatUsageInfo* GetInfo(EffectiveFormat format);
|
||||
FormatUsageInfo* GetUsage(EffectiveFormat format);
|
||||
|
||||
FormatUsageInfo* GetInfo(const FormatInfo* format)
|
||||
FormatUsageInfo* GetUsage(const FormatInfo* format)
|
||||
{
|
||||
return GetInfo(format->effectiveFormat);
|
||||
return GetUsage(format->effectiveFormat);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
} // namespace webgl
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -186,7 +186,7 @@ protected:
|
||||
return true;
|
||||
}
|
||||
|
||||
mGLContext = GLContextProvider::CreateHeadless(false);
|
||||
mGLContext = GLContextProvider::CreateHeadless(CreateContextFlags::NONE);
|
||||
return mGLContext;
|
||||
}
|
||||
|
||||
|
@ -69,8 +69,6 @@ class ScopeCheckingGetCallback : public nsINotificationStorageCallback
|
||||
{
|
||||
const nsString mScope;
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
explicit ScopeCheckingGetCallback(const nsAString& aScope)
|
||||
: mScope(aScope)
|
||||
{}
|
||||
@ -121,8 +119,6 @@ protected:
|
||||
nsTArray<NotificationStrings> mStrings;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ScopeCheckingGetCallback, nsINotificationStorageCallback)
|
||||
|
||||
class NotificationStorageCallback final : public ScopeCheckingGetCallback
|
||||
{
|
||||
public:
|
||||
@ -190,7 +186,9 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationStorageCallback)
|
||||
NS_IMPL_CYCLE_COLLECTION(NotificationStorageCallback, mWindow, mPromise);
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationStorageCallback)
|
||||
NS_INTERFACE_MAP_END_INHERITING(ScopeCheckingGetCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsINotificationStorageCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
class NotificationGetRunnable final : public nsRunnable
|
||||
{
|
||||
@ -1766,7 +1764,7 @@ class WorkerGetCallback final : public ScopeCheckingGetCallback
|
||||
{
|
||||
nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
WorkerGetCallback(PromiseWorkerProxy* aProxy, const nsAString& aScope)
|
||||
: ScopeCheckingGetCallback(aScope), mPromiseProxy(aProxy)
|
||||
@ -1808,7 +1806,7 @@ private:
|
||||
{}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(WorkerGetCallback, ScopeCheckingGetCallback)
|
||||
NS_IMPL_ISUPPORTS(WorkerGetCallback, nsINotificationStorageCallback)
|
||||
|
||||
class WorkerGetRunnable final : public nsRunnable
|
||||
{
|
||||
|
@ -92,8 +92,7 @@ static nsRefPtr<GLContext> sPluginContext = nullptr;
|
||||
static bool EnsureGLContext()
|
||||
{
|
||||
if (!sPluginContext) {
|
||||
bool requireCompatProfile = true;
|
||||
sPluginContext = GLContextProvider::CreateHeadless(requireCompatProfile);
|
||||
sPluginContext = GLContextProvider::CreateHeadless(CreateContextFlags::REQUIRE_COMPAT_PROFILE);
|
||||
}
|
||||
|
||||
return sPluginContext != nullptr;
|
||||
|
@ -129,23 +129,6 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushSubscription)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
// PushManager
|
||||
// static
|
||||
bool
|
||||
PushManager::Enabled(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return Preferences::GetBool("dom.push.enabled", false);
|
||||
}
|
||||
|
||||
// XXXnsm: As of this patch it seems like Push will be enabled before or with
|
||||
// ServiceWorkers, so this seems OK for now.
|
||||
ServiceWorkerGlobalScope* scope = nullptr;
|
||||
nsresult rv = UnwrapObject<prototypes::id::ServiceWorkerGlobalScope_workers,
|
||||
mozilla::dom::ServiceWorkerGlobalScopeBinding_workers::NativeType>(aObj, scope);
|
||||
return NS_SUCCEEDED(rv);
|
||||
}
|
||||
|
||||
PushManager::PushManager(nsIGlobalObject* aGlobal, const nsAString& aScope)
|
||||
: mGlobal(aGlobal), mScope(aScope)
|
||||
{
|
||||
|
@ -106,9 +106,6 @@ public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushManager)
|
||||
|
||||
static bool
|
||||
Enabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
explicit PushManager(nsIGlobalObject* aGlobal, const nsAString& aScope);
|
||||
|
||||
nsIGlobalObject*
|
||||
|
@ -47,6 +47,8 @@ interface MutationObserver {
|
||||
sequence<MutationObservingInfo?> getObservingInfo();
|
||||
[ChromeOnly]
|
||||
readonly attribute MutationCallback mutationCallback;
|
||||
[ChromeOnly]
|
||||
attribute boolean mergeAttributeRecords;
|
||||
};
|
||||
|
||||
callback MutationCallback = void (sequence<MutationRecord> mutations, MutationObserver observer);
|
||||
|
@ -7,7 +7,9 @@
|
||||
* https://w3c.github.io/push-api/
|
||||
*/
|
||||
|
||||
[Constructor(DOMString type, optional PushEventInit eventInitDict), Exposed=ServiceWorker]
|
||||
[Constructor(DOMString type, optional PushEventInit eventInitDict),
|
||||
Func="nsContentUtils::PushEnabled",
|
||||
Exposed=ServiceWorker]
|
||||
interface PushEvent : ExtendableEvent {
|
||||
readonly attribute PushMessageData data;
|
||||
};
|
||||
@ -16,4 +18,4 @@ typedef USVString PushMessageDataInit;
|
||||
|
||||
dictionary PushEventInit : ExtendableEventInit {
|
||||
PushMessageDataInit data;
|
||||
};
|
||||
};
|
||||
|
@ -22,7 +22,7 @@ interface PushManagerImpl {
|
||||
[Func="ServiceWorkerRegistration::WebPushMethodHider"] void setScope(DOMString scope);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker), Func="mozilla::dom::PushManager::Enabled"]
|
||||
[Exposed=(Window,Worker), Func="nsContentUtils::PushEnabled"]
|
||||
interface PushManager {
|
||||
[ChromeOnly, Throws, Exposed=Window]
|
||||
void setPushManagerImpl(PushManagerImpl store);
|
||||
|
@ -7,7 +7,8 @@
|
||||
* https://w3c.github.io/push-api/
|
||||
*/
|
||||
|
||||
[Exposed=ServiceWorker]
|
||||
[Func="nsContentUtils::PushEnabled",
|
||||
Exposed=ServiceWorker]
|
||||
interface PushMessageData
|
||||
{
|
||||
// FIXME(nsm): Bug 1149195.
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
interface Principal;
|
||||
|
||||
[Exposed=(Window,Worker), Func="mozilla::dom::PushManager::Enabled",
|
||||
[Exposed=(Window,Worker), Func="nsContentUtils::PushEnabled",
|
||||
ChromeConstructor(DOMString pushEndpoint, DOMString scope)]
|
||||
interface PushSubscription
|
||||
{
|
||||
|
@ -28,7 +28,7 @@ interface ServiceWorkerRegistration : EventTarget {
|
||||
|
||||
partial interface ServiceWorkerRegistration {
|
||||
#ifndef MOZ_SIMPLEPUSH
|
||||
[Throws, Exposed=(Window,Worker), Func="mozilla::dom::PushManager::Enabled"]
|
||||
[Throws, Exposed=(Window,Worker), Func="nsContentUtils::PushEnabled"]
|
||||
readonly attribute PushManager pushManager;
|
||||
#endif
|
||||
};
|
||||
|
@ -166,6 +166,7 @@ static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
|
||||
#define PREF_SERVICEWORKERS_TESTING_ENABLED "dom.serviceWorkers.testing.enabled"
|
||||
#define PREF_INTERCEPTION_ENABLED "dom.serviceWorkers.interception.enabled"
|
||||
#define PREF_INTERCEPTION_OPAQUE_ENABLED "dom.serviceWorkers.interception.opaque.enabled"
|
||||
#define PREF_PUSH_ENABLED "dom.push.enabled"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -1949,6 +1950,10 @@ RuntimeService::Init()
|
||||
WorkerPrefChanged,
|
||||
PREF_SERVICEWORKERS_TESTING_ENABLED,
|
||||
reinterpret_cast<void *>(WORKERPREF_SERVICEWORKERS_TESTING))) ||
|
||||
NS_FAILED(Preferences::RegisterCallbackAndCall(
|
||||
WorkerPrefChanged,
|
||||
PREF_PUSH_ENABLED,
|
||||
reinterpret_cast<void *>(WORKERPREF_PUSH))) ||
|
||||
NS_FAILED(Preferences::RegisterCallback(LoadRuntimeOptions,
|
||||
PREF_JS_OPTIONS_PREFIX,
|
||||
nullptr)) ||
|
||||
@ -2176,6 +2181,10 @@ RuntimeService::Cleanup()
|
||||
WorkerPrefChanged,
|
||||
PREF_DOM_WORKERNOTIFICATION_ENABLED,
|
||||
reinterpret_cast<void *>(WORKERPREF_DOM_WORKERNOTIFICATION))) ||
|
||||
NS_FAILED(Preferences::UnregisterCallback(
|
||||
WorkerPrefChanged,
|
||||
PREF_PUSH_ENABLED,
|
||||
reinterpret_cast<void *>(WORKERPREF_PUSH))) ||
|
||||
#if DUMP_CONTROLLED_BY_PREF
|
||||
NS_FAILED(Preferences::UnregisterCallback(
|
||||
WorkerPrefChanged,
|
||||
@ -2725,6 +2734,7 @@ RuntimeService::WorkerPrefChanged(const char* aPrefName, void* aClosure)
|
||||
case WORKERPREF_INTERCEPTION_OPAQUE_ENABLED:
|
||||
case WORKERPREF_SERVICEWORKERS:
|
||||
case WORKERPREF_SERVICEWORKERS_TESTING:
|
||||
case WORKERPREF_PUSH:
|
||||
sDefaultPreferences[key] = Preferences::GetBool(aPrefName, false);
|
||||
break;
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#endif
|
||||
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
class nsIInterceptedChannel;
|
||||
|
||||
|
@ -1328,6 +1328,13 @@ public:
|
||||
return mPreferences[WORKERPREF_PERFORMANCE_LOGGING_ENABLED];
|
||||
}
|
||||
|
||||
bool
|
||||
PushEnabled() const
|
||||
{
|
||||
AssertIsOnWorkerThread();
|
||||
return mPreferences[WORKERPREF_PUSH];
|
||||
}
|
||||
|
||||
bool
|
||||
OnLine() const
|
||||
{
|
||||
|
@ -206,6 +206,7 @@ enum WorkerPreference
|
||||
WORKERPREF_SERVICEWORKERS_TESTING, // dom.serviceWorkers.testing.enabled
|
||||
WORKERPREF_INTERCEPTION_OPAQUE_ENABLED, // dom.serviceWorkers.interception.opaque.enabled
|
||||
WORKERPREF_PERFORMANCE_LOGGING_ENABLED, // dom.performance.enable_user_timing_logging
|
||||
WORKERPREF_PUSH, // dom.push.enabled
|
||||
WORKERPREF_COUNT
|
||||
};
|
||||
|
||||
|
@ -48,8 +48,6 @@ var ecmaGlobals =
|
||||
"Number",
|
||||
"Object",
|
||||
"Proxy",
|
||||
"PushEvent",
|
||||
"PushMessageData",
|
||||
"RangeError",
|
||||
"ReferenceError",
|
||||
"Reflect",
|
||||
@ -174,9 +172,13 @@ var interfaceNamesInGlobalScope =
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Promise",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"PushManager",
|
||||
{ name: "PushEvent", b2g: false, android: false, release: false },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"PushSubscription",
|
||||
{ name: "PushManager", b2g: false, android: false, release: false },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "PushMessageData", b2g: false, android: false, release: false },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "PushSubscription", b2g: false, android: false, release: false },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Request",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
@ -163,6 +163,10 @@ var interfaceNamesInGlobalScope =
|
||||
"PerformanceMeasure",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Promise",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "PushManager", b2g: false, android: false, release: false },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "PushSubscription", b2g: false, android: false, release: false },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Request",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
@ -430,11 +430,11 @@ DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface,
|
||||
|
||||
SkPaint paint;
|
||||
|
||||
SkImageFilter* filter = SkDropShadowImageFilter::Create(aOffset.x, aOffset.y,
|
||||
aSigma, aSigma,
|
||||
ColorToSkColor(aColor, 1.0));
|
||||
SkAutoTUnref<SkImageFilter> filter(SkDropShadowImageFilter::Create(aOffset.x, aOffset.y,
|
||||
aSigma, aSigma,
|
||||
ColorToSkColor(aColor, 1.0)));
|
||||
|
||||
paint.setImageFilter(filter);
|
||||
paint.setImageFilter(filter.get());
|
||||
paint.setXfermodeMode(GfxOpToSkiaOp(aOperator));
|
||||
|
||||
mCanvas->drawBitmap(bitmap.mBitmap, aDest.x, aDest.y, &paint);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "GLContextTypes.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "SurfaceTypes.h"
|
||||
#include "mozilla/TypedEnumBits.h"
|
||||
|
||||
#include "nsSize.h" // for gfx::IntSize (needed by GLContextProviderImpl.h below)
|
||||
|
||||
@ -17,6 +18,16 @@ class nsIWidget;
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
|
||||
enum class CreateContextFlags : int8_t {
|
||||
NONE = 0,
|
||||
REQUIRE_COMPAT_PROFILE = 1 << 0,
|
||||
// Force the use of hardware backed GL, don't allow software implementations.
|
||||
FORCE_ENABLE_HARDWARE = 1 << 1,
|
||||
/* Don't force discrete GPU to be used (if applicable) */
|
||||
ALLOW_OFFLINE_RENDERER = 1 << 2,
|
||||
};
|
||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CreateContextFlags)
|
||||
|
||||
#define IN_GL_CONTEXT_PROVIDER_H
|
||||
|
||||
// Null is always there
|
||||
|
@ -187,6 +187,11 @@ static const NSOpenGLPixelFormatAttribute kAttribs_offscreen[] = {
|
||||
0
|
||||
};
|
||||
|
||||
static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_allow_offline[] = {
|
||||
NSOpenGLPFAAllowOfflineRenderers,
|
||||
0
|
||||
};
|
||||
|
||||
static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_accel[] = {
|
||||
NSOpenGLPFAAccelerated,
|
||||
0
|
||||
@ -253,7 +258,7 @@ GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget)
|
||||
}
|
||||
|
||||
static already_AddRefed<GLContextCGL>
|
||||
CreateOffscreenFBOContext(bool requireCompatProfile)
|
||||
CreateOffscreenFBOContext(CreateContextFlags flags)
|
||||
{
|
||||
if (!sCGLLibrary.EnsureInitialized()) {
|
||||
return nullptr;
|
||||
@ -262,17 +267,25 @@ CreateOffscreenFBOContext(bool requireCompatProfile)
|
||||
ContextProfile profile;
|
||||
NSOpenGLContext* context = nullptr;
|
||||
|
||||
if (!requireCompatProfile) {
|
||||
if (!(flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE)) {
|
||||
profile = ContextProfile::OpenGLCore;
|
||||
context = CreateWithFormat(kAttribs_offscreen_coreProfile);
|
||||
}
|
||||
if (!context) {
|
||||
profile = ContextProfile::OpenGLCompatibility;
|
||||
|
||||
if (gfxPrefs::RequireHardwareGL())
|
||||
context = CreateWithFormat(kAttribs_offscreen_accel);
|
||||
else
|
||||
context = CreateWithFormat(kAttribs_offscreen);
|
||||
if (flags & CreateContextFlags::ALLOW_OFFLINE_RENDERER) {
|
||||
if (gfxPrefs::RequireHardwareGL())
|
||||
context = CreateWithFormat(kAttribs_singleBuffered);
|
||||
else
|
||||
context = CreateWithFormat(kAttribs_offscreen_allow_offline);
|
||||
|
||||
} else {
|
||||
if (gfxPrefs::RequireHardwareGL())
|
||||
context = CreateWithFormat(kAttribs_offscreen_accel);
|
||||
else
|
||||
context = CreateWithFormat(kAttribs_offscreen);
|
||||
}
|
||||
}
|
||||
if (!context) {
|
||||
NS_WARNING("Failed to create NSOpenGLContext.");
|
||||
@ -287,10 +300,10 @@ CreateOffscreenFBOContext(bool requireCompatProfile)
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderCGL::CreateHeadless(bool requireCompatProfile, bool forceEnabled)
|
||||
GLContextProviderCGL::CreateHeadless(CreateContextFlags flags)
|
||||
{
|
||||
nsRefPtr<GLContextCGL> gl;
|
||||
gl = CreateOffscreenFBOContext(requireCompatProfile);
|
||||
gl = CreateOffscreenFBOContext(flags);
|
||||
if (!gl)
|
||||
return nullptr;
|
||||
|
||||
@ -305,9 +318,9 @@ GLContextProviderCGL::CreateHeadless(bool requireCompatProfile, bool forceEnable
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderCGL::CreateOffscreen(const IntSize& size,
|
||||
const SurfaceCaps& caps,
|
||||
bool requireCompatProfile)
|
||||
CreateContextFlags flags)
|
||||
{
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless(flags);
|
||||
if (!glContext->InitOffscreen(size, caps)) {
|
||||
NS_WARNING("Failed during InitOffscreen.");
|
||||
return nullptr;
|
||||
@ -330,7 +343,7 @@ GLContextProviderCGL::GetGlobalContext()
|
||||
// than 16x16 in size; also 16x16 is POT so that we can do
|
||||
// a FBO with it on older video cards. A FBO context for
|
||||
// sharing is preferred since it has no associated target.
|
||||
gGlobalContext = CreateOffscreenFBOContext(false);
|
||||
gGlobalContext = CreateOffscreenFBOContext(CreateContextFlags::NONE);
|
||||
if (!gGlobalContext || !static_cast<GLContextCGL*>(gGlobalContext.get())->Init()) {
|
||||
NS_WARNING("Couldn't init gGlobalContext.");
|
||||
gGlobalContext = nullptr;
|
||||
|
@ -933,9 +933,9 @@ GLContextEGL::CreateEGLPixmapOffscreenContext(const mozilla::gfx::IntSize& size)
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderEGL::CreateHeadless(bool requireCompatProfile, bool forceEnabled)
|
||||
GLContextProviderEGL::CreateHeadless(CreateContextFlags flags)
|
||||
{
|
||||
if (!sEGLLibrary.EnsureInitialized(forceEnabled)) {
|
||||
if (!sEGLLibrary.EnsureInitialized(bool(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -953,9 +953,9 @@ GLContextProviderEGL::CreateHeadless(bool requireCompatProfile, bool forceEnable
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderEGL::CreateOffscreen(const mozilla::gfx::IntSize& size,
|
||||
const SurfaceCaps& caps,
|
||||
bool requireCompatProfile)
|
||||
CreateContextFlags flags)
|
||||
{
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless(flags);
|
||||
if (!glContext)
|
||||
return nullptr;
|
||||
|
||||
|
@ -1215,7 +1215,7 @@ DONE_CREATING_PIXMAP:
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderGLX::CreateHeadless(bool requireCompatProfile, bool forceEnabled)
|
||||
GLContextProviderGLX::CreateHeadless(CreateContextFlags)
|
||||
{
|
||||
IntSize dummySize = IntSize(16, 16);
|
||||
nsRefPtr<GLContext> glContext = CreateOffscreenPixmapContext(dummySize);
|
||||
@ -1228,9 +1228,9 @@ GLContextProviderGLX::CreateHeadless(bool requireCompatProfile, bool forceEnable
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderGLX::CreateOffscreen(const IntSize& size,
|
||||
const SurfaceCaps& caps,
|
||||
bool requireCompatProfile)
|
||||
CreateContextFlags flags)
|
||||
{
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless(flags);
|
||||
if (!glContext)
|
||||
return nullptr;
|
||||
|
||||
|
@ -54,19 +54,21 @@ public:
|
||||
* resource sharing can be avoided on the target platform, it will
|
||||
* be, in order to isolate the offscreen context.
|
||||
*
|
||||
* @param aSize The initial size of this offscreen context.
|
||||
* @param aFormat The ContextFormat for this offscreen context.
|
||||
* @param size The initial size of this offscreen context.
|
||||
* @param caps The SurfaceCaps for this offscreen context.
|
||||
* @param flags The set of CreateContextFlags to be used for this
|
||||
* offscreen context.
|
||||
*
|
||||
* @return Context to use for offscreen rendering
|
||||
*/
|
||||
static already_AddRefed<GLContext>
|
||||
CreateOffscreen(const mozilla::gfx::IntSize& size,
|
||||
const SurfaceCaps& caps,
|
||||
bool requireCompatProfile);
|
||||
CreateContextFlags flags);
|
||||
|
||||
// Just create a context. We'll add offscreen stuff ourselves.
|
||||
static already_AddRefed<GLContext>
|
||||
CreateHeadless(bool requireCompatProfile, bool forceEnabled = false);
|
||||
CreateHeadless(CreateContextFlags flags);
|
||||
|
||||
/**
|
||||
* Create wrapping Gecko GLContext for external gl context.
|
||||
|
@ -23,13 +23,13 @@ GLContextProviderNull::CreateWrappingExisting(void*, void*)
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderNull::CreateOffscreen(const gfx::IntSize&,
|
||||
const SurfaceCaps&,
|
||||
bool)
|
||||
CreateContextFlags)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderNull::CreateHeadless(bool)
|
||||
GLContextProviderNull::CreateHeadless(CreateContextFlags)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -607,7 +607,7 @@ CreateWindowOffscreenContext()
|
||||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderWGL::CreateHeadless(bool requireCompatProfile, bool forceEnabled)
|
||||
GLContextProviderWGL::CreateHeadless(CreateContextFlags)
|
||||
{
|
||||
if (!sWGLLib.EnsureInitialized()) {
|
||||
return nullptr;
|
||||
@ -642,9 +642,9 @@ GLContextProviderWGL::CreateHeadless(bool requireCompatProfile, bool forceEnable
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderWGL::CreateOffscreen(const IntSize& size,
|
||||
const SurfaceCaps& caps,
|
||||
bool requireCompatProfile)
|
||||
CreateContextFlags flags)
|
||||
{
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless(requireCompatProfile);
|
||||
nsRefPtr<GLContext> glContext = CreateHeadless(flags);
|
||||
if (!glContext)
|
||||
return nullptr;
|
||||
|
||||
|
@ -39,7 +39,7 @@ GLImage::GetAsSourceSurface()
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread");
|
||||
|
||||
if (!sSnapshotContext) {
|
||||
sSnapshotContext = GLContextProvider::CreateHeadless(false);
|
||||
sSnapshotContext = GLContextProvider::CreateHeadless(CreateContextFlags::NONE);
|
||||
if (!sSnapshotContext) {
|
||||
NS_WARNING("Failed to create snapshot GLContext");
|
||||
return nullptr;
|
||||
|
@ -126,9 +126,8 @@ CompositorOGL::CreateContext()
|
||||
caps.preserve = false;
|
||||
caps.bpp16 = gfxPlatform::GetPlatform()->GetOffscreenFormat() == gfxImageFormat::RGB16_565;
|
||||
|
||||
bool requireCompatProfile = true;
|
||||
context = GLContextProvider::CreateOffscreen(mSurfaceSize,
|
||||
caps, requireCompatProfile);
|
||||
caps, CreateContextFlags::REQUIRE_COMPAT_PROFILE);
|
||||
}
|
||||
|
||||
if (!context) {
|
||||
|
@ -45,7 +45,8 @@ public:
|
||||
caps.preserve = false;
|
||||
caps.bpp16 = false;
|
||||
nsRefPtr<GLContext> context = GLContextProvider::CreateOffscreen(
|
||||
IntSize(gCompWidth, gCompHeight), caps, true);
|
||||
IntSize(gCompWidth, gCompHeight), caps,
|
||||
CreateContextFlags::REQUIRE_COMPAT_PROFILE);
|
||||
return context.forget().take();
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -25,7 +25,7 @@ TEST(GfxPrefs, LiveValues) {
|
||||
ASSERT_TRUE(gfxPrefs::SingletonExists());
|
||||
|
||||
// Live boolean, default false
|
||||
ASSERT_FALSE(gfxPrefs::CanvasAzureAccelerated());
|
||||
ASSERT_FALSE(gfxPrefs::LayersDumpTexture());
|
||||
|
||||
// Live int32_t, default 23456
|
||||
ASSERT_TRUE(gfxPrefs::LayerScopePort() == 23456);
|
||||
@ -66,11 +66,11 @@ TEST(GfxPrefs, Set) {
|
||||
ASSERT_FALSE(gfxPrefs::LayersDump());
|
||||
|
||||
// Live boolean, default false
|
||||
ASSERT_FALSE(gfxPrefs::CanvasAzureAccelerated());
|
||||
gfxPrefs::SetCanvasAzureAccelerated(true);
|
||||
ASSERT_TRUE(gfxPrefs::CanvasAzureAccelerated());
|
||||
gfxPrefs::SetCanvasAzureAccelerated(false);
|
||||
ASSERT_FALSE(gfxPrefs::CanvasAzureAccelerated());
|
||||
ASSERT_FALSE(gfxPrefs::LayersDumpTexture());
|
||||
gfxPrefs::SetLayersDumpTexture(true);
|
||||
ASSERT_TRUE(gfxPrefs::LayersDumpTexture());
|
||||
gfxPrefs::SetLayersDumpTexture(false);
|
||||
ASSERT_FALSE(gfxPrefs::LayersDumpTexture());
|
||||
|
||||
// Once float, default -1
|
||||
ASSERT_TRUE(gfxPrefs::APZMaxVelocity() == -1.0f);
|
||||
|
@ -122,6 +122,7 @@ void ShutdownTileCache();
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::layers;
|
||||
using namespace mozilla::gl;
|
||||
|
||||
gfxPlatform *gPlatform = nullptr;
|
||||
static bool gEverInitialized = false;
|
||||
@ -494,7 +495,7 @@ gfxPlatform::Init()
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_GL_DEBUG
|
||||
mozilla::gl::GLContext::StaticInit();
|
||||
GLContext::StaticInit();
|
||||
#endif
|
||||
|
||||
InitLayersAccelerationPrefs();
|
||||
@ -542,11 +543,11 @@ gfxPlatform::Init()
|
||||
gPlatform->mFontPrefsObserver = new FontPrefsObserver();
|
||||
Preferences::AddStrongObservers(gPlatform->mFontPrefsObserver, kObservedPrefs);
|
||||
|
||||
mozilla::gl::GLContext::PlatformStartup();
|
||||
GLContext::PlatformStartup();
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// Texture pool init
|
||||
mozilla::gl::TexturePoolOGL::Init();
|
||||
TexturePoolOGL::Init();
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
@ -626,11 +627,11 @@ gfxPlatform::Shutdown()
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// Shut down the texture pool
|
||||
mozilla::gl::TexturePoolOGL::Shutdown();
|
||||
TexturePoolOGL::Shutdown();
|
||||
#endif
|
||||
|
||||
// Shut down the default GL context provider.
|
||||
mozilla::gl::GLContextProvider::Shutdown();
|
||||
GLContextProvider::Shutdown();
|
||||
|
||||
#if defined(XP_WIN)
|
||||
// The above shutdown calls operate on the available context providers on
|
||||
@ -639,7 +640,7 @@ gfxPlatform::Shutdown()
|
||||
// We should only support the default GL provider on Windows; then, this
|
||||
// could go away. Unfortunately, we currently support WGL (the default) for
|
||||
// WebGL on Optimus.
|
||||
mozilla::gl::GLContextProviderEGL::Shutdown();
|
||||
GLContextProviderEGL::Shutdown();
|
||||
#endif
|
||||
|
||||
// This is a bit iffy - we're assuming that we were the ones that set the
|
||||
@ -1078,7 +1079,7 @@ gfxPlatform::InitializeSkiaCacheLimits()
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::gl::SkiaGLGlue*
|
||||
SkiaGLGlue*
|
||||
gfxPlatform::GetSkiaGLGlue()
|
||||
{
|
||||
#ifdef USE_SKIA_GPU
|
||||
@ -1088,14 +1089,14 @@ gfxPlatform::GetSkiaGLGlue()
|
||||
* FIXME: This should be stored in TLS or something, since there needs to be one for each thread using it. As it
|
||||
* stands, this only works on the main thread.
|
||||
*/
|
||||
bool requireCompatProfile = true;
|
||||
nsRefPtr<mozilla::gl::GLContext> glContext;
|
||||
glContext = mozilla::gl::GLContextProvider::CreateHeadless(requireCompatProfile);
|
||||
nsRefPtr<GLContext> glContext;
|
||||
glContext = GLContextProvider::CreateHeadless(CreateContextFlags::REQUIRE_COMPAT_PROFILE |
|
||||
CreateContextFlags::ALLOW_OFFLINE_RENDERER);
|
||||
if (!glContext) {
|
||||
printf_stderr("Failed to create GLContext for SkiaGL!\n");
|
||||
return nullptr;
|
||||
}
|
||||
mSkiaGlue = new mozilla::gl::SkiaGLGlue(glContext);
|
||||
mSkiaGlue = new SkiaGLGlue(glContext);
|
||||
MOZ_ASSERT(mSkiaGlue->GetGrContext(), "No GrContext");
|
||||
InitializeSkiaCacheLimits();
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,4,88500) fuzzy-if(azureSkiaGL,3,89700) fuzzy-if(azureQuartz,1,34792) == linear-1a.html linear-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,4,88500) fuzzy-if(azureSkiaGL,3,89700) fuzzy-if(azureQuartz,1,34792) == linear-1b.html linear-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,4,88500) fuzzy-if(azureSkiaGL,4,89700) fuzzy-if(azureQuartz,1,34792) == linear-1a.html linear-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,4,88500) fuzzy-if(azureSkiaGL,4,89700) fuzzy-if(azureQuartz,1,34792) == linear-1b.html linear-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,2,88500) fuzzy-if(azureSkiaGL,2,89997) fuzzy-if(azureQuartz,1,11469) == linear-keywords-1a.html linear-keywords-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,2,88500) fuzzy-if(azureSkiaGL,2,89997) fuzzy-if(azureQuartz,1,11985) == linear-keywords-1b.html linear-keywords-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,2,88500) fuzzy-if(azureQuartz,1,10230) == linear-percent.html linear-percent-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,4,92400) fuzzy-if(azureSkiaGL,2,143400) fuzzy-if(azureQuartz,1,27827) fuzzy-if(Android&&AndroidVersion>=15,4,93000) == linear-mix.html linear-mix-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,4,92400) fuzzy-if(azureSkiaGL,3,143400) fuzzy-if(azureQuartz,1,27827) fuzzy-if(Android&&AndroidVersion>=15,4,93000) == linear-mix.html linear-mix-ref.html
|
||||
== linear-diagonal-1a.html linear-diagonal-1-ref.html
|
||||
== linear-diagonal-1b.html linear-diagonal-1-ref.html
|
||||
== linear-diagonal-1c.html linear-diagonal-1-ref.html
|
||||
@ -45,11 +45,11 @@ fails-if(d2d) == linear-repeat-1g.html linear-repeat-1-ref.html # bug 582236
|
||||
== linear-stops-1d.html linear-stops-1-ref.html
|
||||
== linear-stops-1e.html linear-stops-1-ref.html
|
||||
== linear-stops-1f.html linear-stops-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,3,88500) fuzzy-if(azureSkiaGL,2,89700) fuzzy-if(azureQuartz,1,22367) == linear-vertical-1a.html linear-vertical-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,3,88500) fuzzy-if(azureSkiaGL,2,89700) fuzzy-if(azureQuartz,1,22367) == linear-vertical-1b.html linear-vertical-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,3,88500) fuzzy-if(azureSkiaGL,2,89700) fuzzy-if(azureQuartz,2,26777) == linear-vertical-1c.html linear-vertical-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,3,88500) fuzzy-if(azureSkiaGL,2,89700) fuzzy-if(azureQuartz,2,26777) == linear-vertical-1d.html linear-vertical-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,3,88500) fuzzy-if(azureSkiaGL,2,89700) fuzzy-if(azureQuartz,1,22367) == linear-vertical-1e.html linear-vertical-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,3,88500) fuzzy-if(azureSkiaGL,3,89700) fuzzy-if(azureQuartz,1,22367) == linear-vertical-1a.html linear-vertical-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,3,88500) fuzzy-if(azureSkiaGL,3,89700) fuzzy-if(azureQuartz,1,22367) == linear-vertical-1b.html linear-vertical-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,3,88500) fuzzy-if(azureSkiaGL,3,89700) fuzzy-if(azureQuartz,2,26777) == linear-vertical-1c.html linear-vertical-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,3,88500) fuzzy-if(azureSkiaGL,3,89700) fuzzy-if(azureQuartz,2,26777) == linear-vertical-1d.html linear-vertical-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,3,88500) fuzzy-if(azureSkiaGL,3,89700) fuzzy-if(azureQuartz,1,22367) == linear-vertical-1e.html linear-vertical-1-ref.html
|
||||
== linear-vertical-subpixel-1.html linear-vertical-subpixel-1-ref.html
|
||||
== linear-viewport.html linear-viewport-ref.html
|
||||
fails-if(OSX==1010) fuzzy-if(Android,4,248) == linear-zero-length-1a.html linear-zero-length-1-ref.html
|
||||
@ -57,15 +57,15 @@ fails-if(OSX==1010) fuzzy-if(Android,4,248) == linear-zero-length-1b.html linear
|
||||
fails-if(OSX==1010) fuzzy-if(Android,4,248) == linear-zero-length-1c.html linear-zero-length-1-ref.html
|
||||
== nostops.html about:blank
|
||||
== onestop.html about:blank
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,1,5884) fuzzy-if(cocoaWidget,9,87824) fuzzy-if(azureSkiaGL,2,88024) random-if(d2d) == radial-1a.html radial-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,1,5884) fuzzy-if(cocoaWidget,9,87824) fuzzy-if(azureSkiaGL,2,88024) random-if(d2d) == radial-1b.html radial-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,1,5884) fuzzy-if(cocoaWidget,9,87824) fuzzy-if(azureSkiaGL,2,88024) random-if(d2d) == radial-1c.html radial-1-ref.html
|
||||
fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,2,90000) == radial-2a.html radial-2-ref.html
|
||||
fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,2,90000) == radial-2b.html radial-2-ref.html
|
||||
fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,2,90000) == radial-2c.html radial-2-ref.html
|
||||
fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,2,90000) == radial-2d.html radial-2-ref.html
|
||||
fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,2,90000) == radial-2e.html radial-2-ref.html
|
||||
fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,2,90000) == radial-2f.html radial-2-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,1,5884) fuzzy-if(cocoaWidget,9,87824) fuzzy-if(azureSkiaGL,6,88024) random-if(d2d) == radial-1a.html radial-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,1,5884) fuzzy-if(cocoaWidget,9,87824) fuzzy-if(azureSkiaGL,6,88024) random-if(d2d) == radial-1b.html radial-1-ref.html
|
||||
fuzzy-if(!contentSameGfxBackendAsCanvas,1,5884) fuzzy-if(cocoaWidget,9,87824) fuzzy-if(azureSkiaGL,6,88024) random-if(d2d) == radial-1c.html radial-1-ref.html
|
||||
fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,4,90000) == radial-2a.html radial-2-ref.html
|
||||
fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,4,90000) == radial-2b.html radial-2-ref.html
|
||||
fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,4,90000) == radial-2c.html radial-2-ref.html
|
||||
fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,4,90000) == radial-2d.html radial-2-ref.html
|
||||
fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,4,90000) == radial-2e.html radial-2-ref.html
|
||||
fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,4,90000) == radial-2f.html radial-2-ref.html
|
||||
== radial-position-1a.html radial-position-1-ref.html
|
||||
fuzzy-if(cocoaWidget,1,28) fuzzy-if(winWidget,1,18) == radial-position-1b.html radial-position-1-ref.html
|
||||
fuzzy-if(cocoaWidget,4,22317) fuzzy-if(Android,8,771) == radial-shape-closest-corner-1a.html radial-shape-closest-corner-1-ref.html
|
||||
|
@ -129,7 +129,7 @@ public class FirefoxAccounts {
|
||||
// exist.
|
||||
final AndroidFxAccount fxAccount =
|
||||
AccountPickler.unpickle(context, FxAccountConstants.ACCOUNT_PICKLE_FILENAME);
|
||||
accounts[0] = fxAccount.getAndroidAccount();
|
||||
accounts[0] = fxAccount != null ? fxAccount.getAndroidAccount() : null;
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
|
@ -80,12 +80,13 @@ public class AccountPickler {
|
||||
|
||||
/**
|
||||
* Remove Firefox account persisted to disk.
|
||||
* This operation is synchronized to avoid race condition while deleting the account.
|
||||
*
|
||||
* @param context Android context.
|
||||
* @param filename name of persisted pickle file; must not contain path separators.
|
||||
* @return <code>true</code> if given pickle existed and was successfully deleted.
|
||||
*/
|
||||
public static boolean deletePickle(final Context context, final String filename) {
|
||||
public synchronized static boolean deletePickle(final Context context, final String filename) {
|
||||
return context.deleteFile(filename);
|
||||
}
|
||||
|
||||
@ -122,11 +123,12 @@ public class AccountPickler {
|
||||
|
||||
/**
|
||||
* Persist Firefox account to disk as a JSON object.
|
||||
* This operation is synchronized to avoid race condition while deleting the account.
|
||||
*
|
||||
* @param AndroidFxAccount the account to persist to disk
|
||||
* @param account the AndroidFxAccount to persist to disk
|
||||
* @param filename name of file to persist to; must not contain path separators.
|
||||
*/
|
||||
public static void pickle(final AndroidFxAccount account, final String filename) {
|
||||
public synchronized static void pickle(final AndroidFxAccount account, final String filename) {
|
||||
final ExtendedJSONObject o = toJSON(account, System.currentTimeMillis());
|
||||
writeToDisk(account.context, filename, o);
|
||||
}
|
||||
@ -155,6 +157,7 @@ public class AccountPickler {
|
||||
|
||||
/**
|
||||
* Create Android account from saved JSON object. Assumes that an account does not exist.
|
||||
* This operation is synchronized to avoid race condition while deleting the account.
|
||||
*
|
||||
* @param context
|
||||
* Android context.
|
||||
@ -162,7 +165,7 @@ public class AccountPickler {
|
||||
* name of file to read from; must not contain path separators.
|
||||
* @return created Android account, or null on error.
|
||||
*/
|
||||
public static AndroidFxAccount unpickle(final Context context, final String filename) {
|
||||
public synchronized static AndroidFxAccount unpickle(final Context context, final String filename) {
|
||||
final String jsonString = Utils.readFile(context, filename);
|
||||
if (jsonString == null) {
|
||||
Logger.info(LOG_TAG, "Pickle file '" + filename + "' not found; aborting.");
|
||||
|
@ -688,7 +688,7 @@ pref("gfx.content.azure.backends", "direct2d1.1,direct2d,cairo");
|
||||
pref("gfx.content.azure.backends", "cg");
|
||||
pref("gfx.canvas.azure.backends", "skia");
|
||||
// Accelerated cg canvas where available (10.7+)
|
||||
pref("gfx.canvas.azure.accelerated", false);
|
||||
pref("gfx.canvas.azure.accelerated", true);
|
||||
#else
|
||||
pref("gfx.canvas.azure.backends", "cairo");
|
||||
pref("gfx.content.azure.backends", "cairo");
|
||||
@ -4429,7 +4429,7 @@ pref("dom.mozAlarms.enabled", false);
|
||||
|
||||
// Push
|
||||
|
||||
#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
|
||||
#if !defined(MOZ_B2G) && !defined(ANDROID)
|
||||
// Desktop prefs
|
||||
#ifdef RELEASE_BUILD
|
||||
pref("dom.push.enabled", false);
|
||||
@ -4878,7 +4878,14 @@ pref("intl.collation.mac.use_icu", true);
|
||||
// Enable NSTextInput protocol for use with IMEs that have not
|
||||
// been updated to use the NSTextInputClient protocol.
|
||||
pref("intl.ime.nstextinput.enable", false);
|
||||
|
||||
#if !defined(RELEASE_BUILD) || defined(DEBUG)
|
||||
// In non-release builds we crash by default on insecure text input (when a
|
||||
// password editor has focus but secure event input isn't enabled). The
|
||||
// following pref, when turned on, disables this behavior. See bug 1188425.
|
||||
pref("intl.allow-insecure-text-input", false);
|
||||
#endif
|
||||
#endif // XP_MACOSX
|
||||
|
||||
// Enable meta-viewport support in remote APZ-enabled frames.
|
||||
pref("dom.meta-viewport.enabled", false);
|
||||
|
@ -1471,6 +1471,7 @@ var WalkerActor = protocol.ActorClass({
|
||||
// Create the observer on the node's actor. The node will make sure
|
||||
// the observer is cleaned up when the actor is released.
|
||||
actor.observer = new actor.rawNode.defaultView.MutationObserver(this.onMutations);
|
||||
actor.observer.mergeAttributeRecords = true;
|
||||
actor.observer.observe(node, {
|
||||
attributes: true,
|
||||
characterData: true,
|
||||
@ -2736,29 +2737,7 @@ var WalkerActor = protocol.ActorClass({
|
||||
this._orphaned = new Set();
|
||||
}
|
||||
|
||||
|
||||
// Clear out any duplicate attribute mutations before sending them over
|
||||
// the protocol. Keep only the most recent change for each attribute.
|
||||
let targetMap = {};
|
||||
let filtered = pending.reverse().filter(mutation => {
|
||||
if (mutation.type === "attributes") {
|
||||
if (!targetMap[mutation.target]) {
|
||||
targetMap[mutation.target] = {};
|
||||
}
|
||||
let attributesForTarget = targetMap[mutation.target];
|
||||
|
||||
if (attributesForTarget[mutation.attributeName]) {
|
||||
// Since the array was reversed, if we've seen this attribute already
|
||||
// then this one is a duplicate and can be skipped.
|
||||
return false;
|
||||
}
|
||||
|
||||
attributesForTarget[mutation.attributeName] = true;
|
||||
}
|
||||
return true;
|
||||
}).reverse();
|
||||
|
||||
return filtered;
|
||||
return pending;
|
||||
}, {
|
||||
request: {
|
||||
cleanup: Option(0)
|
||||
|
@ -152,12 +152,19 @@ ifdef INSTALL_SDK # Here comes the hard part
|
||||
endif # INSTALL_SDK
|
||||
|
||||
make-sdk:
|
||||
ifndef SDK_UNIFY
|
||||
$(MAKE) stage-package UNIVERSAL_BINARY= STAGE_SDK=1 MOZ_PKG_DIR=sdk-stage
|
||||
endif
|
||||
@echo 'Packaging SDK...'
|
||||
$(RM) -rf $(DIST)/$(MOZ_APP_NAME)-sdk
|
||||
$(NSINSTALL) -D $(DIST)/$(MOZ_APP_NAME)-sdk/bin
|
||||
ifdef SDK_UNIFY
|
||||
(cd $(UNIFY_DIST)/sdk-stage && $(TAR) $(TAR_CREATE_FLAGS) - .) | \
|
||||
(cd $(DIST)/$(MOZ_APP_NAME)-sdk/bin && tar -xf -)
|
||||
else
|
||||
(cd $(DIST)/sdk-stage && $(TAR) $(TAR_CREATE_FLAGS) - .) | \
|
||||
(cd $(DIST)/$(MOZ_APP_NAME)-sdk/bin && tar -xf -)
|
||||
endif
|
||||
$(NSINSTALL) -D $(DIST)/$(MOZ_APP_NAME)-sdk/host/bin
|
||||
(cd $(DIST)/host/bin && $(TAR) $(TAR_CREATE_FLAGS) - .) | \
|
||||
(cd $(DIST)/$(MOZ_APP_NAME)-sdk/host/bin && tar -xf -)
|
||||
@ -180,6 +187,11 @@ ifndef PKG_SKIP_STRIP
|
||||
USE_ELF_HACK= $(PYTHON) $(MOZILLA_DIR)/toolkit/mozapps/installer/strip.py $(DIST)/$(MOZ_APP_NAME)-sdk
|
||||
endif
|
||||
cd $(DIST) && $(MAKE_SDK)
|
||||
ifdef UNIFY_DIST
|
||||
ifndef SDK_UNIFY
|
||||
$(MAKE) -C $(UNIFY_DIST)/.. sdk SDK_UNIFY=1
|
||||
endif
|
||||
endif
|
||||
|
||||
checksum:
|
||||
mkdir -p `dirname $(CHECKSUM_FILE)`
|
||||
|
@ -758,6 +758,13 @@ UPLOAD_FILES += \
|
||||
$(call QUOTED_WILDCARD,$(DIST)/$(PKG_PATH)$(CODE_COVERAGE_ARCHIVE_BASENAME).zip)
|
||||
endif
|
||||
|
||||
ifdef UNIFY_DIST
|
||||
UNIFY_ARCH := $(notdir $(patsubst %/,%,$(dir $(UNIFY_DIST))))
|
||||
UPLOAD_FILES += \
|
||||
$(wildcard $(UNIFY_DIST)/$(SDK_PATH)$(PKG_BASENAME)-$(UNIFY_ARCH).sdk$(SDK_SUFFIX)) \
|
||||
$(wildcard $(UNIFY_DIST)/$(SDK_PATH)$(PKG_BASENAME)-$(UNIFY_ARCH).sdk$(SDK_SUFFIX).asc)
|
||||
endif
|
||||
|
||||
SIGN_CHECKSUM_CMD=
|
||||
ifdef MOZ_SIGN_CMD
|
||||
# If we're signing with gpg, we'll have a bunch of extra detached signatures to
|
||||
|
@ -75,8 +75,7 @@ public:
|
||||
}
|
||||
|
||||
nsRefPtr<gl::GLContext> gl;
|
||||
bool requireCompatProfile = true;
|
||||
gl = gl::GLContextProvider::CreateHeadless(requireCompatProfile);
|
||||
gl = gl::GLContextProvider::CreateHeadless(gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE);
|
||||
|
||||
if (!gl) {
|
||||
// Setting mReady to true here means that we won't retry. Everything will
|
||||
|
@ -5442,7 +5442,8 @@ static int32_t RoundUp(double aDouble)
|
||||
}
|
||||
|
||||
#if !defined(RELEASE_BUILD) || defined(DEBUG)
|
||||
if (mGeckoChild && mTextInputHandler && mTextInputHandler->IsFocused()) {
|
||||
if (!Preferences::GetBool("intl.allow-insecure-text-input", false) &&
|
||||
mGeckoChild && mTextInputHandler && mTextInputHandler->IsFocused()) {
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
NSWindow* window = [self window];
|
||||
NSString* info = [NSString stringWithFormat:@"\nview [%@], window [%@], window is key %i, is fullscreen %i, app is active %i",
|
||||
|
@ -274,7 +274,16 @@ gboolean nsDeviceContextSpecGTK::PrinterEnumerator(GtkPrinter *aPrinter,
|
||||
NS_ConvertUTF16toUTF8 requestedName(printerName);
|
||||
const char* currentName = gtk_printer_get_name(aPrinter);
|
||||
if (requestedName.Equals(currentName)) {
|
||||
nsDeviceContextSpecGTK::StartPrintJob(spec, aPrinter);
|
||||
spec->mPrintSettings->SetGtkPrinter(aPrinter);
|
||||
|
||||
// Bug 1145916 - attempting to kick off a print job for this printer
|
||||
// during this tick of the event loop will result in the printer backend
|
||||
// misunderstanding what the capabilities of the printer are due to a
|
||||
// GTK bug (https://bugzilla.gnome.org/show_bug.cgi?id=753041). We
|
||||
// sidestep this by deferring the print to the next tick.
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(spec, &nsDeviceContextSpecGTK::StartPrintJob);
|
||||
NS_DispatchToCurrentThread(event);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@ -283,19 +292,17 @@ gboolean nsDeviceContextSpecGTK::PrinterEnumerator(GtkPrinter *aPrinter,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void nsDeviceContextSpecGTK::StartPrintJob(nsDeviceContextSpecGTK* spec,
|
||||
GtkPrinter* printer) {
|
||||
GtkPrintJob* job = gtk_print_job_new(spec->mTitle.get(),
|
||||
printer,
|
||||
spec->mGtkPrintSettings,
|
||||
spec->mGtkPageSetup);
|
||||
void nsDeviceContextSpecGTK::StartPrintJob() {
|
||||
GtkPrintJob* job = gtk_print_job_new(mTitle.get(),
|
||||
mPrintSettings->GetGtkPrinter(),
|
||||
mGtkPrintSettings,
|
||||
mGtkPageSetup);
|
||||
|
||||
if (!gtk_print_job_set_source_file(job, spec->mSpoolName.get(), nullptr))
|
||||
if (!gtk_print_job_set_source_file(job, mSpoolName.get(), nullptr))
|
||||
return;
|
||||
|
||||
NS_ADDREF(spec->mSpoolFile.get());
|
||||
gtk_print_job_send(job, print_callback, spec->mSpoolFile, ns_release_macro);
|
||||
NS_ADDREF(mSpoolFile.get());
|
||||
gtk_print_job_send(job, print_callback, mSpoolFile, ns_release_macro);
|
||||
}
|
||||
|
||||
void
|
||||
@ -328,7 +335,7 @@ NS_IMETHODIMP nsDeviceContextSpecGTK::EndDocument()
|
||||
GtkPrinter* printer = mPrintSettings->GetGtkPrinter();
|
||||
if (printer) {
|
||||
// We have a printer, so we can print right away.
|
||||
nsDeviceContextSpecGTK::StartPrintJob(this, printer);
|
||||
StartPrintJob();
|
||||
} else {
|
||||
// We don't have a printer. We have to enumerate the printers and find
|
||||
// one with a matching name.
|
||||
|
@ -56,9 +56,8 @@ protected:
|
||||
|
||||
private:
|
||||
void EnumeratePrinters();
|
||||
void StartPrintJob();
|
||||
static gboolean PrinterEnumerator(GtkPrinter *aPrinter, gpointer aData);
|
||||
static void StartPrintJob(nsDeviceContextSpecGTK *spec,
|
||||
GtkPrinter *printer);
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
@ -18,6 +18,11 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
typedef nsresult(*TestFuncPtr)();
|
||||
@ -55,7 +60,7 @@ class AutoCreateAndDestroyReentrantMonitor
|
||||
public:
|
||||
AutoCreateAndDestroyReentrantMonitor() {
|
||||
mReentrantMonitor = new ReentrantMonitor("TestTimers::AutoMon");
|
||||
NS_ASSERTION(mReentrantMonitor, "Out of memory!");
|
||||
MOZ_ASSERT(mReentrantMonitor, "Out of memory!");
|
||||
}
|
||||
|
||||
~AutoCreateAndDestroyReentrantMonitor() {
|
||||
@ -79,12 +84,12 @@ public:
|
||||
: mThreadPtr(aThreadPtr), mReentrantMonitor(aReentrantMonitor) { }
|
||||
|
||||
NS_IMETHOD Notify(nsITimer* aTimer) override {
|
||||
NS_ASSERTION(mThreadPtr, "Callback was not supposed to be called!");
|
||||
MOZ_ASSERT(mThreadPtr, "Callback was not supposed to be called!");
|
||||
nsCOMPtr<nsIThread> current(do_GetCurrentThread());
|
||||
|
||||
ReentrantMonitorAutoEnter mon(*mReentrantMonitor);
|
||||
|
||||
NS_ASSERTION(!*mThreadPtr, "Timer called back more than once!");
|
||||
MOZ_ASSERT(!*mThreadPtr, "Timer called back more than once!");
|
||||
*mThreadPtr = current;
|
||||
|
||||
mon.Notify();
|
||||
@ -124,8 +129,7 @@ TestTargetedTimers()
|
||||
new TimerCallback(¬ifiedThread, newMon);
|
||||
NS_ENSURE_TRUE(callback, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
rv = timer->InitWithCallback(callback, PR_MillisecondsToInterval(2000),
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
rv = timer->InitWithCallback(callback, 2000, nsITimer::TYPE_ONE_SHOT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ReentrantMonitorAutoEnter mon(*newMon);
|
||||
@ -157,8 +161,7 @@ TestTimerWithStoppedTarget()
|
||||
new TimerCallback(nullptr, nullptr);
|
||||
NS_ENSURE_TRUE(callback, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
rv = timer->InitWithCallback(callback, PR_MillisecondsToInterval(100),
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
rv = timer->InitWithCallback(callback, 100, nsITimer::TYPE_ONE_SHOT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
testThread->Shutdown();
|
||||
@ -168,6 +171,305 @@ TestTimerWithStoppedTarget()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#define FUZZ_MAX_TIMEOUT 9
|
||||
class FuzzTestThreadState final : public nsITimerCallback {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
explicit FuzzTestThreadState(nsIThread* thread) :
|
||||
mThread(thread),
|
||||
mStopped(false)
|
||||
{}
|
||||
|
||||
class StartRunnable final : public nsRunnable {
|
||||
public:
|
||||
explicit StartRunnable(FuzzTestThreadState* threadState) :
|
||||
mThreadState(threadState)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mThreadState->ScheduleOrCancelTimers();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<FuzzTestThreadState> mThreadState;
|
||||
};
|
||||
|
||||
void Start()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable = new StartRunnable(this);
|
||||
nsresult rv = mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_ASSERT(false, "Failed to dispatch StartRunnable.");
|
||||
}
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
mStopped = true;
|
||||
}
|
||||
|
||||
NS_IMETHOD Notify(nsITimer* aTimer) override
|
||||
{
|
||||
bool onCorrectThread;
|
||||
nsresult rv = mThread->IsOnCurrentThread(&onCorrectThread);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to perform thread check.");
|
||||
MOZ_ASSERT(onCorrectThread, "Notify invoked on wrong thread.");
|
||||
|
||||
uint32_t delay;
|
||||
rv = aTimer->GetDelay(&delay);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_ASSERT(false, "GetDelay failed.");
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (delay > FUZZ_MAX_TIMEOUT) {
|
||||
MOZ_ASSERT(false, "Delay was an invalid value for this test.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
uint32_t type;
|
||||
rv = aTimer->GetType(&type);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to get timer type.");
|
||||
MOZ_ASSERT(type <= nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP);
|
||||
|
||||
if (type == nsITimer::TYPE_ONE_SHOT) {
|
||||
if (mOneShotTimersByDelay[delay].empty()) {
|
||||
MOZ_ASSERT(false, "Unexpected one-shot timer.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (mOneShotTimersByDelay[delay].front().get() != aTimer) {
|
||||
MOZ_ASSERT(false,
|
||||
"One-shot timers for a given duration have been reordered.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mOneShotTimersByDelay[delay].pop_front();
|
||||
--mTimersOutstanding;
|
||||
} else if (mStopped) {
|
||||
CancelRepeatingTimer(aTimer);
|
||||
}
|
||||
|
||||
ScheduleOrCancelTimers();
|
||||
RescheduleSomeTimers();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool HasTimersOutstanding() const
|
||||
{
|
||||
return !!mTimersOutstanding;
|
||||
}
|
||||
|
||||
private:
|
||||
~FuzzTestThreadState()
|
||||
{
|
||||
for (size_t i = 0; i <= FUZZ_MAX_TIMEOUT; ++i) {
|
||||
MOZ_ASSERT(mOneShotTimersByDelay[i].empty(),
|
||||
"Timers remain at end of test.");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GetRandomType() const
|
||||
{
|
||||
return rand() % (nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP + 1);
|
||||
}
|
||||
|
||||
size_t CountOneShotTimers() const
|
||||
{
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i <= FUZZ_MAX_TIMEOUT; ++i) {
|
||||
count += mOneShotTimersByDelay[i].size();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void ScheduleOrCancelTimers()
|
||||
{
|
||||
if (mStopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t numTimersDesired = (rand() % 100) + 100;
|
||||
MOZ_ASSERT(numTimersDesired >= 100);
|
||||
MOZ_ASSERT(numTimersDesired < 200);
|
||||
int adjustment = numTimersDesired - mTimersOutstanding;
|
||||
|
||||
while (adjustment > 0) {
|
||||
CreateRandomTimer();
|
||||
--adjustment;
|
||||
}
|
||||
|
||||
while (adjustment < 0) {
|
||||
CancelRandomTimer();
|
||||
++adjustment;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(numTimersDesired == mTimersOutstanding);
|
||||
}
|
||||
|
||||
void RescheduleSomeTimers()
|
||||
{
|
||||
if (mStopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const size_t kNumRescheduled = 40;
|
||||
|
||||
// Reschedule some timers with a Cancel first.
|
||||
for (size_t i = 0; i < kNumRescheduled; ++i) {
|
||||
InitRandomTimer(CancelRandomTimer().get());
|
||||
}
|
||||
// Reschedule some timers without a Cancel first.
|
||||
for (size_t i = 0; i < kNumRescheduled; ++i) {
|
||||
InitRandomTimer(RemoveRandomTimer().get());
|
||||
}
|
||||
}
|
||||
|
||||
void CreateRandomTimer()
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_ASSERT(false, "Failed to create timer.");
|
||||
return;
|
||||
}
|
||||
|
||||
rv = timer->SetTarget(static_cast<nsIEventTarget*>(mThread.get()));
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_ASSERT(false, "Failed to set target.");
|
||||
return;
|
||||
}
|
||||
|
||||
InitRandomTimer(timer.get());
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITimer> CancelRandomTimer()
|
||||
{
|
||||
nsCOMPtr<nsITimer> timer(RemoveRandomTimer());
|
||||
timer->Cancel();
|
||||
return timer;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITimer> RemoveRandomTimer()
|
||||
{
|
||||
MOZ_ASSERT(mTimersOutstanding);
|
||||
|
||||
if ((GetRandomType() == nsITimer::TYPE_ONE_SHOT && CountOneShotTimers())
|
||||
|| mRepeatingTimers.empty()) {
|
||||
uint32_t delayToRemove = rand() % (FUZZ_MAX_TIMEOUT + 1);
|
||||
while (mOneShotTimersByDelay[delayToRemove].empty()) {
|
||||
// ++delayToRemove mod FUZZ_MAX_TIMEOUT + 1
|
||||
delayToRemove = (delayToRemove + 1) % (FUZZ_MAX_TIMEOUT + 1);
|
||||
}
|
||||
|
||||
uint32_t indexToRemove =
|
||||
rand() % mOneShotTimersByDelay[delayToRemove].size();
|
||||
|
||||
for (auto it = mOneShotTimersByDelay[delayToRemove].begin();
|
||||
it != mOneShotTimersByDelay[delayToRemove].end();
|
||||
++it) {
|
||||
if (indexToRemove) {
|
||||
--indexToRemove;
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITimer> removed = *it;
|
||||
mOneShotTimersByDelay[delayToRemove].erase(it);
|
||||
--mTimersOutstanding;
|
||||
return removed;
|
||||
}
|
||||
} else {
|
||||
size_t indexToRemove = rand() % mRepeatingTimers.size();
|
||||
nsCOMPtr<nsITimer> removed(mRepeatingTimers[indexToRemove]);
|
||||
mRepeatingTimers.erase(mRepeatingTimers.begin() + indexToRemove);
|
||||
--mTimersOutstanding;
|
||||
return removed;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_UNREACHABLE("Unable to remove a timer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void InitRandomTimer(nsITimer* aTimer)
|
||||
{
|
||||
// Between 0 and FUZZ_MAX_TIMEOUT
|
||||
uint32_t delay = rand() % (FUZZ_MAX_TIMEOUT + 1);
|
||||
uint32_t type = GetRandomType();
|
||||
nsresult rv = aTimer->InitWithCallback(this, delay, type);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_ASSERT(false, "Failed to set timer.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == nsITimer::TYPE_ONE_SHOT) {
|
||||
mOneShotTimersByDelay[delay].push_back(aTimer);
|
||||
} else {
|
||||
mRepeatingTimers.push_back(aTimer);
|
||||
}
|
||||
++mTimersOutstanding;
|
||||
}
|
||||
|
||||
void CancelRepeatingTimer(nsITimer* aTimer)
|
||||
{
|
||||
for (auto it = mRepeatingTimers.begin();
|
||||
it != mRepeatingTimers.end();
|
||||
++it) {
|
||||
if (it->get() == aTimer) {
|
||||
mRepeatingTimers.erase(it);
|
||||
aTimer->Cancel();
|
||||
--mTimersOutstanding;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
// Scheduled timers, indexed by delay between 0-9 ms, in lists
|
||||
// with most recently scheduled last.
|
||||
std::list<nsCOMPtr<nsITimer>> mOneShotTimersByDelay[FUZZ_MAX_TIMEOUT + 1];
|
||||
std::vector<nsCOMPtr<nsITimer>> mRepeatingTimers;
|
||||
Atomic<bool> mStopped;
|
||||
Atomic<size_t> mTimersOutstanding;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(FuzzTestThreadState, nsITimerCallback)
|
||||
|
||||
nsresult
|
||||
FuzzTestTimers()
|
||||
{
|
||||
static const size_t kNumThreads(10);
|
||||
AutoTestThread threads[kNumThreads];
|
||||
nsRefPtr<FuzzTestThreadState> threadStates[kNumThreads];
|
||||
|
||||
for (size_t i = 0; i < kNumThreads; ++i) {
|
||||
threadStates[i] = new FuzzTestThreadState(&*threads[i]);
|
||||
threadStates[i]->Start();
|
||||
}
|
||||
|
||||
PR_Sleep(PR_MillisecondsToInterval(20000));
|
||||
|
||||
for (size_t i = 0; i < kNumThreads; ++i) {
|
||||
threadStates[i]->Stop();
|
||||
}
|
||||
|
||||
// Wait at most 10 seconds for all outstanding timers to pop
|
||||
PRIntervalTime start = PR_IntervalNow();
|
||||
for (auto& threadState : threadStates) {
|
||||
while (threadState->HasTimersOutstanding()) {
|
||||
if (PR_IntervalToMilliseconds(PR_IntervalNow() - start) > 10000) {
|
||||
MOZ_ASSERT(false, "Timed out waiting for all timers to pop");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
PR_Sleep(PR_MillisecondsToInterval(10));
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
ScopedXPCOM xpcom("TestTimers");
|
||||
@ -175,7 +477,8 @@ int main(int argc, char** argv)
|
||||
|
||||
static TestFuncPtr testsToRun[] = {
|
||||
TestTargetedTimers,
|
||||
TestTimerWithStoppedTarget
|
||||
TestTimerWithStoppedTarget,
|
||||
FuzzTestTimers
|
||||
};
|
||||
static uint32_t testCount = sizeof(testsToRun) / sizeof(testsToRun[0]);
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "TimerThread.h"
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
#include "plarena.h"
|
||||
#include "pratom.h"
|
||||
|
||||
#include "nsIObserverService.h"
|
||||
@ -80,6 +81,200 @@ TimerObserverRunnable::Run()
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
|
||||
// TimerEventAllocator is a thread-safe allocator used only for nsTimerEvents.
|
||||
// It's needed to avoid contention over the default allocator lock when
|
||||
// firing timer events (see bug 733277). The thread-safety is required because
|
||||
// nsTimerEvent objects are allocated on the timer thread, and freed on another
|
||||
// thread. Because TimerEventAllocator has its own lock, contention over that
|
||||
// lock is limited to the allocation and deallocation of nsTimerEvent objects.
|
||||
//
|
||||
// Because this allocator is layered over PLArenaPool, it never shrinks -- even
|
||||
// "freed" nsTimerEvents aren't truly freed, they're just put onto a free-list
|
||||
// for later recycling. So the amount of memory consumed will always be equal
|
||||
// to the high-water mark consumption. But nsTimerEvents are small and it's
|
||||
// unusual to have more than a few hundred of them, so this shouldn't be a
|
||||
// problem in practice.
|
||||
|
||||
class TimerEventAllocator
|
||||
{
|
||||
private:
|
||||
struct FreeEntry
|
||||
{
|
||||
FreeEntry* mNext;
|
||||
};
|
||||
|
||||
PLArenaPool mPool;
|
||||
FreeEntry* mFirstFree;
|
||||
mozilla::Monitor mMonitor;
|
||||
|
||||
public:
|
||||
TimerEventAllocator()
|
||||
: mFirstFree(nullptr)
|
||||
, mMonitor("TimerEventAllocator")
|
||||
{
|
||||
PL_InitArenaPool(&mPool, "TimerEventPool", 4096, /* align = */ 0);
|
||||
}
|
||||
|
||||
~TimerEventAllocator()
|
||||
{
|
||||
PL_FinishArenaPool(&mPool);
|
||||
}
|
||||
|
||||
void* Alloc(size_t aSize);
|
||||
void Free(void* aPtr);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class nsTimerEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run();
|
||||
|
||||
nsTimerEvent()
|
||||
: mTimer()
|
||||
, mGeneration(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsTimerEvent);
|
||||
|
||||
// Note: We override operator new for this class, and the override is
|
||||
// fallible!
|
||||
sAllocatorUsers++;
|
||||
}
|
||||
|
||||
TimeStamp mInitTime;
|
||||
|
||||
static void Init();
|
||||
static void Shutdown();
|
||||
static void DeleteAllocatorIfNeeded();
|
||||
|
||||
static void* operator new(size_t aSize) CPP_THROW_NEW
|
||||
{
|
||||
return sAllocator->Alloc(aSize);
|
||||
}
|
||||
void operator delete(void* aPtr)
|
||||
{
|
||||
sAllocator->Free(aPtr);
|
||||
DeleteAllocatorIfNeeded();
|
||||
}
|
||||
|
||||
already_AddRefed<nsTimerImpl> ForgetTimer()
|
||||
{
|
||||
return mTimer.forget();
|
||||
}
|
||||
|
||||
void SetTimer(already_AddRefed<nsTimerImpl> aTimer)
|
||||
{
|
||||
mTimer = aTimer;
|
||||
mGeneration = mTimer->GetGeneration();
|
||||
}
|
||||
|
||||
private:
|
||||
~nsTimerEvent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsTimerEvent);
|
||||
|
||||
MOZ_ASSERT(!sCanDeleteAllocator || sAllocatorUsers > 0,
|
||||
"This will result in us attempting to deallocate the nsTimerEvent allocator twice");
|
||||
sAllocatorUsers--;
|
||||
}
|
||||
|
||||
nsRefPtr<nsTimerImpl> mTimer;
|
||||
int32_t mGeneration;
|
||||
|
||||
static TimerEventAllocator* sAllocator;
|
||||
static Atomic<int32_t> sAllocatorUsers;
|
||||
static bool sCanDeleteAllocator;
|
||||
};
|
||||
|
||||
TimerEventAllocator* nsTimerEvent::sAllocator = nullptr;
|
||||
Atomic<int32_t> nsTimerEvent::sAllocatorUsers;
|
||||
bool nsTimerEvent::sCanDeleteAllocator = false;
|
||||
|
||||
namespace {
|
||||
|
||||
void*
|
||||
TimerEventAllocator::Alloc(size_t aSize)
|
||||
{
|
||||
MOZ_ASSERT(aSize == sizeof(nsTimerEvent));
|
||||
|
||||
mozilla::MonitorAutoLock lock(mMonitor);
|
||||
|
||||
void* p;
|
||||
if (mFirstFree) {
|
||||
p = mFirstFree;
|
||||
mFirstFree = mFirstFree->mNext;
|
||||
} else {
|
||||
PL_ARENA_ALLOCATE(p, &mPool, aSize);
|
||||
if (!p) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
TimerEventAllocator::Free(void* aPtr)
|
||||
{
|
||||
mozilla::MonitorAutoLock lock(mMonitor);
|
||||
|
||||
FreeEntry* entry = reinterpret_cast<FreeEntry*>(aPtr);
|
||||
|
||||
entry->mNext = mFirstFree;
|
||||
mFirstFree = entry;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
nsTimerEvent::Init()
|
||||
{
|
||||
sAllocator = new TimerEventAllocator();
|
||||
}
|
||||
|
||||
void
|
||||
nsTimerEvent::Shutdown()
|
||||
{
|
||||
sCanDeleteAllocator = true;
|
||||
DeleteAllocatorIfNeeded();
|
||||
}
|
||||
|
||||
void
|
||||
nsTimerEvent::DeleteAllocatorIfNeeded()
|
||||
{
|
||||
if (sCanDeleteAllocator && sAllocatorUsers == 0) {
|
||||
delete sAllocator;
|
||||
sAllocator = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTimerEvent::Run()
|
||||
{
|
||||
if (mGeneration != mTimer->GetGeneration()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
|
||||
("[this=%p] time between PostTimerEvent() and Fire(): %fms\n",
|
||||
this, (now - mInitTime).ToMilliseconds()));
|
||||
}
|
||||
|
||||
mTimer->Fire();
|
||||
// Since nsTimerImpl is not thread-safe, we should release |mTimer|
|
||||
// here in the target thread to avoid race condition. Otherwise,
|
||||
// ~nsTimerEvent() which calls nsTimerImpl::Release() could run in the
|
||||
// timer thread and result in race condition.
|
||||
mTimer = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TimerThread::Init()
|
||||
{
|
||||
@ -94,6 +289,8 @@ TimerThread::Init()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsTimerEvent::Init();
|
||||
|
||||
if (mInitInProgress.exchange(true) == false) {
|
||||
// We hold on to mThread to keep the thread alive.
|
||||
nsresult rv = NS_NewThread(getter_AddRefs(mThread), this);
|
||||
@ -168,6 +365,8 @@ TimerThread::Shutdown()
|
||||
|
||||
mThread->Shutdown(); // wait for the thread to die
|
||||
|
||||
nsTimerEvent::Shutdown();
|
||||
|
||||
MOZ_LOG(GetTimerLog(), LogLevel::Debug, ("TimerThread::Shutdown end\n"));
|
||||
return NS_OK;
|
||||
}
|
||||
@ -265,15 +464,10 @@ TimerThread::Run()
|
||||
("Timer thread woke up %fms from when it was supposed to\n",
|
||||
fabs((now - timerRef->mTimeout).ToMilliseconds())));
|
||||
|
||||
{
|
||||
// We release mMonitor around the Fire call to avoid deadlock.
|
||||
MonitorAutoUnlock unlock(mMonitor);
|
||||
|
||||
// We are going to let the call to PostTimerEvent here handle the
|
||||
// release of the timer so that we don't end up releasing the timer
|
||||
// on the TimerThread instead of on the thread it targets.
|
||||
timerRef = nsTimerImpl::PostTimerEvent(timerRef.forget());
|
||||
}
|
||||
// We are going to let the call to PostTimerEvent here handle the
|
||||
// release of the timer so that we don't end up releasing the timer
|
||||
// on the TimerThread instead of on the thread it targets.
|
||||
timerRef = PostTimerEvent(timerRef.forget());
|
||||
|
||||
if (timerRef) {
|
||||
// We got our reference back due to an error.
|
||||
@ -487,6 +681,75 @@ TimerThread::ReleaseTimerInternal(nsTimerImpl* aTimer)
|
||||
NS_RELEASE(aTimer);
|
||||
}
|
||||
|
||||
already_AddRefed<nsTimerImpl>
|
||||
TimerThread::PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef)
|
||||
{
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
|
||||
nsRefPtr<nsTimerImpl> timer(aTimerRef);
|
||||
if (!timer->mEventTarget) {
|
||||
NS_ERROR("Attempt to post timer event to NULL event target");
|
||||
return timer.forget();
|
||||
}
|
||||
|
||||
// XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
|
||||
|
||||
// Since we already addref'd 'timer', we don't need to addref here.
|
||||
// We will release either in ~nsTimerEvent(), or pass the reference back to
|
||||
// the caller. We need to copy the generation number from this timer into the
|
||||
// event, so we can avoid firing a timer that was re-initialized after being
|
||||
// canceled.
|
||||
|
||||
nsRefPtr<nsTimerEvent> event = new nsTimerEvent;
|
||||
if (!event) {
|
||||
return timer.forget();
|
||||
}
|
||||
|
||||
if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
|
||||
event->mInitTime = TimeStamp::Now();
|
||||
}
|
||||
|
||||
// If this is a repeating precise timer, we need to calculate the time for
|
||||
// the next timer to fire before we make the callback.
|
||||
if (timer->IsRepeatingPrecisely()) {
|
||||
timer->SetDelayInternal(timer->mDelay);
|
||||
|
||||
// But only re-arm REPEATING_PRECISE timers.
|
||||
if (timer->mType == nsTimerImpl::TYPE_REPEATING_PRECISE) {
|
||||
if (AddTimerInternal(timer) == -1) {
|
||||
return timer.forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
// During the dispatch of TimerEvent, we overwrite the current TraceInfo
|
||||
// partially with the info saved in timer earlier, and restore it back by
|
||||
// AutoSaveCurTraceInfo.
|
||||
AutoSaveCurTraceInfo saveCurTraceInfo;
|
||||
(timer->GetTracedTask()).SetTLSTraceInfo();
|
||||
#endif
|
||||
|
||||
nsIEventTarget* target = timer->mEventTarget;
|
||||
event->SetTimer(timer.forget());
|
||||
|
||||
nsresult rv;
|
||||
{
|
||||
// We release mMonitor around the Dispatch because if this timer is targeted
|
||||
// at the TimerThread we'll deadlock.
|
||||
MonitorAutoUnlock unlock(mMonitor);
|
||||
rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
timer = event->ForgetTimer();
|
||||
RemoveTimerInternal(timer);
|
||||
return timer.forget();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
TimerThread::DoBeforeSleep()
|
||||
{
|
||||
|
@ -68,6 +68,8 @@ private:
|
||||
bool RemoveTimerInternal(nsTimerImpl* aTimer);
|
||||
void ReleaseTimerInternal(nsTimerImpl* aTimer);
|
||||
|
||||
already_AddRefed<nsTimerImpl> PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef);
|
||||
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
Monitor mMonitor;
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsThreadManager.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "plarena.h"
|
||||
#include "pratom.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
@ -66,155 +65,6 @@ myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
|
||||
*stdDevResult = stdDev;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// TimerEventAllocator is a thread-safe allocator used only for nsTimerEvents.
|
||||
// It's needed to avoid contention over the default allocator lock when
|
||||
// firing timer events (see bug 733277). The thread-safety is required because
|
||||
// nsTimerEvent objects are allocated on the timer thread, and freed on another
|
||||
// thread. Because TimerEventAllocator has its own lock, contention over that
|
||||
// lock is limited to the allocation and deallocation of nsTimerEvent objects.
|
||||
//
|
||||
// Because this allocator is layered over PLArenaPool, it never shrinks -- even
|
||||
// "freed" nsTimerEvents aren't truly freed, they're just put onto a free-list
|
||||
// for later recycling. So the amount of memory consumed will always be equal
|
||||
// to the high-water mark consumption. But nsTimerEvents are small and it's
|
||||
// unusual to have more than a few hundred of them, so this shouldn't be a
|
||||
// problem in practice.
|
||||
|
||||
class TimerEventAllocator
|
||||
{
|
||||
private:
|
||||
struct FreeEntry
|
||||
{
|
||||
FreeEntry* mNext;
|
||||
};
|
||||
|
||||
PLArenaPool mPool;
|
||||
FreeEntry* mFirstFree;
|
||||
mozilla::Monitor mMonitor;
|
||||
|
||||
public:
|
||||
TimerEventAllocator()
|
||||
: mFirstFree(nullptr)
|
||||
, mMonitor("TimerEventAllocator")
|
||||
{
|
||||
PL_InitArenaPool(&mPool, "TimerEventPool", 4096, /* align = */ 0);
|
||||
}
|
||||
|
||||
~TimerEventAllocator()
|
||||
{
|
||||
PL_FinishArenaPool(&mPool);
|
||||
}
|
||||
|
||||
void* Alloc(size_t aSize);
|
||||
void Free(void* aPtr);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class nsTimerEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run();
|
||||
|
||||
nsTimerEvent()
|
||||
: mTimer()
|
||||
, mGeneration(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsTimerEvent);
|
||||
|
||||
MOZ_ASSERT(gThread->IsOnTimerThread(),
|
||||
"nsTimer must always be allocated on the timer thread");
|
||||
|
||||
sAllocatorUsers++;
|
||||
}
|
||||
|
||||
TimeStamp mInitTime;
|
||||
|
||||
static void Init();
|
||||
static void Shutdown();
|
||||
static void DeleteAllocatorIfNeeded();
|
||||
|
||||
static void* operator new(size_t aSize) CPP_THROW_NEW
|
||||
{
|
||||
return sAllocator->Alloc(aSize);
|
||||
}
|
||||
void operator delete(void* aPtr)
|
||||
{
|
||||
sAllocator->Free(aPtr);
|
||||
DeleteAllocatorIfNeeded();
|
||||
}
|
||||
|
||||
already_AddRefed<nsTimerImpl> ForgetTimer()
|
||||
{
|
||||
return mTimer.forget();
|
||||
}
|
||||
|
||||
void SetTimer(already_AddRefed<nsTimerImpl> aTimer)
|
||||
{
|
||||
mTimer = aTimer;
|
||||
mGeneration = mTimer->GetGeneration();
|
||||
}
|
||||
|
||||
private:
|
||||
~nsTimerEvent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsTimerEvent);
|
||||
|
||||
MOZ_ASSERT(!sCanDeleteAllocator || sAllocatorUsers > 0,
|
||||
"This will result in us attempting to deallocate the nsTimerEvent allocator twice");
|
||||
sAllocatorUsers--;
|
||||
}
|
||||
|
||||
nsRefPtr<nsTimerImpl> mTimer;
|
||||
int32_t mGeneration;
|
||||
|
||||
static TimerEventAllocator* sAllocator;
|
||||
static Atomic<int32_t> sAllocatorUsers;
|
||||
static bool sCanDeleteAllocator;
|
||||
};
|
||||
|
||||
TimerEventAllocator* nsTimerEvent::sAllocator = nullptr;
|
||||
Atomic<int32_t> nsTimerEvent::sAllocatorUsers;
|
||||
bool nsTimerEvent::sCanDeleteAllocator = false;
|
||||
|
||||
namespace {
|
||||
|
||||
void*
|
||||
TimerEventAllocator::Alloc(size_t aSize)
|
||||
{
|
||||
MOZ_ASSERT(aSize == sizeof(nsTimerEvent));
|
||||
|
||||
mozilla::MonitorAutoLock lock(mMonitor);
|
||||
|
||||
void* p;
|
||||
if (mFirstFree) {
|
||||
p = mFirstFree;
|
||||
mFirstFree = mFirstFree->mNext;
|
||||
} else {
|
||||
PL_ARENA_ALLOCATE(p, &mPool, aSize);
|
||||
if (!p) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
TimerEventAllocator::Free(void* aPtr)
|
||||
{
|
||||
mozilla::MonitorAutoLock lock(mMonitor);
|
||||
|
||||
FreeEntry* entry = reinterpret_cast<FreeEntry*>(aPtr);
|
||||
|
||||
entry->mNext = mFirstFree;
|
||||
mFirstFree = entry;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NS_IMPL_QUERY_INTERFACE(nsTimerImpl, nsITimer)
|
||||
NS_IMPL_ADDREF(nsTimerImpl)
|
||||
|
||||
@ -302,8 +152,6 @@ nsTimerImpl::Startup()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsTimerEvent::Init();
|
||||
|
||||
gThread = new TimerThread();
|
||||
|
||||
NS_ADDREF(gThread);
|
||||
@ -336,8 +184,6 @@ nsTimerImpl::Shutdown()
|
||||
|
||||
gThread->Shutdown();
|
||||
NS_RELEASE(gThread);
|
||||
|
||||
nsTimerEvent::Shutdown();
|
||||
}
|
||||
|
||||
|
||||
@ -359,23 +205,7 @@ nsTimerImpl::InitCommon(uint32_t aType, uint32_t aDelay)
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* In case of re-Init, both with and without a preceding Cancel, clear the
|
||||
* mCanceled flag and assign a new mGeneration. But first, remove any armed
|
||||
* timer from the timer thread's list.
|
||||
*
|
||||
* If we are racing with the timer thread to remove this timer and we lose,
|
||||
* the RemoveTimer call made here will fail to find this timer in the timer
|
||||
* thread's list, and will return false harmlessly. We test mArmed here to
|
||||
* avoid the small overhead in RemoveTimer of locking the timer thread and
|
||||
* checking its list for this timer. It's safe to test mArmed even though
|
||||
* it might be cleared on another thread in the next cycle (or even already
|
||||
* be cleared by another CPU whose store hasn't reached our CPU's cache),
|
||||
* because RemoveTimer is idempotent.
|
||||
*/
|
||||
if (mArmed) {
|
||||
gThread->RemoveTimer(this);
|
||||
}
|
||||
gThread->RemoveTimer(this);
|
||||
mCanceled = false;
|
||||
mTimeout = TimeStamp();
|
||||
mGeneration = gGenerator++;
|
||||
@ -661,117 +491,6 @@ nsTimerImpl::Fire()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsTimerEvent::Init()
|
||||
{
|
||||
sAllocator = new TimerEventAllocator();
|
||||
}
|
||||
|
||||
void
|
||||
nsTimerEvent::Shutdown()
|
||||
{
|
||||
sCanDeleteAllocator = true;
|
||||
DeleteAllocatorIfNeeded();
|
||||
}
|
||||
|
||||
void
|
||||
nsTimerEvent::DeleteAllocatorIfNeeded()
|
||||
{
|
||||
if (sCanDeleteAllocator && sAllocatorUsers == 0) {
|
||||
delete sAllocator;
|
||||
sAllocator = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTimerEvent::Run()
|
||||
{
|
||||
if (mGeneration != mTimer->GetGeneration()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
MOZ_LOG(GetTimerLog(), LogLevel::Debug,
|
||||
("[this=%p] time between PostTimerEvent() and Fire(): %fms\n",
|
||||
this, (now - mInitTime).ToMilliseconds()));
|
||||
}
|
||||
|
||||
mTimer->Fire();
|
||||
// Since nsTimerImpl is not thread-safe, we should release |mTimer|
|
||||
// here in the target thread to avoid race condition. Otherwise,
|
||||
// ~nsTimerEvent() which calls nsTimerImpl::Release() could run in the
|
||||
// timer thread and result in race condition.
|
||||
mTimer = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsTimerImpl>
|
||||
nsTimerImpl::PostTimerEvent(already_AddRefed<nsTimerImpl> aTimerRef)
|
||||
{
|
||||
nsRefPtr<nsTimerImpl> timer(aTimerRef);
|
||||
if (!timer->mEventTarget) {
|
||||
NS_ERROR("Attempt to post timer event to NULL event target");
|
||||
return timer.forget();
|
||||
}
|
||||
|
||||
// XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
|
||||
|
||||
// Since TimerThread addref'd 'timer' for us, we don't need to addref here.
|
||||
// We will release either in ~nsTimerEvent(), or pass the reference back to
|
||||
// the caller. We need to copy the generation number from this timer into the
|
||||
// event, so we can avoid firing a timer that was re-initialized after being
|
||||
// canceled.
|
||||
|
||||
// Note: We override operator new for this class, and the override is
|
||||
// fallible!
|
||||
nsRefPtr<nsTimerEvent> event = new nsTimerEvent;
|
||||
if (!event) {
|
||||
return timer.forget();
|
||||
}
|
||||
|
||||
if (MOZ_LOG_TEST(GetTimerLog(), LogLevel::Debug)) {
|
||||
event->mInitTime = TimeStamp::Now();
|
||||
}
|
||||
|
||||
// If this is a repeating precise timer, we need to calculate the time for
|
||||
// the next timer to fire before we make the callback.
|
||||
if (timer->IsRepeatingPrecisely()) {
|
||||
timer->SetDelayInternal(timer->mDelay);
|
||||
|
||||
// But only re-arm REPEATING_PRECISE timers.
|
||||
if (gThread && timer->mType == TYPE_REPEATING_PRECISE) {
|
||||
nsresult rv = gThread->AddTimer(timer);
|
||||
if (NS_FAILED(rv)) {
|
||||
return timer.forget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
// During the dispatch of TimerEvent, we overwrite the current TraceInfo
|
||||
// partially with the info saved in timer earlier, and restore it back by
|
||||
// AutoSaveCurTraceInfo.
|
||||
AutoSaveCurTraceInfo saveCurTraceInfo;
|
||||
(timer->GetTracedTask()).SetTLSTraceInfo();
|
||||
#endif
|
||||
|
||||
nsIEventTarget* target = timer->mEventTarget;
|
||||
event->SetTimer(timer.forget());
|
||||
|
||||
nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
timer = event->ForgetTimer();
|
||||
if (gThread) {
|
||||
gThread->RemoveTimer(timer);
|
||||
}
|
||||
return timer.forget();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
nsTimerImpl::SetDelayInternal(uint32_t aDelay)
|
||||
{
|
||||
|
@ -42,8 +42,8 @@ public:
|
||||
static void Shutdown();
|
||||
|
||||
friend class TimerThread;
|
||||
friend struct TimerAdditionComparator;
|
||||
friend class nsTimerEvent;
|
||||
friend struct TimerAdditionComparator;
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSITIMER
|
||||
|
Loading…
Reference in New Issue
Block a user