Merge m-c to b2g-inbound. a=merge

CLOSED TREE
This commit is contained in:
Ryan VanderMeulen 2015-07-30 11:57:28 -04:00
commit 20495ab522
98 changed files with 2124 additions and 1010 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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>

View File

@ -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]

View 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);
});

View File

@ -1,5 +0,0 @@
function test(){
var tab = gBrowser.addTab();
ok(tab.getAttribute("closetabtext") != "", "tab has non-empty closetabtext");
gBrowser.removeTab(tab);
}

View File

@ -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");
}

View File

@ -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: [],

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
<audio src="audio.ogg" controls loop>

View File

@ -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 {

View File

@ -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.

View File

@ -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");
});

View File

@ -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]

View File

@ -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);
}

View File

@ -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>

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View 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

View 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

View File

@ -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;
}

View File

@ -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)

View File

@ -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();

View 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;

View File

@ -17,6 +17,7 @@ SOURCES += [
'TestNeedsNoVTableType.cpp',
'TestNoAddRefReleaseOnReturn.cpp',
'TestNoArithmeticExprInArgument.cpp',
'TestNoDuplicateRefCntMember.cpp',
'TestNonHeapClass.cpp',
'TestNonMemMovable.cpp',
'TestNoRefcountedInsideLambdas.cpp',

View File

@ -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.

View File

@ -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
]

View File

@ -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();
}

View File

@ -2439,6 +2439,8 @@ public:
nsIDocument* aDoc,
nsIHttpChannel* aChannel);
static bool PushEnabled(JSContext* aCx, JSObject* aObj);
private:
static bool InitializeEventTable();

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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>

View File

@ -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;
}

View File

@ -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();

View File

@ -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,

View File

@ -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)

View File

@ -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) {

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -186,7 +186,7 @@ protected:
return true;
}
mGLContext = GLContextProvider::CreateHeadless(false);
mGLContext = GLContextProvider::CreateHeadless(CreateContextFlags::NONE);
return mGLContext;
}

View File

@ -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
{

View File

@ -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;

View File

@ -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)
{

View File

@ -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*

View File

@ -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);

View File

@ -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;
};
};

View File

@ -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);

View File

@ -7,7 +7,8 @@
* https://w3c.github.io/push-api/
*/
[Exposed=ServiceWorker]
[Func="nsContentUtils::PushEnabled",
Exposed=ServiceWorker]
interface PushMessageData
{
// FIXME(nsm): Bug 1149195.

View File

@ -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
{

View File

@ -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
};

View File

@ -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;

View File

@ -20,6 +20,7 @@
#endif
#include "nsProxyRelease.h"
#include "nsContentUtils.h"
class nsIInterceptedChannel;

View File

@ -1328,6 +1328,13 @@ public:
return mPreferences[WORKERPREF_PERFORMANCE_LOGGING_ENABLED];
}
bool
PushEnabled() const
{
AssertIsOnWorkerThread();
return mPreferences[WORKERPREF_PUSH];
}
bool
OnLine() const
{

View File

@ -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
};

View File

@ -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!

View File

@ -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!

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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();
}

View File

@ -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

View File

@ -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();
}

View File

@ -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.");

View File

@ -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);

View File

@ -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)

View File

@ -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)`

View 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

View File

@ -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

View File

@ -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",

View File

@ -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.

View File

@ -56,9 +56,8 @@ protected:
private:
void EnumeratePrinters();
void StartPrintJob();
static gboolean PrinterEnumerator(GtkPrinter *aPrinter, gpointer aData);
static void StartPrintJob(nsDeviceContextSpecGTK *spec,
GtkPrinter *printer);
};
//-------------------------------------------------------------------------

View File

@ -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(&notifiedThread, 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]);

View File

@ -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()
{

View File

@ -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;

View File

@ -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)
{

View File

@ -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