Merge mozilla-central to inbound. a=merge CLOSED TREE

This commit is contained in:
Gurzau Raul 2019-03-01 09:28:28 +02:00
commit 6b94d177d1
203 changed files with 2045 additions and 3108 deletions

View File

@ -26,7 +26,7 @@
</div> </div>
<div id="errorLongContent"> <div id="errorLongContent">
<div id="errorLongDesc"> <div id="errorLongDesc">
<p data-l10n-id="restart-required-intro"></p> <p data-l10n-id="restart-required-intro-brand"></p>
<p data-l10n-id="restart-required-description"></p> <p data-l10n-id="restart-required-description"></p>
</div> </div>
</div> </div>

View File

@ -261,7 +261,7 @@ var CaptivePortalWatcher = {
gBrowser.selectedTab = tab; gBrowser.selectedTab = tab;
let canonicalURI = makeURI(this.canonicalURL); let canonicalURI = Services.io.newURI(this.canonicalURL);
// When we are no longer captive, close the tab if it's at the canonical URL. // When we are no longer captive, close the tab if it's at the canonical URL.
let tabCloser = () => { let tabCloser = () => {

View File

@ -44,8 +44,6 @@ async function submitForm(event) {
event.preventDefault(); event.preventDefault();
let input = document.getElementById("sync-input"); let input = document.getElementById("sync-input");
input.disabled = true;
document.getElementById("sync-button").disabled = true;
let { flowId, flowBeginTime } = await metrics; let { flowId, flowBeginTime } = await metrics;
@ -59,6 +57,7 @@ async function submitForm(event) {
}); });
window.open(requestURL, "_blank", "noopener"); window.open(requestURL, "_blank", "noopener");
document.getElementById("sync").hidden = true;
} }
const endpoint = RPMGetFxAccountsEndpoint(ENTRYPOINT); const endpoint = RPMGetFxAccountsEndpoint(ENTRYPOINT);

View File

@ -2429,7 +2429,8 @@
if (event.originalTarget.classList.contains("tab-icon-sound") || if (event.originalTarget.classList.contains("tab-icon-sound") ||
(event.originalTarget.classList.contains("tab-icon-overlay") && (event.originalTarget.classList.contains("tab-icon-overlay") &&
(event.originalTarget.hasAttribute("soundplaying") || (event.originalTarget.hasAttribute("soundplaying") ||
event.originalTarget.hasAttribute("muted")))) { event.originalTarget.hasAttribute("muted") ||
event.originalTarget.hasAttribute("activemedia-blocked")))) {
if (this.multiselected) { if (this.multiselected) {
gBrowser.toggleMuteAudioOnMultiSelectedTabs(this); gBrowser.toggleMuteAudioOnMultiSelectedTabs(this);
} else { } else {

View File

@ -27,10 +27,9 @@ add_task(async function findbar_test() {
ok(!gFindBar.hidden, "the Find bar isn't hidden after the location of a " + ok(!gFindBar.hidden, "the Find bar isn't hidden after the location of a " +
"subdocument changes"); "subdocument changes");
let findBarClosePromise = promiseWaitForEvent(gBrowser, "findbarclose"); let findBarClosePromise = BrowserTestUtils.waitForEvent(gBrowser, "findbarclose");
gFindBar.close(); gFindBar.close();
await findBarClosePromise; await findBarClosePromise;
gBrowser.removeTab(newTab); gBrowser.removeTab(newTab);
}); });

View File

@ -8,6 +8,7 @@ prefs =
# Skip migration work in BG__migrateUI for browser_startup.js since it isn't # Skip migration work in BG__migrateUI for browser_startup.js since it isn't
# representative of common startup. # representative of common startup.
browser.migration.version=9999999 browser.migration.version=9999999
browser.urlbar.quantumbar=true
browser.startup.record=true browser.startup.record=true
gfx.canvas.willReadFrequently.enable=true gfx.canvas.willReadFrequently.enable=true
# The form autofill framescript is only used in certain locales if this # The form autofill framescript is only used in certain locales if this

View File

@ -1,8 +1,6 @@
"use strict"; "use strict";
// There are a _lot_ of reflows in this test, and processing them takes // This tests searching in the urlbar (a.k.a. the quantumbar).
// time. On slower builds, we need to boost our allowed test time.
requestLongerTimeout(5);
/** /**
* WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This * WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This
@ -14,186 +12,42 @@ requestLongerTimeout(5);
* for tips on how to do that. * for tips on how to do that.
*/ */
/* These reflows happen only the first time the awesomebar panel opens. */ /* These reflows happen only the first time the panel opens. */
const EXPECTED_REFLOWS_FIRST_OPEN = []; const EXPECTED_REFLOWS_FIRST_OPEN = [
if (AppConstants.platform != "macosx" && // This is the this.panel.openPopup() call in UrlbarView._openPanel. See bug
(AppConstants.DEBUG || // 1359989, which was filed against the legacy awesomebar but applies here too
AppConstants.platform == "win")) { // because it seems to be caused by platform code.
EXPECTED_REFLOWS_FIRST_OPEN.push({ {
stack: [
"_openPanel@resource:///modules/UrlbarView.jsm",
"onQueryResults@resource:///modules/UrlbarView.jsm",
"_notify@resource:///modules/UrlbarController.jsm",
"receiveResults@resource:///modules/UrlbarController.jsm",
"notifyResults@resource:///modules/UrlbarProvidersManager.jsm",
"add@resource:///modules/UrlbarProvidersManager.jsm",
"onSearchResult@resource:///modules/UrlbarProviderUnifiedComplete.jsm",
],
},
{
stack: [ stack: [
"__rebuild@chrome://browser/content/search/search-one-offs.js", "__rebuild@chrome://browser/content/search/search-one-offs.js",
/* This is limited to a one-line stack, because the next item is an async /* This is limited to a one-line stack, because the next item is an async
function and as such not supported on all trees, according to bug 1501761. function and as such not supported on all trees, according to bug 1501761.
"async*set popup@chrome://browser/content/search/search-one-offs.js", "async*_rebuild@chrome://browser/content/search/search-one-offs.js",
"_syncOneOffSearchesEnabled@chrome://browser/content/urlbarBindings.xml", "async*_on_popupshowing@chrome://browser/content/search/search-one-offs.js",
"toggleOneOffSearches@chrome://browser/content/urlbarBindings.xml", "handleEvent@chrome://browser/content/search/search-one-offs.js",
"_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml", "_openPanel@resource:///modules/UrlbarView.jsm",
"@chrome://browser/content/urlbarBindings.xml", "onQueryResults@resource:///modules/UrlbarView.jsm",
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml", "_notify@resource:///modules/UrlbarController.jsm",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml", "receiveResults@resource:///modules/UrlbarController.jsm",
"openPopup@chrome://global/content/bindings/autocomplete.xml", "notifyResults@resource:///modules/UrlbarProvidersManager.jsm",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",*/ "add@resource:///modules/UrlbarProvidersManager.jsm",
"onSearchResult@resource:///modules/UrlbarProviderUnifiedComplete.jsm",
*/
], ],
});
}
EXPECTED_REFLOWS_FIRST_OPEN.push(
{
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
"invalidate@chrome://global/content/bindings/autocomplete.xml",
],
maxCount: 60, // This number should only ever go down - never up.
}, },
];
{ add_task(async function quantumbar() {
stack: [ await runUrlbarTest(false, true, EXPECTED_REFLOWS_FIRST_OPEN);
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openPopup@chrome://global/content/bindings/autocomplete.xml",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
],
maxCount: 6, // This number should only ever go down - never up.
},
// Bug 1359989
{
stack: [
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openPopup@chrome://global/content/bindings/autocomplete.xml",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
],
}
);
// These extra reflows happen on beta/release as one of the default bookmarks in
// bookmarks.html.in has a long URL.
if (AppConstants.RELEASE_OR_BETA) {
EXPECTED_REFLOWS_FIRST_OPEN.push({
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_onUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"MozAutocompleteRichlistitem/<@chrome://global/content/elements/autocomplete-richlistitem.js",
],
maxCount: 6,
},
{
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_onOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"MozAutocompleteRichlistitem/<@chrome://global/content/elements/autocomplete-richlistitem.js",
],
maxCount: 6,
},
{
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_adjustAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
"invalidate@chrome://global/content/bindings/autocomplete.xml",
],
maxCount: 12,
});
}
/**
* Returns a Promise that resolves once the AwesomeBar popup for a particular
* window has appeared after having done a search for its input text.
*
* @param win (browser window)
* The window to do the search in.
* @returns Promise
*/
async function promiseSearchComplete(win) {
let URLBar = win.gURLBar;
if (URLBar.popup.state != "open") {
await BrowserTestUtils.waitForEvent(URLBar.popup, "popupshown");
}
await BrowserTestUtils.waitForCondition(() => {
return URLBar.controller.searchStatus >=
Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH;
});
// There are several setTimeout(fn, 0); calls inside autocomplete.xml
// that we need to wait for. Since those have higher priority than
// idle callbacks, we can be sure they will have run once this
// idle callback is called. The timeout seems to be required in
// automation - presumably because the machines can be pretty busy
// especially if it's GC'ing from previous tests.
await new Promise(resolve => win.requestIdleCallback(resolve, { timeout: 1000 }));
}
add_task(async function setup() {
await addDummyHistoryEntries();
});
/**
* This test ensures that there are no unexpected uninterruptible reflows when
* typing into the URL bar with the default values in Places.
*/
add_task(async function() {
let win = await prepareSettledWindow();
let URLBar = win.gURLBar;
let popup = URLBar.popup;
URLBar.focus();
URLBar.value = "";
let dropmarkerRect = document.getAnonymousElementByAttribute(gURLBar,
"anonid", "historydropmarker").getBoundingClientRect();
let textBoxRect = document.getAnonymousElementByAttribute(gURLBar,
"anonid", "moz-input-box").getBoundingClientRect();
await withPerfObserver(async function() {
let oldInvalidate = popup.invalidate.bind(popup);
let oldResultsAdded = popup.onResultsAdded.bind(popup);
// We need to invalidate the frame tree outside of the normal
// mechanism since invalidations and result additions to the
// URL bar occur without firing JS events (which is how we
// normally know to dirty the frame tree).
popup.invalidate = (reason) => {
dirtyFrame(win);
oldInvalidate(reason);
};
popup.onResultsAdded = () => {
dirtyFrame(win);
oldResultsAdded();
};
// Only keying in 6 characters because the number of reflows triggered
// is so high that we risk timing out the test if we key in any more.
const SEARCH_TERM = "ows-10";
for (let i = 0; i < SEARCH_TERM.length; ++i) {
let char = SEARCH_TERM[i];
EventUtils.synthesizeKey(char, {}, win);
await promiseSearchComplete(win);
}
let hiddenPromise = BrowserTestUtils.waitForEvent(URLBar.popup, "popuphidden");
EventUtils.synthesizeKey("VK_ESCAPE", {}, win);
await hiddenPromise;
}, {expectedReflows: EXPECTED_REFLOWS_FIRST_OPEN,
frames: {filter: rects => rects.filter(r => !(
// We put text into the urlbar so expect its textbox to change.
(r.x1 >= textBoxRect.left && r.x2 <= textBoxRect.right &&
r.y1 >= textBoxRect.top && r.y2 <= textBoxRect.bottom) ||
// The dropmarker is replaced with the go arrow during the test.
// dropmarkerRect.left isn't always an integer, hence the - 1 and + 1
(r.x1 >= dropmarkerRect.left - 1 && r.x2 <= dropmarkerRect.right + 1 &&
r.y1 >= dropmarkerRect.top && r.y2 <= dropmarkerRect.bottom)
// XXX For some reason the awesomebar panel isn't in our screenshots,
// but that's where we actually expect many changes.
))}}, win);
await BrowserTestUtils.closeWindow(win);
}); });

View File

@ -0,0 +1,111 @@
"use strict";
// This tests searching in the legacy urlbar implementation (a.k.a. the
// awesomebar).
// There are a _lot_ of reflows in this test, and processing them takes
// time. On slower builds, we need to boost our allowed test time.
requestLongerTimeout(5);
/**
* WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This
* is a whitelist that should slowly go away as we improve the performance of
* the front-end. Instead of adding more reflows to the whitelist, you should
* be modifying your code to avoid the reflow.
*
* See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
* for tips on how to do that.
*/
/* These reflows happen only the first time the awesomebar panel opens. */
const EXPECTED_REFLOWS_FIRST_OPEN = [];
if (AppConstants.platform != "macosx" &&
(AppConstants.DEBUG ||
AppConstants.platform == "win")) {
EXPECTED_REFLOWS_FIRST_OPEN.push({
stack: [
"__rebuild@chrome://browser/content/search/search-one-offs.js",
/* This is limited to a one-line stack, because the next item is an async
function and as such not supported on all trees, according to bug 1501761.
"async*set popup@chrome://browser/content/search/search-one-offs.js",
"_syncOneOffSearchesEnabled@chrome://browser/content/urlbarBindings.xml",
"toggleOneOffSearches@chrome://browser/content/urlbarBindings.xml",
"_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
"@chrome://browser/content/urlbarBindings.xml",
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openPopup@chrome://global/content/bindings/autocomplete.xml",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",*/
],
});
}
EXPECTED_REFLOWS_FIRST_OPEN.push(
{
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
"invalidate@chrome://global/content/bindings/autocomplete.xml",
],
maxCount: 60, // This number should only ever go down - never up.
},
{
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openPopup@chrome://global/content/bindings/autocomplete.xml",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
],
maxCount: 6, // This number should only ever go down - never up.
},
// Bug 1359989
{
stack: [
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openPopup@chrome://global/content/bindings/autocomplete.xml",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
],
}
);
// These extra reflows happen on beta/release as one of the default bookmarks in
// bookmarks.html.in has a long URL.
if (AppConstants.RELEASE_OR_BETA) {
EXPECTED_REFLOWS_FIRST_OPEN.push({
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_onUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"MozAutocompleteRichlistitem/<@chrome://global/content/elements/autocomplete-richlistitem.js",
],
maxCount: 6,
},
{
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_onOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"MozAutocompleteRichlistitem/<@chrome://global/content/elements/autocomplete-richlistitem.js",
],
maxCount: 6,
},
{
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_adjustAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
"invalidate@chrome://global/content/bindings/autocomplete.xml",
],
maxCount: 12,
});
}
add_task(async function awesomebar() {
await runUrlbarTest(true, true, EXPECTED_REFLOWS_FIRST_OPEN);
});

View File

@ -1,8 +1,6 @@
"use strict"; "use strict";
// There are a _lot_ of reflows in this test, and processing them takes // This tests searching in the urlbar (a.k.a. the quantumbar).
// time. On slower builds, we need to boost our allowed test time.
requestLongerTimeout(5);
/** /**
* WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This * WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This
@ -14,185 +12,58 @@ requestLongerTimeout(5);
* for tips on how to do that. * for tips on how to do that.
*/ */
/* These reflows happen only the first time the awesomebar panel opens. */ /* These reflows happen only the first time the panel opens. */
const EXPECTED_REFLOWS_FIRST_OPEN = []; const EXPECTED_REFLOWS_FIRST_OPEN = [
if (AppConstants.platform != "macosx" && {
(AppConstants.DEBUG || stack: [
AppConstants.platform == "linux" || "_openPanel@resource:///modules/UrlbarView.jsm",
AppConstants.isPlatformAndVersionAtLeast("win", "10"))) { "onQueryResults@resource:///modules/UrlbarView.jsm",
EXPECTED_REFLOWS_FIRST_OPEN.push({ "_notify@resource:///modules/UrlbarController.jsm",
"receiveResults@resource:///modules/UrlbarController.jsm",
"notifyResults@resource:///modules/UrlbarProvidersManager.jsm",
"add@resource:///modules/UrlbarProvidersManager.jsm",
"onSearchResult@resource:///modules/UrlbarProviderUnifiedComplete.jsm",
],
},
{
stack: [ stack: [
"__rebuild@chrome://browser/content/search/search-one-offs.js", "__rebuild@chrome://browser/content/search/search-one-offs.js",
/* This is limited to a one-line stack, because the next item is an async /* This is limited to a one-line stack, because the next item is an async
function and as such not supported on all trees, according to bug 1501761. function and as such not supported on all trees, according to bug 1501761.
"async*set popup@chrome://browser/content/search/search-one-offs.js", "async*_rebuild@chrome://browser/content/search/search-one-offs.js",
"_syncOneOffSearchesEnabled@chrome://browser/content/urlbarBindings.xml", "async*_on_popupshowing@chrome://browser/content/search/search-one-offs.js",
"toggleOneOffSearches@chrome://browser/content/urlbarBindings.xml", "handleEvent@chrome://browser/content/search/search-one-offs.js",
"_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml", "_openPanel@resource:///modules/UrlbarView.jsm",
"@chrome://browser/content/urlbarBindings.xml", "onQueryResults@resource:///modules/UrlbarView.jsm",
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml", "_notify@resource:///modules/UrlbarController.jsm",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml", "receiveResults@resource:///modules/UrlbarController.jsm",
"openPopup@chrome://global/content/bindings/autocomplete.xml", "notifyResults@resource:///modules/UrlbarProvidersManager.jsm",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",*/ "add@resource:///modules/UrlbarProvidersManager.jsm",
], "onSearchResult@resource:///modules/UrlbarProviderUnifiedComplete.jsm",
}); */
}
EXPECTED_REFLOWS_FIRST_OPEN.push(
{
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
"invalidate@chrome://global/content/bindings/autocomplete.xml",
],
maxCount: 36, // This number should only ever go down - never up.
},
{
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openPopup@chrome://global/content/bindings/autocomplete.xml",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
],
maxCount: 6, // This number should only ever go down - never up.
},
// Bug 1359989
{
stack: [
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openPopup@chrome://global/content/bindings/autocomplete.xml",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
],
}
);
/* These reflows happen everytime the awesomebar panel opens. */
const EXPECTED_REFLOWS_SECOND_OPEN = [
{
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
"invalidate@chrome://global/content/bindings/autocomplete.xml",
],
maxCount: 24, // This number should only ever go down - never up.
},
// Bug 1359989
{
stack: [
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openPopup@chrome://global/content/bindings/autocomplete.xml",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
], ],
}, },
]; ];
const SEARCH_TERM = "urlbar-reflows-" + Date.now(); /* These reflows happen every time the panel opens. */
const EXPECTED_REFLOWS_SECOND_OPEN = [
// This is the this.panel.openPopup() call in UrlbarView._openPanel. See bug
// 1359989, which was filed against the legacy awesomebar but applies here too
// because it seems to be caused by platform code.
{
stack: [
"_openPanel@resource:///modules/UrlbarView.jsm",
"onQueryResults@resource:///modules/UrlbarView.jsm",
"_notify@resource:///modules/UrlbarController.jsm",
"receiveResults@resource:///modules/UrlbarController.jsm",
"notifyResults@resource:///modules/UrlbarProvidersManager.jsm",
"add@resource:///modules/UrlbarProvidersManager.jsm",
"onSearchResult@resource:///modules/UrlbarProviderUnifiedComplete.jsm",
],
},
];
add_task(async function setup() { add_task(async function quantumbar() {
await addDummyHistoryEntries(SEARCH_TERM); await runUrlbarTest(false, false, EXPECTED_REFLOWS_FIRST_OPEN,
}); EXPECTED_REFLOWS_SECOND_OPEN);
/**
* This test ensures that there are no unexpected
* uninterruptible reflows when typing into the URL bar
* with the default values in Places.
*/
add_task(async function() {
let win = await prepareSettledWindow();
let URLBar = win.gURLBar;
let popup = URLBar.popup;
URLBar.focus();
URLBar.value = SEARCH_TERM;
let testFn = async function() {
let oldInvalidate = popup.invalidate.bind(popup);
let oldResultsAdded = popup.onResultsAdded.bind(popup);
let oldSetTimeout = win.setTimeout;
// We need to invalidate the frame tree outside of the normal
// mechanism since invalidations and result additions to the
// URL bar occur without firing JS events (which is how we
// normally know to dirty the frame tree).
popup.invalidate = (reason) => {
dirtyFrame(win);
oldInvalidate(reason);
};
popup.onResultsAdded = () => {
dirtyFrame(win);
oldResultsAdded();
};
win.setTimeout = (fn, ms) => {
return oldSetTimeout(() => {
dirtyFrame(win);
fn();
}, ms);
};
URLBar.controller.startSearch(URLBar.value);
await BrowserTestUtils.waitForEvent(URLBar.popup, "popupshown");
await BrowserTestUtils.waitForCondition(() => {
return URLBar.controller.searchStatus >=
Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH;
});
let matchCount = URLBar.popup.matchCount;
await BrowserTestUtils.waitForCondition(() => {
return URLBar.popup.richlistbox.children.length == matchCount;
});
URLBar.controller.stopSearch();
// There are several setTimeout(fn, 0); calls inside autocomplete.xml
// that we need to wait for. Since those have higher priority than
// idle callbacks, we can be sure they will have run once this
// idle callback is called. The timeout seems to be required in
// automation - presumably because the machines can be pretty busy
// especially if it's GC'ing from previous tests.
await new Promise(resolve => win.requestIdleCallback(resolve, { timeout: 1000 }));
let hiddenPromise = BrowserTestUtils.waitForEvent(URLBar.popup, "popuphidden");
EventUtils.synthesizeKey("VK_ESCAPE", {}, win);
await hiddenPromise;
};
let dropmarkerRect = document.getAnonymousElementByAttribute(gURLBar,
"anonid", "historydropmarker").getBoundingClientRect();
let textBoxRect = document.getAnonymousElementByAttribute(gURLBar,
"anonid", "moz-input-box").getBoundingClientRect();
let expectedRects = {
filter: rects => rects.filter(r => !(
// We put text into the urlbar so expect its textbox to change.
(r.x1 >= textBoxRect.left && r.x2 <= textBoxRect.right &&
r.y1 >= textBoxRect.top && r.y2 <= textBoxRect.bottom) ||
// The dropmarker is displayed as active during some of the test.
// dropmarkerRect.left isn't always an integer, hence the - 1 and + 1
(r.x1 >= dropmarkerRect.left - 1 && r.x2 <= dropmarkerRect.right + 1 &&
r.y1 >= dropmarkerRect.top && r.y2 <= dropmarkerRect.bottom)
// XXX For some reason the awesomebar panel isn't in our screenshots,
// but that's where we actually expect many changes.
)),
};
info("First opening");
await withPerfObserver(testFn, {expectedReflows: EXPECTED_REFLOWS_FIRST_OPEN,
frames: expectedRects}, win);
info("Second opening");
await withPerfObserver(testFn, {expectedReflows: EXPECTED_REFLOWS_SECOND_OPEN,
frames: expectedRects}, win);
await BrowserTestUtils.closeWindow(win);
}); });

View File

@ -0,0 +1,107 @@
"use strict";
// This tests searching in the legacy urlbar implementation (a.k.a. the
// awesomebar).
// There are a _lot_ of reflows in this test, and processing them takes
// time. On slower builds, we need to boost our allowed test time.
requestLongerTimeout(5);
/**
* WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This
* is a whitelist that should slowly go away as we improve the performance of
* the front-end. Instead of adding more reflows to the whitelist, you should
* be modifying your code to avoid the reflow.
*
* See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
* for tips on how to do that.
*/
/* These reflows happen only the first time the awesomebar panel opens. */
const EXPECTED_REFLOWS_FIRST_OPEN = [];
if (AppConstants.platform != "macosx" &&
(AppConstants.DEBUG ||
AppConstants.platform == "linux" ||
AppConstants.isPlatformAndVersionAtLeast("win", "10"))) {
EXPECTED_REFLOWS_FIRST_OPEN.push({
stack: [
"__rebuild@chrome://browser/content/search/search-one-offs.js",
/* This is limited to a one-line stack, because the next item is an async
function and as such not supported on all trees, according to bug 1501761.
"async*set popup@chrome://browser/content/search/search-one-offs.js",
"_syncOneOffSearchesEnabled@chrome://browser/content/urlbarBindings.xml",
"toggleOneOffSearches@chrome://browser/content/urlbarBindings.xml",
"_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
"@chrome://browser/content/urlbarBindings.xml",
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openPopup@chrome://global/content/bindings/autocomplete.xml",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",*/
],
});
}
EXPECTED_REFLOWS_FIRST_OPEN.push(
{
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
"invalidate@chrome://global/content/bindings/autocomplete.xml",
],
maxCount: 36, // This number should only ever go down - never up.
},
{
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openPopup@chrome://global/content/bindings/autocomplete.xml",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
],
maxCount: 6, // This number should only ever go down - never up.
},
// Bug 1359989
{
stack: [
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openPopup@chrome://global/content/bindings/autocomplete.xml",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
],
}
);
/* These reflows happen everytime the awesomebar panel opens. */
const EXPECTED_REFLOWS_SECOND_OPEN = [
{
stack: [
"_handleOverflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"handleOverUnderflow@chrome://global/content/elements/autocomplete-richlistitem.js",
"_reuseAcItem@chrome://global/content/elements/autocomplete-richlistitem.js",
"_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
"_invalidate@chrome://global/content/bindings/autocomplete.xml",
"invalidate@chrome://global/content/bindings/autocomplete.xml",
],
maxCount: 24, // This number should only ever go down - never up.
},
// Bug 1359989
{
stack: [
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
"openPopup@chrome://global/content/bindings/autocomplete.xml",
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
],
},
];
add_task(async function awesomebar() {
await runUrlbarTest(true, false, EXPECTED_REFLOWS_FIRST_OPEN,
EXPECTED_REFLOWS_SECOND_OPEN);
});

View File

@ -1,10 +1,10 @@
"use strict"; "use strict";
ChromeUtils.defineModuleGetter(this, "PlacesUtils", XPCOMUtils.defineLazyModuleGetters(this, {
"resource://gre/modules/PlacesUtils.jsm"); PlacesTestUtils: "resource://testing-common/PlacesTestUtils.jsm",
ChromeUtils.defineModuleGetter(this, "PlacesTestUtils", PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
"resource://testing-common/PlacesTestUtils.jsm"); UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.jsm",
});
/** /**
* This function can be called if the test needs to trigger frame dirtying * This function can be called if the test needs to trigger frame dirtying
@ -639,3 +639,121 @@ async function withPerfObserver(testFn, exceptions = {}, win = window) {
let frames = await promiseFrames; let frames = await promiseFrames;
reportUnexpectedFlicker(frames, exceptions.frames); reportUnexpectedFlicker(frames, exceptions.frames);
} }
/**
* This test ensures that there are no unexpected
* uninterruptible reflows when typing into the URL bar
* with the default values in Places.
*
* @param {bool} useAwesomebar
* Pass true if the legacy awesomebar is enabled. Pass false if the
* quantumbar is enabled.
* @param {bool} keyed
* Pass true to synthesize typing the search string one key at a time.
* @param {array} expectedReflowsFirstOpen
* The array of expected reflow stacks when the panel is first opened.
* @param {array} [expectedReflowsSecondOpen]
* The array of expected reflow stacks when the panel is subsequently
* opened, if you're testing opening the panel twice.
*/
async function runUrlbarTest(useAwesomebar,
keyed,
expectedReflowsFirstOpen,
expectedReflowsSecondOpen = null) {
const SEARCH_TERM = keyed ? "" : "urlbar-reflows-" + Date.now();
await addDummyHistoryEntries(SEARCH_TERM);
let win = await prepareSettledWindow();
let URLBar = win.gURLBar;
URLBar.focus();
URLBar.value = SEARCH_TERM;
let testFn = async function() {
if (useAwesomebar) {
let popup = URLBar.popup;
let oldInvalidate = popup.invalidate.bind(popup);
let oldResultsAdded = popup.onResultsAdded.bind(popup);
let oldSetTimeout = win.setTimeout;
// We need to invalidate the frame tree outside of the normal
// mechanism since invalidations and result additions to the
// URL bar occur without firing JS events (which is how we
// normally know to dirty the frame tree).
popup.invalidate = (reason) => {
dirtyFrame(win);
oldInvalidate(reason);
};
popup.onResultsAdded = () => {
dirtyFrame(win);
oldResultsAdded();
};
win.setTimeout = (fn, ms) => {
return oldSetTimeout(() => {
dirtyFrame(win);
fn();
}, ms);
};
}
let waitExtra = async () => {
// There are several setTimeout(fn, 0); calls inside autocomplete.xml
// that we need to wait for. Since those have higher priority than
// idle callbacks, we can be sure they will have run once this
// idle callback is called. The timeout seems to be required in
// automation - presumably because the machines can be pretty busy
// especially if it's GC'ing from previous tests.
await new Promise(resolve => win.requestIdleCallback(resolve, { timeout: 1000 }));
};
if (keyed) {
// Only keying in 6 characters because the number of reflows triggered
// is so high that we risk timing out the test if we key in any more.
let searchTerm = "ows-10";
for (let i = 0; i < searchTerm.length; ++i) {
let char = searchTerm[i];
EventUtils.synthesizeKey(char, {}, win);
await UrlbarTestUtils.promiseSearchComplete(win);
await waitExtra();
}
} else {
await UrlbarTestUtils.promiseAutocompleteResultPopup(win, URLBar.value,
SimpleTest.waitForFocus);
await waitExtra();
}
await UrlbarTestUtils.promisePopupClose(win);
};
let dropmarkerRect = win.document.getAnonymousElementByAttribute(URLBar.textbox,
"anonid", "historydropmarker").getBoundingClientRect();
let textBoxRect = win.document.getAnonymousElementByAttribute(URLBar.textbox,
"anonid", "moz-input-box").getBoundingClientRect();
let expectedRects = {
filter: rects => rects.filter(r => !(
// We put text into the urlbar so expect its textbox to change.
(r.x1 >= textBoxRect.left && r.x2 <= textBoxRect.right &&
r.y1 >= textBoxRect.top && r.y2 <= textBoxRect.bottom) ||
// The dropmarker is displayed as active during some of the test.
// dropmarkerRect.left isn't always an integer, hence the - 1 and + 1
(r.x1 >= dropmarkerRect.left - 1 && r.x2 <= dropmarkerRect.right + 1 &&
r.y1 >= dropmarkerRect.top && r.y2 <= dropmarkerRect.bottom)
// XXX For some reason the awesomebar panel isn't in our screenshots,
// but that's where we actually expect many changes.
)),
};
info(`First opening, useAwesomebar=${useAwesomebar}`);
await withPerfObserver(testFn, {expectedReflows: expectedReflowsFirstOpen,
frames: expectedRects}, win);
if (expectedReflowsSecondOpen) {
info(`Second opening, useAwesomebar=${useAwesomebar}`);
await withPerfObserver(testFn, {expectedReflows: expectedReflowsSecondOpen,
frames: expectedRects}, win);
}
await BrowserTestUtils.closeWindow(win);
}

View File

@ -0,0 +1,8 @@
[DEFAULT]
prefs =
browser.urlbar.quantumbar=false
[../browser_urlbar_keyed_search_legacy.js]
skip-if = (os == 'linux') || (os == 'win' && debug) || (os == 'win' && bits == 32) # Disabled on Linux and Windows debug due to perma failures. Bug 1392320. Disabled on Win32 because of intermittent OOM failures (bug 1448241).
[../browser_urlbar_search_legacy.js]
skip-if = (debug || ccov) && (os == 'linux' || os == 'win') || (os == 'win' && bits == 32) # Disabled on Linux and Windows debug and ccov due to intermittent timeouts. Bug 1414126, bug 1426611. Disabled on Win32 because of intermittent OOM failures (bug 1448241)

View File

@ -4,6 +4,9 @@
const AUTOPLAY_PAGE = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "browser_autoplay_blocked.html"; const AUTOPLAY_PAGE = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "browser_autoplay_blocked.html";
const AUTOPLAY_PREF = "media.autoplay.default";
const AUTOPLAY_PERM = "autoplay-media";
function openIdentityPopup() { function openIdentityPopup() {
let promise = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popupshown"); let promise = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popupshown");
gIdentityHandler._identityBox.click(); gIdentityHandler._identityBox.click();
@ -21,8 +24,28 @@ function autoplayBlockedIcon() {
".blocked-permission-icon.autoplay-media-icon"); ".blocked-permission-icon.autoplay-media-icon");
} }
function sleep(ms) {
/* eslint-disable mozilla/no-arbitrary-setTimeout */
return new Promise(resolve => setTimeout(resolve, ms));
}
async function blockedIconShown(browser) {
// May need to wait for `GloballyAutoplayBlocked` event before showing icon.
if (BrowserTestUtils.is_hidden(autoplayBlockedIcon())) {
await BrowserTestUtils.waitForEvent(browser, "GloballyAutoplayBlocked");
}
ok(!BrowserTestUtils.is_hidden(autoplayBlockedIcon()), "Blocked icon is shown");
}
add_task(async function setup() {
registerCleanupFunction(() => {
Services.perms.removeAll();
Services.prefs.clearUserPref(AUTOPLAY_PREF);
});
});
add_task(async function testMainViewVisible() { add_task(async function testMainViewVisible() {
Services.prefs.setIntPref("media.autoplay.default", Ci.nsIAutoplay.ALLOWED); Services.prefs.setIntPref(AUTOPLAY_PREF, Ci.nsIAutoplay.ALLOWED);
await BrowserTestUtils.withNewTab(AUTOPLAY_PAGE, async function() { await BrowserTestUtils.withNewTab(AUTOPLAY_PAGE, async function() {
let permissionsList = document.getElementById("identity-popup-permission-list"); let permissionsList = document.getElementById("identity-popup-permission-list");
@ -35,21 +58,17 @@ add_task(async function testMainViewVisible() {
await closeIdentityPopup(); await closeIdentityPopup();
}); });
Services.prefs.setIntPref("media.autoplay.default", Ci.nsIAutoplay.BLOCKED); Services.prefs.setIntPref(AUTOPLAY_PREF, Ci.nsIAutoplay.BLOCKED);
await BrowserTestUtils.withNewTab(AUTOPLAY_PAGE, async function(browser) { await BrowserTestUtils.withNewTab(AUTOPLAY_PAGE, async function(browser) {
let permissionsList = document.getElementById("identity-popup-permission-list"); let permissionsList = document.getElementById("identity-popup-permission-list");
let emptyLabel = permissionsList.nextElementSibling.nextElementSibling; let emptyLabel = permissionsList.nextElementSibling.nextElementSibling;
if (BrowserTestUtils.is_hidden(autoplayBlockedIcon())) { await blockedIconShown(browser);
// The block icon would be showed after tab receives `GloballyAutoplayBlocked` event.
await BrowserTestUtils.waitForEvent(browser, "GloballyAutoplayBlocked");
}
ok(!BrowserTestUtils.is_hidden(autoplayBlockedIcon()), "Blocked icon is shown");
await openIdentityPopup(); await openIdentityPopup();
ok(BrowserTestUtils.is_hidden(emptyLabel), "List of permissions is not empty"); ok(BrowserTestUtils.is_hidden(emptyLabel), "List of permissions is not empty");
let labelText = SitePermissions.getPermissionLabel("autoplay-media"); let labelText = SitePermissions.getPermissionLabel(AUTOPLAY_PERM);
let labels = permissionsList.querySelectorAll(".identity-popup-permission-label"); let labels = permissionsList.querySelectorAll(".identity-popup-permission-label");
is(labels.length, 1, "One permission visible in main view"); is(labels.length, 1, "One permission visible in main view");
is(labels[0].textContent, labelText, "Correct value"); is(labels[0].textContent, labelText, "Correct value");
@ -70,10 +89,62 @@ add_task(async function testMainViewVisible() {
await closeIdentityPopup(); await closeIdentityPopup();
let uri = Services.io.newURI(AUTOPLAY_PAGE); let uri = Services.io.newURI(AUTOPLAY_PAGE);
let state = SitePermissions.get(uri, "autoplay-media").state; let state = SitePermissions.get(uri, AUTOPLAY_PERM).state;
Assert.equal(state, SitePermissions.ALLOW); Assert.equal(state, SitePermissions.ALLOW);
}); });
Services.perms.removeAll(); Services.perms.removeAll();
Services.prefs.clearUserPref("media.autoplay.default"); });
add_task(async function testGloballyBlockedOnNewWindow() {
Services.prefs.setIntPref(AUTOPLAY_PREF, Ci.nsIAutoplay.BLOCKED);
let uri = Services.io.newURI(AUTOPLAY_PAGE);
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, uri.spec);
await blockedIconShown(tab.linkedBrowser);
Assert.deepEqual(SitePermissions.get(uri, AUTOPLAY_PERM, tab.linkedBrowser), {
state: SitePermissions.BLOCK,
scope: SitePermissions.SCOPE_PERSISTENT,
});
let promiseWin = BrowserTestUtils.waitForNewWindow();
gBrowser.replaceTabWithWindow(tab);
let win = await promiseWin;
tab = win.gBrowser.selectedTab;
Assert.deepEqual(SitePermissions.get(uri, AUTOPLAY_PERM, tab.linkedBrowser), {
state: SitePermissions.BLOCK,
scope: SitePermissions.SCOPE_PERSISTENT,
});
SitePermissions.remove(uri, AUTOPLAY_PERM, tab.linkedBrowser);
await BrowserTestUtils.closeWindow(win);
});
add_task(async function testBFCache() {
Services.prefs.setIntPref(AUTOPLAY_PREF, Ci.nsIAutoplay.BLOCKED);
await BrowserTestUtils.withNewTab("about:home", async function(browser) {
await BrowserTestUtils.loadURI(browser, AUTOPLAY_PAGE);
await blockedIconShown(browser);
Services.prefs.setIntPref(AUTOPLAY_PREF, Ci.nsIAutoplay.ALLOWED);
gBrowser.goBack();
await TestUtils.waitForCondition(() => {
return BrowserTestUtils.is_hidden(autoplayBlockedIcon());
});
gBrowser.goForward();
// Sleep here to prevent false positives, the icon gets shown with an
// async `GloballyAutoplayBlocked` event. The sleep gives it a little
// time for it to show otherwise there is a chance it passes before it
// would have shown.
await sleep(100);
ok(BrowserTestUtils.is_hidden(autoplayBlockedIcon()), "Blocked icon is hidden");
});
Services.perms.removeAll();
}); });

View File

@ -8,12 +8,11 @@ var {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetters(this, { XPCOMUtils.defineLazyModuleGetters(this, {
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm", BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm", ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
ExtensionSettingsStore: "resource://gre/modules/ExtensionSettingsStore.jsm",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
ShellService: "resource:///modules/ShellService.jsm", ShellService: "resource:///modules/ShellService.jsm",
}); });
@ -31,9 +30,10 @@ Object.defineProperty(this, "BROWSER_NEW_TAB_URL", {
} }
// If the extension does not have private browsing permission, // If the extension does not have private browsing permission,
// use about:privatebrowsing. // use about:privatebrowsing.
if (aboutNewTabService.newTabURL.startsWith("moz-extension")) { let extensionInfo = ExtensionSettingsStore.getSetting("url_overrides", "newTabURL");
let url = new URL(aboutNewTabService.newTabURL); if (extensionInfo) {
if (!WebExtensionPolicy.getByHostname(url.hostname).privateBrowsingAllowed) { let policy = WebExtensionPolicy.getByID(extensionInfo.id);
if (!policy || !policy.privateBrowsingAllowed) {
return "about:privatebrowsing"; return "about:privatebrowsing";
} }
} }

View File

@ -36,6 +36,7 @@ BROWSER_CHROME_MANIFESTS += [
'content/test/pageinfo/browser.ini', 'content/test/pageinfo/browser.ini',
'content/test/performance/browser.ini', 'content/test/performance/browser.ini',
'content/test/performance/hidpi/browser.ini', 'content/test/performance/hidpi/browser.ini',
'content/test/performance/legacyurlbar/browser.ini',
'content/test/performance/lowdpi/browser.ini', 'content/test/performance/lowdpi/browser.ini',
'content/test/permissions/browser.ini', 'content/test/permissions/browser.ini',
'content/test/plugins/browser.ini', 'content/test/plugins/browser.ini',

View File

@ -209,7 +209,7 @@ function openBrowserWindow(cmdLine, triggeringPrincipal, urlOrUrlList, postData
let args; let args;
if (!urlOrUrlList) { if (!urlOrUrlList) {
// Just pass in the defaultArgs directly. We'll use system principal on the other end. // Just pass in the defaultArgs directly. We'll use system principal on the other end.
args = [gBrowserContentHandler.getDefaultArgs(forcePrivate)]; args = [gBrowserContentHandler.defaultArgs];
} else { } else {
let pService = Cc["@mozilla.org/toolkit/profile-service;1"]. let pService = Cc["@mozilla.org/toolkit/profile-service;1"].
getService(Ci.nsIToolkitProfileService); getService(Ci.nsIToolkitProfileService);
@ -520,7 +520,7 @@ nsBrowserContentHandler.prototype = {
/* nsIBrowserHandler */ /* nsIBrowserHandler */
getDefaultArgs(forcePrivate = false) { get defaultArgs() {
var prefb = Services.prefs; var prefb = Services.prefs;
if (!gFirstWindow) { if (!gFirstWindow) {
@ -605,7 +605,7 @@ nsBrowserContentHandler.prototype = {
try { try {
var choice = prefb.getIntPref("browser.startup.page"); var choice = prefb.getIntPref("browser.startup.page");
if (choice == 1 || choice == 3) if (choice == 1 || choice == 3)
startPage = forcePrivate ? HomePage.getPrivate() : HomePage.get(); startPage = HomePage.get();
} catch (e) { } catch (e) {
Cu.reportError(e); Cu.reportError(e);
} }
@ -623,10 +623,6 @@ nsBrowserContentHandler.prototype = {
return overridePage || startPage || "about:blank"; return overridePage || startPage || "about:blank";
}, },
get defaultArgs() {
return this.getDefaultArgs(PrivateBrowsingUtils.permanentPrivateBrowsing);
},
mFeatures: null, mFeatures: null,
getFeatures: function bch_features(cmdLine) { getFeatures: function bch_features(cmdLine) {

View File

@ -11,18 +11,18 @@ add_task(async function() {
ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar."); ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
let oldChildCount = CustomizableUI.getCustomizationTarget(navbar).childElementCount; let oldChildCount = CustomizableUI.getCustomizationTarget(navbar).childElementCount;
window.resizeTo(kForceOverflowWidthPx, window.outerHeight); window.resizeTo(kForceOverflowWidthPx, window.outerHeight);
await waitForCondition(() => navbar.hasAttribute("overflowing")); await TestUtils.waitForCondition(() => navbar.hasAttribute("overflowing"));
ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar."); ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
ok(CustomizableUI.getCustomizationTarget(navbar).childElementCount < oldChildCount, "Should have fewer children."); ok(CustomizableUI.getCustomizationTarget(navbar).childElementCount < oldChildCount, "Should have fewer children.");
let newWindow = await openAndLoadWindow(); let newWindow = await openAndLoadWindow();
let otherNavBar = newWindow.document.getElementById(CustomizableUI.AREA_NAVBAR); let otherNavBar = newWindow.document.getElementById(CustomizableUI.AREA_NAVBAR);
await waitForCondition(() => otherNavBar.hasAttribute("overflowing")); await TestUtils.waitForCondition(() => otherNavBar.hasAttribute("overflowing"));
ok(otherNavBar.hasAttribute("overflowing"), "Other window should have an overflowing toolbar."); ok(otherNavBar.hasAttribute("overflowing"), "Other window should have an overflowing toolbar.");
await promiseWindowClosed(newWindow); await promiseWindowClosed(newWindow);
window.resizeTo(originalWindowWidth, window.outerHeight); window.resizeTo(originalWindowWidth, window.outerHeight);
await waitForCondition(() => !navbar.hasAttribute("overflowing")); await TestUtils.waitForCondition(() => !navbar.hasAttribute("overflowing"));
ok(!navbar.hasAttribute("overflowing"), "Should no longer have an overflowing toolbar."); ok(!navbar.hasAttribute("overflowing"), "Should no longer have an overflowing toolbar.");
}); });

View File

@ -191,13 +191,8 @@ this.windows = class extends ExtensionAPI {
args.appendElement(mkstr(createData.url)); args.appendElement(mkstr(createData.url));
} }
} else { } else {
let url; let url = createData.incognito && !PrivateBrowsingUtils.permanentPrivateBrowsing ?
if (createData.incognito) { "about:privatebrowsing" : HomePage.get().split("|", 1)[0];
url = PrivateBrowsingUtils.permanentPrivateBrowsing ?
HomePage.getPrivate().split("|", 1)[0] : "about:privatebrowsing";
} else {
url = HomePage.get().split("|", 1)[0];
}
args.appendElement(mkstr(url)); args.appendElement(mkstr(url));
if (url.startsWith("about:") && if (url.startsWith("about:") &&

View File

@ -471,42 +471,6 @@ add_task(async function test_overriding_home_page_incognito_not_allowed() {
await BrowserTestUtils.closeWindow(win); await BrowserTestUtils.closeWindow(win);
}); });
add_task(async function test_overriding_home_page_incognito_not_allowed_bypass() {
await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "extension",
},
background() {
browser.test.sendMessage("url", browser.runtime.getURL("home.html"));
},
files: {"home.html": "<h1>1</h1>"},
useAddonManager: "temporary",
});
await extension.startup();
let url = await extension.awaitMessage("url");
// Verify manually setting the pref to the extension page does not work.
let changed = promisePrefChangeObserved(HOMEPAGE_URL_PREF);
Services.prefs.setStringPref(HOMEPAGE_URL_PREF, url);
await changed;
let windowOpenedPromise = BrowserTestUtils.waitForNewWindow();
let win = OpenBrowserWindow({private: true});
await windowOpenedPromise;
win.BrowserHome();
await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
is(win.gURLBar.value, "", "home page not used in private window");
changed = promisePrefChangeObserved(HOMEPAGE_URL_PREF);
Services.prefs.clearUserPref(HOMEPAGE_URL_PREF);
await changed;
await extension.unload();
await BrowserTestUtils.closeWindow(win);
});
add_task(async function test_overriding_home_page_incognito_spanning() { add_task(async function test_overriding_home_page_incognito_spanning() {
await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]}); await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
@ -546,3 +510,30 @@ add_task(async function test_overriding_home_page_incognito_spanning() {
await extension.unload(); await extension.unload();
await BrowserTestUtils.closeWindow(win); await BrowserTestUtils.closeWindow(win);
}); });
add_task(async function test_overriding_home_page_incognito_external() {
await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
let extension = ExtensionTestUtils.loadExtension({
manifest: {
chrome_settings_overrides: {"homepage": "https://example.com/home.html"},
name: "extension",
},
useAddonManager: "temporary",
});
await extension.startup();
// Verify a private window does not open the extension page.
let windowOpenedPromise = BrowserTestUtils.waitForNewWindow();
let win = OpenBrowserWindow({private: true});
await windowOpenedPromise;
win.BrowserHome();
await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
is(win.gURLBar.value, "", "home page not used in private window");
is(gBrowser.selectedBrowser.currentURI.spec, "about:home", "home page not used in private window");
await extension.unload();
await BrowserTestUtils.closeWindow(win);
});

View File

@ -604,58 +604,6 @@ add_task(async function test_overriding_newtab_incognito_not_allowed() {
await BrowserTestUtils.closeWindow(win); await BrowserTestUtils.closeWindow(win);
}); });
add_task(async function test_overriding_newtab_incognito_not_allowed_bypass() {
await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "extension",
applications: {
gecko: {id: "@not-allowed-newtab"},
},
},
background() {
browser.test.sendMessage("url", browser.runtime.getURL("newtab.html"));
},
files: {
"newtab.html": `
<!DOCTYPE html>
<head>
<meta charset="utf-8"/></head>
<html>
<body>
</body>
</html>
`,
},
useAddonManager: "permanent",
});
await extension.startup();
let url = await extension.awaitMessage("url");
// Verify setting the pref directly doesn't bypass permissions.
let origUrl = aboutNewTabService.newTabURL;
aboutNewTabService.newTabURL = url;
// Verify a private window does not open the extension page. We would
// get an extra notification that we don't listen for if it gets loaded.
let windowOpenedPromise = BrowserTestUtils.waitForNewWindow();
let win = OpenBrowserWindow({private: true});
await windowOpenedPromise;
let newTabOpened = waitForNewTab();
win.BrowserOpenTab();
await newTabOpened;
is(win.gURLBar.value, "", "directly set newtab not used in private window");
aboutNewTabService.newTabURL = origUrl;
await extension.unload();
await BrowserTestUtils.closeWindow(win);
});
add_task(async function test_overriding_newtab_incognito_spanning() { add_task(async function test_overriding_newtab_incognito_spanning() {
await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]}); await SpecialPowers.pushPrefEnv({set: [["extensions.allowPrivateBrowsingByDefault", false]]});

View File

@ -4,7 +4,7 @@
restart-required-title = Restart Required restart-required-title = Restart Required
restart-required-header = Sorry. We just need to do one small thing to keep going. restart-required-header = Sorry. We just need to do one small thing to keep going.
restart-required-intro = We have just installed an update in the background. Click Restart { -brand-short-name } to finish restart-required-intro-brand = { -brand-short-name } has just been updated in the background. Click Restart { -brand-short-name } to finish
applying it. applying it.
restart-required-description = We will restore all your pages, windows and tabs afterwards, so you can be on your way quickly. restart-required-description = We will restore all your pages, windows and tabs afterwards, so you can be on your way quickly.

View File

@ -12,6 +12,8 @@ var EXPORTED_SYMBOLS = ["HomePage"];
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils", ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm"); "resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
"resource://gre/modules/ExtensionSettingsStore.jsm");
const kPrefName = "browser.startup.homepage"; const kPrefName = "browser.startup.homepage";
@ -48,8 +50,17 @@ let HomePage = {
get(aWindow) { get(aWindow) {
if (PrivateBrowsingUtils.permanentPrivateBrowsing || if (PrivateBrowsingUtils.permanentPrivateBrowsing ||
(aWindow && PrivateBrowsingUtils.isWindowPrivate(aWindow))) { (aWindow && PrivateBrowsingUtils.isWindowPrivate(aWindow))) {
return this.getPrivate(); // If an extension controls the setting and does not have private
// browsing permission, use the default setting.
let extensionInfo = ExtensionSettingsStore.getSetting("prefs", "homepage_override");
if (extensionInfo) {
let policy = WebExtensionPolicy.getByID(extensionInfo.id);
if (!policy || !policy.privateBrowsingAllowed) {
return this.getDefault();
}
}
} }
return getHomepagePref(); return getHomepagePref();
}, },
@ -57,24 +68,6 @@ let HomePage = {
return getHomepagePref(true); return getHomepagePref(true);
}, },
getPrivate() {
let homePages = getHomepagePref();
if (!homePages.includes("moz-extension")) {
return homePages;
}
// Verify private access and build a new list.
let privateHomePages = homePages.split("|").filter(page => {
let url = new URL(page);
if (url.protocol !== "moz-extension:") {
return true;
}
let policy = WebExtensionPolicy.getByHostname(url.hostname);
return policy && policy.privateBrowsingAllowed;
});
// Extensions may not be ready on startup, fallback to defaults.
return privateHomePages.join("|") || this.getDefault();
},
get overridden() { get overridden() {
return Services.prefs.prefHasUserValue(kPrefName); return Services.prefs.prefHasUserValue(kPrefName);
}, },

View File

@ -157,7 +157,7 @@ const GloballyBlockedPermissions = {
Ci.nsISupportsWeakReference]), Ci.nsISupportsWeakReference]),
onLocationChange(aWebProgress, aRequest, aLocation, aFlags) { onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
if (aWebProgress.isTopLevel) { if (aWebProgress.isTopLevel) {
GloballyBlockedPermissions.remove(browser, id); GloballyBlockedPermissions.remove(browser, id, prePath);
browser.removeProgressListener(this); browser.removeProgressListener(this);
} }
}, },
@ -165,9 +165,11 @@ const GloballyBlockedPermissions = {
}, },
// Removes a permission with the specified id for the specified browser. // Removes a permission with the specified id for the specified browser.
remove(browser, id) { remove(browser, id, prePath = null) {
let entry = this._stateByBrowser.get(browser); let entry = this._stateByBrowser.get(browser);
let prePath = browser.currentURI.prePath; if (!prePath) {
prePath = browser.currentURI.prePath;
}
if (entry && entry[prePath]) { if (entry && entry[prePath]) {
delete entry[prePath][id]; delete entry[prePath][id];
} }
@ -192,6 +194,15 @@ const GloballyBlockedPermissions = {
} }
return permissions; return permissions;
}, },
// Copies the globally blocked permission state of one browser
// into a new entry for the other browser.
copy(browser, newBrowser) {
let entry = this._stateByBrowser.get(browser);
if (entry) {
this._stateByBrowser.set(newBrowser, entry);
}
},
}; };
/** /**
@ -594,6 +605,7 @@ var SitePermissions = {
*/ */
copyTemporaryPermissions(browser, newBrowser) { copyTemporaryPermissions(browser, newBrowser) {
TemporaryPermissions.copy(browser, newBrowser); TemporaryPermissions.copy(browser, newBrowser);
GloballyBlockedPermissions.copy(browser, newBrowser);
}, },
/** /**

View File

@ -84,11 +84,9 @@ add_task(async function test_context_menu() {
TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.contextmenu", 1); TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.contextmenu", 1);
// Also check events. // Also check events.
let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); TelemetryTestUtils.assertEvents(
events = (events.parent || []).filter(e => e[1] == "navigation" && e[2] == "search"); [{object: "contextmenu", value: null, extra: {engine: "other-MozSearch"}}],
TelemetryTestUtils.assertEvents(events, [ {category: "navigation", method: "search"});
["navigation", "search", "contextmenu", null, {engine: "other-MozSearch"}],
]);
contextMenu.hidePopup(); contextMenu.hidePopup();
BrowserTestUtils.removeTab(gBrowser.selectedTab); BrowserTestUtils.removeTab(gBrowser.selectedTab);
@ -122,11 +120,9 @@ add_task(async function test_about_newtab() {
TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.newtab", 1); TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.newtab", 1);
// Also check events. // Also check events.
let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); TelemetryTestUtils.assertEvents(
events = (events.parent || []).filter(e => e[1] == "navigation" && e[2] == "search"); [{object: "about_newtab", value: "enter", extra: {engine: "other-MozSearch"}}],
TelemetryTestUtils.assertEvents(events, [ {category: "navigation", method: "search"});
["navigation", "search", "about_newtab", "enter", {engine: "other-MozSearch"}],
]);
BrowserTestUtils.removeTab(tab); BrowserTestUtils.removeTab(tab);
}); });

View File

@ -78,11 +78,9 @@ add_task(async function test_abouthome_activitystream_simpleQuery() {
TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.abouthome", 1); TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.abouthome", 1);
// Also check events. // Also check events.
let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); TelemetryTestUtils.assertEvents(
events = (events.parent || []).filter(e => e[1] == "navigation" && e[2] == "search"); [{object: "about_home", value: "enter", extra: {engine: "other-MozSearch"}}],
TelemetryTestUtils.assertEvents(events, [ {category: "navigation", method: "search"});
["navigation", "search", "about_home", "enter", {engine: "other-MozSearch"}],
]);
BrowserTestUtils.removeTab(tab); BrowserTestUtils.removeTab(tab);
}); });

View File

@ -120,11 +120,9 @@ add_task(async function test_plainQuery() {
TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.searchbar", 1); TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.searchbar", 1);
// Also check events. // Also check events.
let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); TelemetryTestUtils.assertEvents(
events = (events.parent || []).filter(e => e[1] == "navigation" && e[2] == "search"); [{object: "searchbar", value: "enter", extra: {engine: "other-MozSearch"}}],
TelemetryTestUtils.assertEvents(events, [ {category: "navigation", method: "search"});
["navigation", "search", "searchbar", "enter", {engine: "other-MozSearch"}],
]);
// Check the histograms as well. // Check the histograms as well.
let resultMethods = resultMethodHist.snapshot(); let resultMethods = resultMethodHist.snapshot();
@ -167,11 +165,9 @@ add_task(async function test_oneOff_enter() {
TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch2.searchbar", 1); TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch2.searchbar", 1);
// Also check events. // Also check events.
let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); TelemetryTestUtils.assertEvents(
events = (events.parent || []).filter(e => e[1] == "navigation" && e[2] == "search"); [{object: "searchbar", value: "oneoff", extra: {engine: "other-MozSearch2"}}],
TelemetryTestUtils.assertEvents(events, [ {category: "navigation", method: "search"});
["navigation", "search", "searchbar", "oneoff", {engine: "other-MozSearch2"}],
]);
// Check the histograms as well. // Check the histograms as well.
let resultMethods = resultMethodHist.snapshot(); let resultMethods = resultMethodHist.snapshot();
@ -283,11 +279,9 @@ add_task(async function test_suggestion_click() {
TelemetryTestUtils.assertKeyedHistogramSum(search_hist, searchEngineId + ".searchbar", 1); TelemetryTestUtils.assertKeyedHistogramSum(search_hist, searchEngineId + ".searchbar", 1);
// Also check events. // Also check events.
let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); TelemetryTestUtils.assertEvents(
events = (events.parent || []).filter(e => e[1] == "navigation" && e[2] == "search"); [{object: "searchbar", value: "suggestion", extra: {engine: searchEngineId}}],
TelemetryTestUtils.assertEvents(events, [ {category: "navigation", method: "search"});
["navigation", "search", "searchbar", "suggestion", {engine: searchEngineId}],
]);
// Check the histograms as well. // Check the histograms as well.
let resultMethods = resultMethodHist.snapshot(); let resultMethods = resultMethodHist.snapshot();

View File

@ -154,9 +154,9 @@ add_task(async function test_simpleQuery() {
TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.alias", undefined); TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.alias", undefined);
// Also check events. // Also check events.
let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); TelemetryTestUtils.assertEvents(
events = (events.parent || []).filter(e => e[1] == "navigation" && e[2] == "search"); [["navigation", "search", "urlbar", "enter", {engine: "other-MozSearch"}]],
TelemetryTestUtils.assertEvents(events, [["navigation", "search", "urlbar", "enter", {engine: "other-MozSearch"}]]); {category: "navigation", method: "search"});
// Check the histograms as well. // Check the histograms as well.
TelemetryTestUtils.assertHistogram(resultIndexHist, 0, 1); TelemetryTestUtils.assertHistogram(resultIndexHist, 0, 1);
@ -202,9 +202,9 @@ add_task(async function test_searchAlias() {
TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.alias", undefined); TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.alias", undefined);
// Also check events. // Also check events.
let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); TelemetryTestUtils.assertEvents(
events = (events.parent || []).filter(e => e[1] == "navigation" && e[2] == "search"); [["navigation", "search", "urlbar", "alias", {engine: "other-MozSearch"}]],
TelemetryTestUtils.assertEvents(events, [["navigation", "search", "urlbar", "alias", {engine: "other-MozSearch"}]]); {category: "navigation", method: "search"});
// Check the histograms as well. // Check the histograms as well.
TelemetryTestUtils.assertHistogram(resultIndexHist, 0, 1); TelemetryTestUtils.assertHistogram(resultIndexHist, 0, 1);
@ -281,9 +281,9 @@ add_task(async function test_oneOff_enter() {
TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.alias", undefined); TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.alias", undefined);
// Also check events. // Also check events.
let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); TelemetryTestUtils.assertEvents(
events = (events.parent || []).filter(e => e[1] == "navigation" && e[2] == "search"); [["navigation", "search", "urlbar", "oneoff", {engine: "other-MozSearch"}]],
TelemetryTestUtils.assertEvents(events, [["navigation", "search", "urlbar", "oneoff", {engine: "other-MozSearch"}]]); {category: "navigation", method: "search"});
// Check the histograms as well. // Check the histograms as well.
TelemetryTestUtils.assertHistogram(resultIndexHist, 0, 1); TelemetryTestUtils.assertHistogram(resultIndexHist, 0, 1);
@ -382,9 +382,9 @@ add_task(async function test_suggestion_click() {
TelemetryTestUtils.assertKeyedHistogramSum(search_hist, searchEngineId + ".urlbar", 1); TelemetryTestUtils.assertKeyedHistogramSum(search_hist, searchEngineId + ".urlbar", 1);
// Also check events. // Also check events.
let events = Services.telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false); TelemetryTestUtils.assertEvents(
events = (events.parent || []).filter(e => e[1] == "navigation" && e[2] == "search"); [["navigation", "search", "urlbar", "suggestion", {engine: searchEngineId}]],
TelemetryTestUtils.assertEvents(events, [["navigation", "search", "urlbar", "suggestion", {engine: searchEngineId}]]); {category: "navigation", method: "search"});
// Check the histograms as well. // Check the histograms as well.
TelemetryTestUtils.assertHistogram(resultIndexHist, 3, 1); TelemetryTestUtils.assertHistogram(resultIndexHist, 3, 1);

View File

@ -247,8 +247,9 @@ static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, uint32_t aTag,
MOZ_DIAGNOSTIC_ASSERT(!originNoSuffix.IsEmpty()); MOZ_DIAGNOSTIC_ASSERT(!originNoSuffix.IsEmpty());
aInfo = // XXX: Do we care about mDomain for structured clone?
ContentPrincipalInfo(attrs, originNoSuffix, spec, std::move(policies)); aInfo = ContentPrincipalInfo(attrs, originNoSuffix, spec, Nothing(),
std::move(policies));
} else { } else {
#ifdef FUZZING #ifdef FUZZING
return false; return false;

View File

@ -4,31 +4,29 @@
"use strict"; "use strict";
const { Toolbox } = require("devtools/client/framework/toolbox"); const { Toolbox } = require("devtools/client/framework/toolbox");
const { TelemetryTestUtils } = ChromeUtils.import("resource://testing-common/TelemetryTestUtils.jsm");
const URL = "data:text/html;charset=utf8,browser_toolbox_telemetry_close.js"; const URL = "data:text/html;charset=utf8,browser_toolbox_telemetry_close.js";
const OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT;
const { RIGHT, BOTTOM } = Toolbox.HostType; const { RIGHT, BOTTOM } = Toolbox.HostType;
const DATA = [ const DATA = [
{ {
timestamp: null,
category: "devtools.main", category: "devtools.main",
method: "close", method: "close",
object: "tools", object: "tools",
value: null, value: null,
extra: { extra: {
host: "right", host: "right",
width: "1440", width: w => w > 0,
}, },
}, },
{ {
timestamp: null,
category: "devtools.main", category: "devtools.main",
method: "close", method: "close",
object: "tools", object: "tools",
value: null, value: null,
extra: { extra: {
host: "bottom", host: "bottom",
width: "1440", width: w => w > 0,
}, },
}, },
]; ];
@ -38,8 +36,7 @@ add_task(async function() {
Services.telemetry.clearEvents(); Services.telemetry.clearEvents();
// Ensure no events have been logged // Ensure no events have been logged
const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true); TelemetryTestUtils.assertNumberOfEvents(0);
ok(!snapshot.parent, "No events have been logged for the main process");
await openAndCloseToolbox("webconsole", RIGHT); await openAndCloseToolbox("webconsole", RIGHT);
await openAndCloseToolbox("webconsole", BOTTOM); await openAndCloseToolbox("webconsole", BOTTOM);
@ -57,25 +54,5 @@ async function openAndCloseToolbox(toolId, host) {
} }
function checkResults() { function checkResults() {
const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true); TelemetryTestUtils.assertEvents(DATA, {category: "devtools.main", method: "close", object: "tools"});
const events = snapshot.parent.filter(event => event[1] === "devtools.main" &&
event[2] === "close" &&
event[3] === "tools" &&
event[4] === null
);
for (const i in DATA) {
const [ timestamp, category, method, object, value, extra ] = events[i];
const expected = DATA[i];
// ignore timestamp
ok(timestamp > 0, "timestamp is greater than 0");
is(category, expected.category, "category is correct");
is(method, expected.method, "method is correct");
is(object, expected.object, "object is correct");
is(value, expected.value, "value is correct");
is(extra.host, expected.extra.host, "host is correct");
ok(extra.width > 0, "width is greater than 0");
}
} }

View File

@ -16,6 +16,7 @@ const TEST_URI =
<div id="box"></div>`; <div id="box"></div>`;
add_task(async function() { add_task(async function() {
await pushPref("devtools.layout.boxmodel.highlightProperty", true);
await addTab("data:text/html," + encodeURIComponent(TEST_URI)); await addTab("data:text/html," + encodeURIComponent(TEST_URI));
const { inspector, boxmodel } = await openLayoutView(); const { inspector, boxmodel } = await openLayoutView();
await selectNode("#box", inspector); await selectNode("#box", inspector);

View File

@ -16,6 +16,7 @@ const TEST_URI =
<div id="box"></div>`; <div id="box"></div>`;
add_task(async function() { add_task(async function() {
await pushPref("devtools.layout.boxmodel.highlightProperty", true);
await addTab("data:text/html," + encodeURIComponent(TEST_URI)); await addTab("data:text/html," + encodeURIComponent(TEST_URI));
const { inspector, boxmodel } = await openLayoutView(); const { inspector, boxmodel } = await openLayoutView();
const { rulePreviewTooltip } = boxmodel; const { rulePreviewTooltip } = boxmodel;

View File

@ -28,6 +28,7 @@ class ChangesContextMenu {
// Window object to which the Changes panel belongs to. // Window object to which the Changes panel belongs to.
this.window = this.document.defaultView; this.window = this.document.defaultView;
this._onCopyDeclaration = this.view.copyDeclaration.bind(this.view);
this._onCopyRule = this.view.copyRule.bind(this.view); this._onCopyRule = this.view.copyRule.bind(this.view);
this._onCopySelection = this.view.copySelection.bind(this.view); this._onCopySelection = this.view.copySelection.bind(this.view);
this._onSelectAll = this._onSelectAll.bind(this); this._onSelectAll = this._onSelectAll.bind(this);
@ -55,16 +56,24 @@ class ChangesContextMenu {
}); });
menu.append(menuitemCopy); menu.append(menuitemCopy);
const declEl = target.closest(".changes__declaration");
const ruleEl = target.closest("[data-rule-id]"); const ruleEl = target.closest("[data-rule-id]");
const ruleId = ruleEl ? ruleEl.dataset.ruleId : null; const ruleId = ruleEl ? ruleEl.dataset.ruleId : null;
if (ruleId) { if (ruleId || declEl) {
// Copy Rule option // Copy Rule option
menu.append(new MenuItem({ menu.append(new MenuItem({
label: getStr("changes.contextmenu.copyRule"), label: getStr("changes.contextmenu.copyRule"),
click: () => this._onCopyRule(ruleId), click: () => this._onCopyRule(ruleId),
})); }));
// Copy Declaration option. Visible only if there is a declaration element target.
menu.append(new MenuItem({
label: getStr("changes.contextmenu.copyDeclaration"),
click: () => this._onCopyDeclaration(declEl),
visible: !!declEl,
}));
menu.append(new MenuItem({ menu.append(new MenuItem({
type: "separator", type: "separator",
})); }));

View File

@ -146,6 +146,22 @@ class ChangesView {
clipboardHelper.copyString(text); clipboardHelper.copyString(text);
} }
/**
* Handler for the "Copy Declaration" option from the context menu.
* Builds a CSS declaration string with the property name and value, and copies it
* to the clipboard. The declaration is commented out if it is marked as removed.
*
* @param {DOMElement} element
* Host element of a CSS declaration rendered the Changes panel.
*/
copyDeclaration(element) {
const name = element.querySelector(".changes__declaration-name").textContent;
const value = element.querySelector(".changes__declaration-value").textContent;
const isRemoved = element.classList.contains("diff-remove");
const text = isRemoved ? `/* ${name}: ${value}; */` : `${name}: ${value};`;
clipboardHelper.copyString(text);
}
/** /**
* Handler for the "Copy Rule" option from the context menu. * Handler for the "Copy Rule" option from the context menu.
* Gets the full content of the target CSS rule (including any changes applied) * Gets the full content of the target CSS rule (including any changes applied)

View File

@ -28,11 +28,11 @@ class CSSDeclaration extends PureComponent {
render() { render() {
const { className, marker, property, value } = this.props; const { className, marker, property, value } = this.props;
return dom.div({ className: `declaration ${className}` }, return dom.div({ className: `changes__declaration ${className}` },
marker, marker,
dom.span({ className: "declaration-name theme-fg-color3"}, property), dom.span({ className: "changes__declaration-name theme-fg-color3"}, property),
": ", ": ",
dom.span({ className: "declaration-value theme-fg-color1"}, value), dom.span({ className: "changes__declaration-value theme-fg-color1"}, value),
";" ";"
); );
} }

View File

@ -44,7 +44,7 @@ registerCleanupFunction(() => {
* @return {Array} * @return {Array}
*/ */
function getDeclarations(panelDoc, selector = "", containerNode = null) { function getDeclarations(panelDoc, selector = "", containerNode = null) {
const els = panelDoc.querySelectorAll(`#sidebar-panel-changes .declaration${selector}`); const els = panelDoc.querySelectorAll(`.changes__declaration${selector}`);
return [...els] return [...els]
.filter(el => { .filter(el => {
@ -52,8 +52,8 @@ function getDeclarations(panelDoc, selector = "", containerNode = null) {
}) })
.map(el => { .map(el => {
return { return {
property: el.querySelector(".declaration-name").textContent, property: el.querySelector(".changes__declaration-name").textContent,
value: el.querySelector(".declaration-value").textContent, value: el.querySelector(".changes__declaration-value").textContent,
}; };
}); });
} }

View File

@ -28,7 +28,7 @@ add_task(async function() {
expectedPattern: "color", expectedPattern: "color",
visible: { visible: {
copyLocation: false, copyLocation: false,
copyPropertyDeclaration: true, copyDeclaration: true,
copyPropertyName: true, copyPropertyName: true,
copyPropertyValue: false, copyPropertyValue: false,
copySelector: false, copySelector: false,
@ -42,7 +42,7 @@ add_task(async function() {
expectedPattern: "12px", expectedPattern: "12px",
visible: { visible: {
copyLocation: false, copyLocation: false,
copyPropertyDeclaration: true, copyDeclaration: true,
copyPropertyName: false, copyPropertyName: false,
copyPropertyValue: true, copyPropertyValue: true,
copySelector: false, copySelector: false,
@ -56,7 +56,7 @@ add_task(async function() {
expectedPattern: "#00F !important", expectedPattern: "#00F !important",
visible: { visible: {
copyLocation: false, copyLocation: false,
copyPropertyDeclaration: true, copyDeclaration: true,
copyPropertyName: false, copyPropertyName: false,
copyPropertyValue: true, copyPropertyValue: true,
copySelector: false, copySelector: false,
@ -66,11 +66,11 @@ add_task(async function() {
{ {
desc: "Test Copy Property Declaration", desc: "Test Copy Property Declaration",
node: ruleEditor.rule.textProps[2].editor.nameSpan, node: ruleEditor.rule.textProps[2].editor.nameSpan,
menuItemLabel: "styleinspector.contextmenu.copyPropertyDeclaration", menuItemLabel: "styleinspector.contextmenu.copyDeclaration",
expectedPattern: "font-size: 12px;", expectedPattern: "font-size: 12px;",
visible: { visible: {
copyLocation: false, copyLocation: false,
copyPropertyDeclaration: true, copyDeclaration: true,
copyPropertyName: true, copyPropertyName: true,
copyPropertyValue: false, copyPropertyValue: false,
copySelector: false, copySelector: false,
@ -80,11 +80,11 @@ add_task(async function() {
{ {
desc: "Test Copy Property Declaration with Priority", desc: "Test Copy Property Declaration with Priority",
node: ruleEditor.rule.textProps[3].editor.nameSpan, node: ruleEditor.rule.textProps[3].editor.nameSpan,
menuItemLabel: "styleinspector.contextmenu.copyPropertyDeclaration", menuItemLabel: "styleinspector.contextmenu.copyDeclaration",
expectedPattern: "border-color: #00F !important;", expectedPattern: "border-color: #00F !important;",
visible: { visible: {
copyLocation: false, copyLocation: false,
copyPropertyDeclaration: true, copyDeclaration: true,
copyPropertyName: true, copyPropertyName: true,
copyPropertyValue: false, copyPropertyValue: false,
copySelector: false, copySelector: false,
@ -104,7 +104,7 @@ add_task(async function() {
"}", "}",
visible: { visible: {
copyLocation: false, copyLocation: false,
copyPropertyDeclaration: true, copyDeclaration: true,
copyPropertyName: true, copyPropertyName: true,
copyPropertyValue: false, copyPropertyValue: false,
copySelector: false, copySelector: false,
@ -118,7 +118,7 @@ add_task(async function() {
expectedPattern: "html, body, #testid", expectedPattern: "html, body, #testid",
visible: { visible: {
copyLocation: false, copyLocation: false,
copyPropertyDeclaration: false, copyDeclaration: false,
copyPropertyName: false, copyPropertyName: false,
copyPropertyValue: false, copyPropertyValue: false,
copySelector: true, copySelector: true,
@ -133,7 +133,7 @@ add_task(async function() {
"inspector/rules/test/doc_copystyles.css", "inspector/rules/test/doc_copystyles.css",
visible: { visible: {
copyLocation: true, copyLocation: true,
copyPropertyDeclaration: false, copyDeclaration: false,
copyPropertyName: false, copyPropertyName: false,
copyPropertyValue: false, copyPropertyValue: false,
copySelector: false, copySelector: false,
@ -156,7 +156,7 @@ add_task(async function() {
"}", "}",
visible: { visible: {
copyLocation: false, copyLocation: false,
copyPropertyDeclaration: true, copyDeclaration: true,
copyPropertyName: true, copyPropertyName: true,
copyPropertyValue: false, copyPropertyValue: false,
copySelector: false, copySelector: false,
@ -179,7 +179,7 @@ add_task(async function() {
"}", "}",
visible: { visible: {
copyLocation: false, copyLocation: false,
copyPropertyDeclaration: true, copyDeclaration: true,
copyPropertyName: true, copyPropertyName: true,
copyPropertyValue: false, copyPropertyValue: false,
copySelector: false, copySelector: false,
@ -189,11 +189,11 @@ add_task(async function() {
{ {
desc: "Test Copy Property Declaration with Disabled Property", desc: "Test Copy Property Declaration with Disabled Property",
node: ruleEditor.rule.textProps[0].editor.nameSpan, node: ruleEditor.rule.textProps[0].editor.nameSpan,
menuItemLabel: "styleinspector.contextmenu.copyPropertyDeclaration", menuItemLabel: "styleinspector.contextmenu.copyDeclaration",
expectedPattern: "\/\\* color: #F00; \\*\/", expectedPattern: "\/\\* color: #F00; \\*\/",
visible: { visible: {
copyLocation: false, copyLocation: false,
copyPropertyDeclaration: true, copyDeclaration: true,
copyPropertyName: true, copyPropertyName: true,
copyPropertyValue: false, copyPropertyValue: false,
copySelector: false, copySelector: false,
@ -220,8 +220,8 @@ async function checkCopyStyle(view, node, menuItemLabel, expectedPattern, visibl
STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copy")); STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copy"));
const menuitemCopyLocation = allMenuItems.find(item => item.label === const menuitemCopyLocation = allMenuItems.find(item => item.label ===
STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyLocation")); STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyLocation"));
const menuitemCopyPropertyDeclaration = allMenuItems.find(item => item.label === const menuitemCopyDeclaration = allMenuItems.find(item => item.label ===
STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyPropertyDeclaration")); STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyDeclaration"));
const menuitemCopyPropertyName = allMenuItems.find(item => item.label === const menuitemCopyPropertyName = allMenuItems.find(item => item.label ===
STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyPropertyName")); STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyPropertyName"));
const menuitemCopyPropertyValue = allMenuItems.find(item => item.label === const menuitemCopyPropertyValue = allMenuItems.find(item => item.label ===
@ -241,10 +241,10 @@ async function checkCopyStyle(view, node, menuItemLabel, expectedPattern, visibl
"Copy Location visible attribute is as expected: " + "Copy Location visible attribute is as expected: " +
visible.copyLocation); visible.copyLocation);
is(menuitemCopyPropertyDeclaration.visible, is(menuitemCopyDeclaration.visible,
visible.copyPropertyDeclaration, visible.copyDeclaration,
"Copy Property Declaration visible attribute is as expected: " + "Copy Property Declaration visible attribute is as expected: " +
visible.copyPropertyDeclaration); visible.copyDeclaration);
is(menuitemCopyPropertyName.visible, is(menuitemCopyPropertyName.visible,
visible.copyPropertyName, visible.copyPropertyName,

View File

@ -58,6 +58,10 @@ const TEST_DATA = [
]; ];
add_task(async function() { add_task(async function() {
// Bug 1517210: GC heuristics are broken for this test, so that the test ends up
// running out of memory if we don't force to reduce the GC side before/after the test.
Cu.forceShrinkingGC();
requestLongerTimeout(4); requestLongerTimeout(4);
info("Starting the test with the pref set to true before toolbox is opened"); info("Starting the test with the pref set to true before toolbox is opened");
@ -79,6 +83,10 @@ add_task(async function() {
info("Resetting " + PREF_UA_STYLES); info("Resetting " + PREF_UA_STYLES);
Services.prefs.clearUserPref(PREF_UA_STYLES); Services.prefs.clearUserPref(PREF_UA_STYLES);
// Bug 1517210: GC heuristics are broken for this test, so that the test ends up
// running out of memory if we don't force to reduce the GC side before/after the test.
Cu.forceShrinkingGC();
}); });
async function setUserAgentStylesPref(val) { async function setUserAgentStylesPref(val) {

View File

@ -46,7 +46,7 @@ function StyleInspectorMenu(view, options) {
this._onCopyColor = this._onCopyColor.bind(this); this._onCopyColor = this._onCopyColor.bind(this);
this._onCopyImageDataUrl = this._onCopyImageDataUrl.bind(this); this._onCopyImageDataUrl = this._onCopyImageDataUrl.bind(this);
this._onCopyLocation = this._onCopyLocation.bind(this); this._onCopyLocation = this._onCopyLocation.bind(this);
this._onCopyPropertyDeclaration = this._onCopyPropertyDeclaration.bind(this); this._onCopyDeclaration = this._onCopyDeclaration.bind(this);
this._onCopyPropertyName = this._onCopyPropertyName.bind(this); this._onCopyPropertyName = this._onCopyPropertyName.bind(this);
this._onCopyPropertyValue = this._onCopyPropertyValue.bind(this); this._onCopyPropertyValue = this._onCopyPropertyValue.bind(this);
this._onCopyRule = this._onCopyRule.bind(this); this._onCopyRule = this._onCopyRule.bind(this);
@ -131,11 +131,11 @@ StyleInspectorMenu.prototype = {
}, },
visible: this._isImageUrl(), visible: this._isImageUrl(),
}); });
const copyPropDeclarationLabel = "styleinspector.contextmenu.copyPropertyDeclaration"; const copyDeclarationLabel = "styleinspector.contextmenu.copyDeclaration";
const menuitemCopyPropertyDeclaration = new MenuItem({ const menuitemCopyDeclaration = new MenuItem({
label: STYLE_INSPECTOR_L10N.getStr(copyPropDeclarationLabel), label: STYLE_INSPECTOR_L10N.getStr(copyDeclarationLabel),
click: () => { click: () => {
this._onCopyPropertyDeclaration(); this._onCopyDeclaration();
}, },
visible: false, visible: false,
}); });
@ -165,11 +165,11 @@ StyleInspectorMenu.prototype = {
if (this.isRuleView && this._clickedNodeInfo) { if (this.isRuleView && this._clickedNodeInfo) {
switch (this._clickedNodeInfo.type) { switch (this._clickedNodeInfo.type) {
case VIEW_NODE_PROPERTY_TYPE : case VIEW_NODE_PROPERTY_TYPE :
menuitemCopyPropertyDeclaration.visible = true; menuitemCopyDeclaration.visible = true;
menuitemCopyPropertyName.visible = true; menuitemCopyPropertyName.visible = true;
break; break;
case VIEW_NODE_VALUE_TYPE : case VIEW_NODE_VALUE_TYPE :
menuitemCopyPropertyDeclaration.visible = true; menuitemCopyDeclaration.visible = true;
menuitemCopyPropertyValue.visible = true; menuitemCopyPropertyValue.visible = true;
break; break;
case VIEW_NODE_SELECTOR_TYPE : case VIEW_NODE_SELECTOR_TYPE :
@ -187,7 +187,7 @@ StyleInspectorMenu.prototype = {
menu.append(menuitemCopyColor); menu.append(menuitemCopyColor);
menu.append(menuitemCopyUrl); menu.append(menuitemCopyUrl);
menu.append(menuitemCopyImageDataUrl); menu.append(menuitemCopyImageDataUrl);
menu.append(menuitemCopyPropertyDeclaration); menu.append(menuitemCopyDeclaration);
menu.append(menuitemCopyPropertyName); menu.append(menuitemCopyPropertyName);
menu.append(menuitemCopyPropertyValue); menu.append(menuitemCopyPropertyValue);
menu.append(menuitemCopySelector); menu.append(menuitemCopySelector);
@ -409,9 +409,9 @@ StyleInspectorMenu.prototype = {
}, },
/** /**
* Copy the rule property declaration of the current clicked node. * Copy the CSS declaration of the current clicked node.
*/ */
_onCopyPropertyDeclaration: function() { _onCopyDeclaration: function() {
if (!this._clickedNodeInfo) { if (!this._clickedNodeInfo) {
return; return;
} }

View File

@ -43,6 +43,10 @@ changes.contextmenu.copyAllChanges=Copy All Changes
# Changes" button # Changes" button
changes.contextmenu.copyAllChangesDescription=Copy a list of all CSS changes to clipboard. changes.contextmenu.copyAllChangesDescription=Copy a list of all CSS changes to clipboard.
# LOCALIZATION NOTE (changes.contextmenu.copyDeclaration): Label for "Copy Declaration"
# option in Changes panel context menu which copies the target CSS declaration.
changes.contextmenu.copyDeclaration=Copy Declaration
# LOCALIZATION NOTE (changes.contextmenu.copyRule): Label for "Copy Rule" option in # LOCALIZATION NOTE (changes.contextmenu.copyRule): Label for "Copy Rule" option in
# Changes panel context menu which copies the complete contents of a CSS rule. # Changes panel context menu which copies the complete contents of a CSS rule.
changes.contextmenu.copyRule=Copy Rule changes.contextmenu.copyRule=Copy Rule

View File

@ -157,7 +157,7 @@
display: block; display: block;
} }
#sidebar-panel-changes .declaration-name { .changes__declaration-name {
margin-left: 10px; margin-left: 10px;
} }

View File

@ -338,31 +338,29 @@ This is best shown via an example:
"use strict"; "use strict";
const { Toolbox } = require("devtools/client/framework/toolbox"); const { Toolbox } = require("devtools/client/framework/toolbox");
const { TelemetryTestUtils } = ChromeUtils.import("resource://testing-common/TelemetryTestUtils.jsm");
const URL = "data:text/html;charset=utf8,browser_toolbox_telemetry_close.js"; const URL = "data:text/html;charset=utf8,browser_toolbox_telemetry_close.js";
const OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT; const { RIGHT, BOTTOM } = Toolbox.HostType;
const { SIDE, BOTTOM } = Toolbox.HostType; const DATA = [
const TELEMETRY_DATA = [
{ {
timestamp: null,
category: "devtools.main", category: "devtools.main",
method: "close", method: "close",
object: "tools", object: "tools",
value: null, value: null,
extra: { extra: {
host: "right", host: "right",
width: "1440" width: w => w > 0,
} }
}, },
{ {
timestamp: null,
category: "devtools.main", category: "devtools.main",
method: "close", method: "close",
object: "tools", object: "tools",
value: null, value: null,
extra: { extra: {
host: "bottom", host: "bottom",
width: "1440" width: w => w > 0,
} }
} }
]; ];
@ -372,8 +370,7 @@ add_task(async function() {
Services.telemetry.clearEvents(); Services.telemetry.clearEvents();
// Ensure no events have been logged // Ensure no events have been logged
const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true); TelemetryTestUtils.assertNumberOfEvents(0);
ok(!snapshot.parent, "No events have been logged for the main process");
await openAndCloseToolbox("webconsole", SIDE); await openAndCloseToolbox("webconsole", SIDE);
await openAndCloseToolbox("webconsole", BOTTOM); await openAndCloseToolbox("webconsole", BOTTOM);
@ -391,27 +388,7 @@ async function openAndCloseToolbox(toolId, host) {
} }
function checkResults() { function checkResults() {
const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true); TelemetryTestUtils.assertEvents(DATA, {category: "devtools.main", method: "close", object: "tools"});
const events = snapshot.parent.filter(event => event[1] === "devtools.main" &&
event[2] === "close" &&
event[3] === "tools" &&
event[4] === null
);
for (const i in TELEMETRY_DATA) {
const [ timestamp, category, method, object, value, extra ] = events[i];
const expected = TELEMETRY_DATA[i];
// ignore timestamp
ok(timestamp > 0, "timestamp is greater than 0");
is(category, expected.category, "category is correct");
is(method, expected.method, "method is correct");
is(object, expected.object, "object is correct");
is(value, expected.value, "value is correct");
is(extra.host, expected.extra.host, "host is correct");
ok(extra.width > 0, "width is greater than 0");
}
} }
``` ```

View File

@ -102,9 +102,6 @@ function Sandbox(options) {
sandboxPrototype: "prototype" in options ? options.prototype : {}, sandboxPrototype: "prototype" in options ? options.prototype : {},
invisibleToDebugger: "invisibleToDebugger" in options ? invisibleToDebugger: "invisibleToDebugger" in options ?
options.invisibleToDebugger : false, options.invisibleToDebugger : false,
// For now create the sandbox in a new compartment. This is temporary until
// bug 1515290 fixes some devtools tests to not rely on this.
freshCompartment: true,
}; };
const sandbox = Cu.Sandbox(systemPrincipal, options); const sandbox = Cu.Sandbox(systemPrincipal, options);

View File

@ -206,9 +206,9 @@ styleinspector.contextmenu.copy.accessKey=C
# rule view context menu for copying the source location. # rule view context menu for copying the source location.
styleinspector.contextmenu.copyLocation=Copy Location styleinspector.contextmenu.copyLocation=Copy Location
# LOCALIZATION NOTE (styleinspector.contextmenu.copyPropertyDeclaration): Text # LOCALIZATION NOTE (styleinspector.contextmenu.copyDeclaration): Text
# displayed in the rule view context menu for copying the property declaration. # displayed in the rule view context menu for copying the CSS declaration.
styleinspector.contextmenu.copyPropertyDeclaration=Copy Property Declaration styleinspector.contextmenu.copyDeclaration=Copy Declaration
# LOCALIZATION NOTE (styleinspector.contextmenu.copyPropertyName): Text displayed in # LOCALIZATION NOTE (styleinspector.contextmenu.copyPropertyName): Text displayed in
# the rule view context menu for copying the property name. # the rule view context menu for copying the property name.

View File

@ -28,6 +28,7 @@
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsScriptError.h" #include "nsScriptError.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
#include "xpcprivate.h"
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
@ -547,9 +548,43 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(BrowsingContext)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(BrowsingContext, AddRef) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(BrowsingContext, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(BrowsingContext, Release) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(BrowsingContext, Release)
class RemoteLocationProxy
: public RemoteObjectProxy<BrowsingContext::LocationProxy,
Location_Binding::sCrossOriginAttributes,
Location_Binding::sCrossOriginMethods> {
public:
typedef RemoteObjectProxy Base;
constexpr RemoteLocationProxy()
: RemoteObjectProxy(prototypes::id::Location) {}
void NoteChildren(JSObject* aProxy,
nsCycleCollectionTraversalCallback& aCb) const override {
auto location =
static_cast<BrowsingContext::LocationProxy*>(GetNative(aProxy));
CycleCollectionNoteChild(aCb, location->GetBrowsingContext(),
"js::GetObjectPrivate(obj)->GetBrowsingContext()");
}
};
static const RemoteLocationProxy sSingleton;
// Give RemoteLocationProxy 2 reserved slots, like the other wrappers,
// so JSObject::swap can swap it with CrossCompartmentWrappers without requiring
// malloc.
template <>
const js::Class RemoteLocationProxy::Base::sClass =
PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2));
void BrowsingContext::Location(JSContext* aCx, void BrowsingContext::Location(JSContext* aCx,
JS::MutableHandle<JSObject*> aLocation, JS::MutableHandle<JSObject*> aLocation,
OOMReporter& aError) {} ErrorResult& aError) {
aError.MightThrowJSException();
sSingleton.GetProxyObject(aCx, &mLocation, aLocation);
if (!aLocation) {
aError.StealExceptionFromJSContext(aCx);
}
}
void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) { void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) {
// FIXME We need to set mClosed, but only once we're sending the // FIXME We need to set mClosed, but only once we're sending the
@ -673,6 +708,28 @@ void BrowsingContext::Transaction::Commit(BrowsingContext* aBrowsingContext) {
Apply(aBrowsingContext); Apply(aBrowsingContext);
} }
void BrowsingContext::LocationProxy::SetHref(const nsAString& aHref,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aError) {
nsPIDOMWindowOuter* win = GetBrowsingContext()->GetDOMWindow();
if (!win || !win->GetLocation()) {
aError.Throw(NS_ERROR_FAILURE);
return;
}
win->GetLocation()->SetHref(aHref, aSubjectPrincipal, aError);
}
void BrowsingContext::LocationProxy::Replace(const nsAString& aUrl,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aError) {
nsPIDOMWindowOuter* win = GetBrowsingContext()->GetDOMWindow();
if (!win || !win->GetLocation()) {
aError.Throw(NS_ERROR_FAILURE);
return;
}
win->GetLocation()->Replace(aUrl, aSubjectPrincipal, aError);
}
} // namespace dom } // namespace dom
namespace ipc { namespace ipc {

View File

@ -20,6 +20,7 @@
#include "nsWrapperCache.h" #include "nsWrapperCache.h"
class nsGlobalWindowOuter; class nsGlobalWindowOuter;
class nsIPrincipal;
class nsOuterWindowProxy; class nsOuterWindowProxy;
class PickleIterator; class PickleIterator;
@ -31,7 +32,6 @@ namespace mozilla {
class ErrorResult; class ErrorResult;
class LogModule; class LogModule;
class OOMReporter;
namespace ipc { namespace ipc {
class IProtocol; class IProtocol;
@ -267,7 +267,7 @@ class BrowsingContext : public nsWrapperCache,
BrowsingContext* Window() { return Self(); } BrowsingContext* Window() { return Self(); }
BrowsingContext* Self() { return this; } BrowsingContext* Self() { return this; }
void Location(JSContext* aCx, JS::MutableHandle<JSObject*> aLocation, void Location(JSContext* aCx, JS::MutableHandle<JSObject*> aLocation,
OOMReporter& aError); ErrorResult& aError);
void Close(CallerType aCallerType, ErrorResult& aError); void Close(CallerType aCallerType, ErrorResult& aError);
bool GetClosed(ErrorResult&) { return mClosed; } bool GetClosed(ErrorResult&) { return mClosed; }
void Focus(ErrorResult& aError); void Focus(ErrorResult& aError);
@ -329,6 +329,34 @@ class BrowsingContext : public nsWrapperCache,
BrowsingContext* TopLevelBrowsingContext(); BrowsingContext* TopLevelBrowsingContext();
friend class Location;
friend class RemoteLocationProxy;
/**
* LocationProxy is the class for the native object stored as a private in a
* RemoteLocationProxy proxy representing a Location object in a different
* process. It forwards all operations to its BrowsingContext and aggregates
* its refcount to that BrowsingContext.
*/
class LocationProxy {
public:
MozExternalRefCountType AddRef() { return GetBrowsingContext()->AddRef(); }
MozExternalRefCountType Release() {
return GetBrowsingContext()->Release();
}
void SetHref(const nsAString& aHref, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aError);
void Replace(const nsAString& aUrl, nsIPrincipal& aSubjectPrincipal,
ErrorResult& aError);
private:
friend class RemoteLocationProxy;
BrowsingContext* GetBrowsingContext() {
return reinterpret_cast<BrowsingContext*>(
uintptr_t(this) - offsetof(BrowsingContext, mLocation));
}
};
// Type of BrowsingContent // Type of BrowsingContent
const Type mType; const Type mType;
@ -345,6 +373,7 @@ class BrowsingContext : public nsWrapperCache,
// nsOuterWindowProxy handler, which will update the pointer from its // nsOuterWindowProxy handler, which will update the pointer from its
// objectMoved hook and clear it from its finalize hook. // objectMoved hook and clear it from its finalize hook.
JS::Heap<JSObject*> mWindowProxy; JS::Heap<JSObject*> mWindowProxy;
LocationProxy mLocation;
// This flag is only valid in the top level browsing context, it indicates // This flag is only valid in the top level browsing context, it indicates
// whether the corresponding document has been activated by user gesture. // whether the corresponding document has been activated by user gesture.

View File

@ -108,6 +108,7 @@ LOCAL_INCLUDES += [
'/docshell/shistory', '/docshell/shistory',
'/dom/base', '/dom/base',
'/dom/bindings', '/dom/bindings',
'/js/xpconnect/src',
'/layout/base', '/layout/base',
'/layout/generic', '/layout/generic',
'/layout/style', '/layout/style',

View File

@ -29,7 +29,7 @@ namespace dom {
class Location final : public nsISupports, public nsWrapperCache { class Location final : public nsISupports, public nsWrapperCache {
public: public:
typedef Location RemoteProxy; typedef BrowsingContext::LocationProxy RemoteProxy;
Location(nsPIDOMWindowInner* aWindow, nsIDocShell* aDocShell); Location(nsPIDOMWindowInner* aWindow, nsIDocShell* aDocShell);

View File

@ -32,6 +32,8 @@ class RemoteOuterWindowProxy
Window_Binding::sCrossOriginAttributes, Window_Binding::sCrossOriginAttributes,
Window_Binding::sCrossOriginMethods> { Window_Binding::sCrossOriginMethods> {
public: public:
typedef RemoteObjectProxy Base;
constexpr RemoteOuterWindowProxy() constexpr RemoteOuterWindowProxy()
: RemoteObjectProxy(prototypes::id::Window) {} : RemoteObjectProxy(prototypes::id::Window) {}
@ -45,17 +47,22 @@ class RemoteOuterWindowProxy
// SpiderMonkey extensions // SpiderMonkey extensions
bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy, bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::AutoIdVector& props) const final; JS::AutoIdVector& props) const final;
void finalize(JSFreeOp* aFop, JSObject* aProxy) const final;
const char* className(JSContext* aCx, void NoteChildren(JSObject* aProxy,
JS::Handle<JSObject*> aProxy) const final; nsCycleCollectionTraversalCallback& aCb) const override {
CycleCollectionNoteChild(aCb,
static_cast<BrowsingContext*>(GetNative(aProxy)),
"js::GetObjectPrivate(obj)");
}
}; };
static const RemoteOuterWindowProxy sSingleton; static const RemoteOuterWindowProxy sSingleton;
// Give RemoteOuterWindowProxyClass 2 reserved slots, like the other wrappers, // Give RemoteOuterWindowProxy 2 reserved slots, like the other wrappers,
// so JSObject::swap can swap it with CrossCompartmentWrappers without requiring // so JSObject::swap can swap it with CrossCompartmentWrappers without requiring
// malloc. // malloc.
const js::Class RemoteOuterWindowProxyClass = template <>
const js::Class RemoteOuterWindowProxy::Base::sClass =
PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2)); PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2));
bool GetRemoteOuterWindowProxy(JSContext* aCx, BrowsingContext* aContext, bool GetRemoteOuterWindowProxy(JSContext* aCx, BrowsingContext* aContext,
@ -63,30 +70,8 @@ bool GetRemoteOuterWindowProxy(JSContext* aCx, BrowsingContext* aContext,
MOZ_ASSERT(!aContext->GetDocShell(), MOZ_ASSERT(!aContext->GetDocShell(),
"Why are we creating a RemoteOuterWindowProxy?"); "Why are we creating a RemoteOuterWindowProxy?");
xpc::CompartmentPrivate* priv = sSingleton.GetProxyObject(aCx, aContext, aRetVal);
xpc::CompartmentPrivate::Get(JS::CurrentGlobalOrNull(aCx)); return !!aRetVal;
xpc::CompartmentPrivate::RemoteProxyMap& map = priv->GetRemoteProxyMap();
auto result = map.lookupForAdd(aContext);
if (result) {
aRetVal.set(result->value());
return true;
}
JS::Rooted<JSObject*> obj(
aCx, sSingleton.CreateProxyObject(aCx, aContext,
&RemoteOuterWindowProxyClass));
if (!obj) {
return false;
}
NS_ADDREF(aContext);
if (!map.add(result, aContext, obj)) {
JS_ReportOutOfMemory(aCx);
return false;
}
aRetVal.set(obj);
return true;
} }
static BrowsingContext* GetBrowsingContext(JSObject* aProxy) { static BrowsingContext* GetBrowsingContext(JSObject* aProxy) {
@ -177,17 +162,5 @@ bool RemoteOuterWindowProxy::getOwnEnumerablePropertyKeys(
return AppendIndexedPropertyNames(aCx, GetBrowsingContext(aProxy), aProps); return AppendIndexedPropertyNames(aCx, GetBrowsingContext(aProxy), aProps);
} }
void RemoteOuterWindowProxy::finalize(JSFreeOp* aFop, JSObject* aProxy) const {
BrowsingContext* bc = GetBrowsingContext(aProxy);
RefPtr<BrowsingContext> self(dont_AddRef(bc));
}
const char* RemoteOuterWindowProxy::className(
JSContext* aCx, JS::Handle<JSObject*> aProxy) const {
MOZ_ASSERT(js::IsProxy(aProxy));
return "Object";
}
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla

View File

@ -9132,7 +9132,6 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
# We'll use a JSObject. It might make more sense to use remoteType's # We'll use a JSObject. It might make more sense to use remoteType's
# RemoteProxy, but it's not easy to construct a type for that from here. # RemoteProxy, but it's not easy to construct a type for that from here.
remoteType = BuiltinTypes[IDLBuiltinType.Types.object] remoteType = BuiltinTypes[IDLBuiltinType.Types.object]
extendedAttributes.append('canOOM')
extendedAttributes.remove('infallible') extendedAttributes.remove('infallible')
prototypeID, _ = PrototypeIDAndDepth(self.descriptor) prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
prefix = fill(""" prefix = fill("""

View File

@ -11,12 +11,6 @@
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
// Give RemoteObjectProxy 2 reserved slots, like the other wrappers, so
// JSObject::swap can swap it with CrossCompartmentWrappers without requiring
// malloc.
const js::Class RemoteObjectProxyClass =
PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2));
bool RemoteObjectProxyBase::getOwnPropertyDescriptor( bool RemoteObjectProxyBase::getOwnPropertyDescriptor(
JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId, JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
JS::MutableHandle<JS::PropertyDescriptor> aDesc) const { JS::MutableHandle<JS::PropertyDescriptor> aDesc) const {
@ -169,12 +163,41 @@ bool RemoteObjectProxyBase::getOwnEnumerablePropertyKeys(
return true; return true;
} }
JSObject* RemoteObjectProxyBase::CreateProxyObject( const char* RemoteObjectProxyBase::className(
JSContext* aCx, void* aNative, const js::Class* aClasp) const { JSContext* aCx, JS::Handle<JSObject*> aProxy) const {
MOZ_ASSERT(js::IsProxy(aProxy));
return "Object";
}
void RemoteObjectProxyBase::GetOrCreateProxyObject(
JSContext* aCx, void* aNative, const js::Class* aClasp,
JS::MutableHandle<JSObject*> aProxy, bool& aNewObjectCreated) const {
xpc::CompartmentPrivate* priv =
xpc::CompartmentPrivate::Get(JS::CurrentGlobalOrNull(aCx));
xpc::CompartmentPrivate::RemoteProxyMap& map = priv->GetRemoteProxyMap();
auto result = map.lookupForAdd(aNative);
if (result) {
aProxy.set(result->value());
return;
}
js::ProxyOptions options; js::ProxyOptions options;
options.setClass(aClasp); options.setClass(aClasp);
JS::Rooted<JS::Value> native(aCx, JS::PrivateValue(aNative)); JS::Rooted<JS::Value> native(aCx, JS::PrivateValue(aNative));
return js::NewProxyObject(aCx, this, native, nullptr, options); JS::Rooted<JSObject*> obj(
aCx, js::NewProxyObject(aCx, this, native, nullptr, options));
if (!obj) {
return;
}
aNewObjectCreated = true;
if (!map.add(result, aNative, obj)) {
return;
}
aProxy.set(obj);
} }
const char RemoteObjectProxyBase::sCrossOriginProxyFamily = 0; const char RemoteObjectProxyBase::sCrossOriginProxyFamily = 0;

View File

@ -70,10 +70,15 @@ class RemoteObjectProxyBase : public js::BaseProxyHandler,
bool getOwnEnumerablePropertyKeys(JSContext* aCx, bool getOwnEnumerablePropertyKeys(JSContext* aCx,
JS::Handle<JSObject*> aProxy, JS::Handle<JSObject*> aProxy,
JS::AutoIdVector& aProps) const override; JS::AutoIdVector& aProps) const override;
const char* className(JSContext* aCx,
JS::Handle<JSObject*> aProxy) const final;
bool isCallable(JSObject* aObj) const final { return false; } bool isCallable(JSObject* aObj) const final { return false; }
bool isConstructor(JSObject* aObj) const final { return false; } bool isConstructor(JSObject* aObj) const final { return false; }
virtual void NoteChildren(JSObject* aProxy,
nsCycleCollectionTraversalCallback& aCb) const = 0;
static void* GetNative(JSObject* aProxy) { static void* GetNative(JSObject* aProxy) {
return js::GetProxyPrivate(aProxy).toPrivate(); return js::GetProxyPrivate(aProxy).toPrivate();
} }
@ -101,8 +106,17 @@ class RemoteObjectProxyBase : public js::BaseProxyHandler,
} }
protected: protected:
JSObject* CreateProxyObject(JSContext* aCx, void* aNative, /**
const js::Class* aClasp) const; * Gets an existing cached proxy object, or creates a new one and caches it.
* aProxy will be null on failure. aNewObjectCreated is set to true if a new
* object was created, callers probably need to addref the native in that
* case. aNewObjectCreated can be true even if aProxy is null, if something
* failed after creating the object.
*/
void GetOrCreateProxyObject(JSContext* aCx, void* aNative,
const js::Class* aClasp,
JS::MutableHandle<JSObject*> aProxy,
bool& aNewObjectCreated) const;
const prototypes::ID mPrototypeID; const prototypes::ID mPrototypeID;
@ -126,9 +140,18 @@ class RemoteObjectProxyBase : public js::BaseProxyHandler,
template <class Native, JSPropertySpec* P, JSFunctionSpec* F> template <class Native, JSPropertySpec* P, JSFunctionSpec* F>
class RemoteObjectProxy : public RemoteObjectProxyBase { class RemoteObjectProxy : public RemoteObjectProxyBase {
public: public:
JSObject* CreateProxyObject(JSContext* aCx, Native* aNative, void finalize(JSFreeOp* aFop, JSObject* aProxy) const final {
const js::Class* aClasp) const { auto native = static_cast<Native*>(GetNative(aProxy));
return RemoteObjectProxyBase::CreateProxyObject(aCx, aNative, aClasp); RefPtr<Native> self(dont_AddRef(native));
}
void GetProxyObject(JSContext* aCx, Native* aNative,
JS::MutableHandle<JSObject*> aProxy) const {
bool objectCreated = false;
GetOrCreateProxyObject(aCx, aNative, &sClass, aProxy, objectCreated);
if (objectCreated) {
NS_ADDREF(aNative);
}
} }
protected: protected:
@ -140,6 +163,8 @@ class RemoteObjectProxy : public RemoteObjectProxyBase {
return MaybeCrossOriginObjectMixins::EnsureHolder( return MaybeCrossOriginObjectMixins::EnsureHolder(
aCx, aProxy, /* slot = */ 0, P, F, aHolder); aCx, aProxy, /* slot = */ 0, P, F, aHolder);
} }
static const js::Class sClass;
}; };
/** /**

View File

@ -2496,7 +2496,7 @@ nsresult ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
nsTArray<mozilla::ipc::ContentSecurityPolicy> policies; nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
aSavedResponseOut->mValue.principalInfo() = aSavedResponseOut->mValue.principalInfo() =
mozilla::ipc::ContentPrincipalInfo(attrs, origin, specNoSuffix, mozilla::ipc::ContentPrincipalInfo(attrs, origin, specNoSuffix,
std::move(policies)); Nothing(), std::move(policies));
} }
bool nullPadding = false; bool nullPadding = false;

View File

@ -27,12 +27,10 @@ using namespace gfx;
* due to CORS security. * due to CORS security.
*/ */
struct ImageCacheKey { struct ImageCacheKey {
ImageCacheKey(imgIContainer* aImage, HTMLCanvasElement* aCanvas, ImageCacheKey(imgIContainer* aImage, HTMLCanvasElement* aCanvas)
bool aIsAccelerated) : mImage(aImage), mCanvas(aCanvas) {}
: mImage(aImage), mCanvas(aCanvas), mIsAccelerated(aIsAccelerated) {}
nsCOMPtr<imgIContainer> mImage; nsCOMPtr<imgIContainer> mImage;
HTMLCanvasElement* mCanvas; HTMLCanvasElement* mCanvas;
bool mIsAccelerated;
}; };
/** /**
@ -43,13 +41,11 @@ struct ImageCacheEntryData {
ImageCacheEntryData(const ImageCacheEntryData& aOther) ImageCacheEntryData(const ImageCacheEntryData& aOther)
: mImage(aOther.mImage), : mImage(aOther.mImage),
mCanvas(aOther.mCanvas), mCanvas(aOther.mCanvas),
mIsAccelerated(aOther.mIsAccelerated),
mSourceSurface(aOther.mSourceSurface), mSourceSurface(aOther.mSourceSurface),
mSize(aOther.mSize) {} mSize(aOther.mSize) {}
explicit ImageCacheEntryData(const ImageCacheKey& aKey) explicit ImageCacheEntryData(const ImageCacheKey& aKey)
: mImage(aKey.mImage), : mImage(aKey.mImage),
mCanvas(aKey.mCanvas), mCanvas(aKey.mCanvas) {}
mIsAccelerated(aKey.mIsAccelerated) {}
nsExpirationState* GetExpirationState() { return &mState; } nsExpirationState* GetExpirationState() { return &mState; }
size_t SizeInBytes() { return mSize.width * mSize.height * 4; } size_t SizeInBytes() { return mSize.width * mSize.height * 4; }
@ -57,7 +53,6 @@ struct ImageCacheEntryData {
// Key // Key
nsCOMPtr<imgIContainer> mImage; nsCOMPtr<imgIContainer> mImage;
HTMLCanvasElement* mCanvas; HTMLCanvasElement* mCanvas;
bool mIsAccelerated;
// Value // Value
RefPtr<SourceSurface> mSourceSurface; RefPtr<SourceSurface> mSourceSurface;
IntSize mSize; IntSize mSize;
@ -76,13 +71,12 @@ class ImageCacheEntry : public PLDHashEntryHdr {
~ImageCacheEntry() {} ~ImageCacheEntry() {}
bool KeyEquals(KeyTypePointer key) const { bool KeyEquals(KeyTypePointer key) const {
return mData->mImage == key->mImage && mData->mCanvas == key->mCanvas && return mData->mImage == key->mImage && mData->mCanvas == key->mCanvas;
mData->mIsAccelerated == key->mIsAccelerated;
} }
static KeyTypePointer KeyToPointer(KeyType& key) { return &key; } static KeyTypePointer KeyToPointer(KeyType& key) { return &key; }
static PLDHashNumber HashKey(KeyTypePointer key) { static PLDHashNumber HashKey(KeyTypePointer key) {
return HashGeneric(key->mImage.get(), key->mCanvas, key->mIsAccelerated); return HashGeneric(key->mImage.get(), key->mCanvas);
} }
enum { ALLOW_MEMMOVE = true }; enum { ALLOW_MEMMOVE = true };
@ -93,11 +87,10 @@ class ImageCacheEntry : public PLDHashEntryHdr {
* Used for all images across all canvases. * Used for all images across all canvases.
*/ */
struct AllCanvasImageCacheKey { struct AllCanvasImageCacheKey {
AllCanvasImageCacheKey(imgIContainer* aImage, bool aIsAccelerated) explicit AllCanvasImageCacheKey(imgIContainer* aImage)
: mImage(aImage), mIsAccelerated(aIsAccelerated) {} : mImage(aImage) {}
nsCOMPtr<imgIContainer> mImage; nsCOMPtr<imgIContainer> mImage;
bool mIsAccelerated;
}; };
class AllCanvasImageCacheEntry : public PLDHashEntryHdr { class AllCanvasImageCacheEntry : public PLDHashEntryHdr {
@ -106,27 +99,25 @@ class AllCanvasImageCacheEntry : public PLDHashEntryHdr {
typedef const AllCanvasImageCacheKey* KeyTypePointer; typedef const AllCanvasImageCacheKey* KeyTypePointer;
explicit AllCanvasImageCacheEntry(const KeyType* aKey) explicit AllCanvasImageCacheEntry(const KeyType* aKey)
: mImage(aKey->mImage), mIsAccelerated(aKey->mIsAccelerated) {} : mImage(aKey->mImage) {}
AllCanvasImageCacheEntry(const AllCanvasImageCacheEntry& toCopy) AllCanvasImageCacheEntry(const AllCanvasImageCacheEntry& toCopy)
: mImage(toCopy.mImage), : mImage(toCopy.mImage),
mIsAccelerated(toCopy.mIsAccelerated),
mSourceSurface(toCopy.mSourceSurface) {} mSourceSurface(toCopy.mSourceSurface) {}
~AllCanvasImageCacheEntry() {} ~AllCanvasImageCacheEntry() {}
bool KeyEquals(KeyTypePointer key) const { bool KeyEquals(KeyTypePointer key) const {
return mImage == key->mImage && mIsAccelerated == key->mIsAccelerated; return mImage == key->mImage;
} }
static KeyTypePointer KeyToPointer(KeyType& key) { return &key; } static KeyTypePointer KeyToPointer(KeyType& key) { return &key; }
static PLDHashNumber HashKey(KeyTypePointer key) { static PLDHashNumber HashKey(KeyTypePointer key) {
return HashGeneric(key->mImage.get(), key->mIsAccelerated); return HashGeneric(key->mImage.get());
} }
enum { ALLOW_MEMMOVE = true }; enum { ALLOW_MEMMOVE = true };
nsCOMPtr<imgIContainer> mImage; nsCOMPtr<imgIContainer> mImage;
bool mIsAccelerated;
RefPtr<SourceSurface> mSourceSurface; RefPtr<SourceSurface> mSourceSurface;
}; };
@ -149,11 +140,10 @@ class ImageCache final : public nsExpirationTracker<ImageCacheEntryData, 4> {
// Remove from the all canvas cache entry first since nsExpirationTracker // Remove from the all canvas cache entry first since nsExpirationTracker
// will delete aObject. // will delete aObject.
mAllCanvasCache.RemoveEntry( mAllCanvasCache.RemoveEntry(
AllCanvasImageCacheKey(aObject->mImage, aObject->mIsAccelerated)); AllCanvasImageCacheKey(aObject->mImage));
// Deleting the entry will delete aObject since the entry owns aObject. // Deleting the entry will delete aObject since the entry owns aObject.
mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mCanvas, mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mCanvas));
aObject->mIsAccelerated));
} }
nsTHashtable<ImageCacheEntry> mCache; nsTHashtable<ImageCacheEntry> mCache;
@ -273,8 +263,7 @@ static already_AddRefed<imgIContainer> GetImageContainer(dom::Element* aImage) {
void CanvasImageCache::NotifyDrawImage(Element* aImage, void CanvasImageCache::NotifyDrawImage(Element* aImage,
HTMLCanvasElement* aCanvas, HTMLCanvasElement* aCanvas,
SourceSurface* aSource, SourceSurface* aSource,
const IntSize& aSize, const IntSize& aSize) {
bool aIsAccelerated) {
if (!gImageCache) { if (!gImageCache) {
gImageCache = new ImageCache(); gImageCache = new ImageCache();
nsContentUtils::RegisterShutdownObserver( nsContentUtils::RegisterShutdownObserver(
@ -286,8 +275,8 @@ void CanvasImageCache::NotifyDrawImage(Element* aImage,
return; return;
} }
AllCanvasImageCacheKey allCanvasCacheKey(imgContainer, aIsAccelerated); AllCanvasImageCacheKey allCanvasCacheKey(imgContainer);
ImageCacheKey canvasCacheKey(imgContainer, aCanvas, aIsAccelerated); ImageCacheKey canvasCacheKey(imgContainer, aCanvas);
ImageCacheEntry* entry = gImageCache->mCache.PutEntry(canvasCacheKey); ImageCacheEntry* entry = gImageCache->mCache.PutEntry(canvasCacheKey);
if (entry) { if (entry) {
@ -317,8 +306,7 @@ void CanvasImageCache::NotifyDrawImage(Element* aImage,
gImageCache->AgeOneGeneration(); gImageCache->AgeOneGeneration();
} }
SourceSurface* CanvasImageCache::LookupAllCanvas(Element* aImage, SourceSurface* CanvasImageCache::LookupAllCanvas(Element* aImage) {
bool aIsAccelerated) {
if (!gImageCache) { if (!gImageCache) {
return nullptr; return nullptr;
} }
@ -329,7 +317,7 @@ SourceSurface* CanvasImageCache::LookupAllCanvas(Element* aImage,
} }
AllCanvasImageCacheEntry* entry = gImageCache->mAllCanvasCache.GetEntry( AllCanvasImageCacheEntry* entry = gImageCache->mAllCanvasCache.GetEntry(
AllCanvasImageCacheKey(imgContainer, aIsAccelerated)); AllCanvasImageCacheKey(imgContainer));
if (!entry) { if (!entry) {
return nullptr; return nullptr;
} }
@ -339,8 +327,7 @@ SourceSurface* CanvasImageCache::LookupAllCanvas(Element* aImage,
SourceSurface* CanvasImageCache::LookupCanvas(Element* aImage, SourceSurface* CanvasImageCache::LookupCanvas(Element* aImage,
HTMLCanvasElement* aCanvas, HTMLCanvasElement* aCanvas,
IntSize* aSizeOut, IntSize* aSizeOut) {
bool aIsAccelerated) {
if (!gImageCache) { if (!gImageCache) {
return nullptr; return nullptr;
} }
@ -351,7 +338,7 @@ SourceSurface* CanvasImageCache::LookupCanvas(Element* aImage,
} }
ImageCacheEntry* entry = gImageCache->mCache.GetEntry( ImageCacheEntry* entry = gImageCache->mCache.GetEntry(
ImageCacheKey(imgContainer, aCanvas, aIsAccelerated)); ImageCacheKey(imgContainer, aCanvas));
if (!entry) { if (!entry) {
return nullptr; return nullptr;
} }

View File

@ -33,15 +33,13 @@ class CanvasImageCache {
*/ */
static void NotifyDrawImage(dom::Element* aImage, static void NotifyDrawImage(dom::Element* aImage,
dom::HTMLCanvasElement* aCanvas, dom::HTMLCanvasElement* aCanvas,
SourceSurface* aSource, const gfx::IntSize& aSize, SourceSurface* aSource, const gfx::IntSize& aSize);
bool aIsAccelerated);
/** /**
* Check whether aImage has recently been drawn any canvas. If we return * Check whether aImage has recently been drawn any canvas. If we return
* a non-null surface, then the same image was recently drawn into a canvas. * a non-null surface, then the same image was recently drawn into a canvas.
*/ */
static SourceSurface* LookupAllCanvas(dom::Element* aImage, static SourceSurface* LookupAllCanvas(dom::Element* aImage);
bool aIsAccelerated);
/** /**
* Like the top above, but restricts the lookup to only aCanvas. This is * Like the top above, but restricts the lookup to only aCanvas. This is
@ -49,8 +47,7 @@ class CanvasImageCache {
*/ */
static SourceSurface* LookupCanvas(dom::Element* aImage, static SourceSurface* LookupCanvas(dom::Element* aImage,
dom::HTMLCanvasElement* aCanvas, dom::HTMLCanvasElement* aCanvas,
gfx::IntSize* aSizeOut, gfx::IntSize* aSizeOut);
bool aIsAccelerated);
}; };
} // namespace mozilla } // namespace mozilla

View File

@ -106,8 +106,6 @@
#include "mozilla/dom/SVGMatrix.h" #include "mozilla/dom/SVGMatrix.h"
#include "mozilla/FloatingPoint.h" #include "mozilla/FloatingPoint.h"
#include "nsGlobalWindow.h" #include "nsGlobalWindow.h"
#include "GLContext.h"
#include "GLContextProvider.h"
#include "nsIScreenManager.h" #include "nsIScreenManager.h"
#include "nsFilterInstance.h" #include "nsFilterInstance.h"
#include "nsSVGLength2.h" #include "nsSVGLength2.h"
@ -125,16 +123,6 @@
#undef free // apparently defined by some windows header, clashing with a #undef free // apparently defined by some windows header, clashing with a
// free() method in SkTypes.h // free() method in SkTypes.h
#include "SkiaGLGlue.h"
#ifdef USE_SKIA
# include "SurfaceTypes.h"
# include "GLBlitHelper.h"
# include "ScopedGLHelpers.h"
#endif
using mozilla::gl::GLContext;
using mozilla::gl::GLContextProvider;
using mozilla::gl::SkiaGLGlue;
#ifdef XP_WIN #ifdef XP_WIN
# include "gfxWindowsPlatform.h" # include "gfxWindowsPlatform.h"
@ -760,105 +748,6 @@ CanvasShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
return NS_OK; return NS_OK;
} }
class CanvasDrawObserver {
public:
explicit CanvasDrawObserver(CanvasRenderingContext2D* aCanvasContext);
// Only enumerate draw calls that could affect the heuristic
enum DrawCallType { PutImageData, GetImageData, DrawImage };
// This is the one that we call on relevant draw calls and count
// GPU vs. CPU preferrable calls...
void DidDrawCall(DrawCallType aType);
// When this returns true, the observer is done making the decisions.
// Right now, we expect to get rid of the observer after the FrameEnd
// returns true, though the decision could eventually change if the
// function calls shift. If we change to monitor the functions called
// and make decisions to change more than once, we would probably want
// FrameEnd to reset the timer and counters as it returns true.
bool FrameEnd();
private:
// These values will be picked up from preferences:
int32_t mMinFramesBeforeDecision;
float mMinSecondsBeforeDecision;
int32_t mMinCallsBeforeDecision;
CanvasRenderingContext2D* mCanvasContext;
int32_t mSoftwarePreferredCalls;
int32_t mGPUPreferredCalls;
int32_t mFramesRendered;
TimeStamp mCreationTime;
};
// We are not checking for the validity of the preference values. For example,
// negative values will have an effect of a quick exit, so no harm done.
CanvasDrawObserver::CanvasDrawObserver(CanvasRenderingContext2D* aCanvasContext)
: mMinFramesBeforeDecision(gfxPrefs::CanvasAutoAccelerateMinFrames()),
mMinSecondsBeforeDecision(gfxPrefs::CanvasAutoAccelerateMinSeconds()),
mMinCallsBeforeDecision(gfxPrefs::CanvasAutoAccelerateMinCalls()),
mCanvasContext(aCanvasContext),
mSoftwarePreferredCalls(0),
mGPUPreferredCalls(0),
mFramesRendered(0),
mCreationTime(TimeStamp::NowLoRes()) {}
void CanvasDrawObserver::DidDrawCall(DrawCallType aType) {
switch (aType) {
case PutImageData:
case GetImageData:
if (mGPUPreferredCalls == 0 && mSoftwarePreferredCalls == 0) {
mCreationTime = TimeStamp::NowLoRes();
}
mSoftwarePreferredCalls++;
break;
case DrawImage:
if (mGPUPreferredCalls == 0 && mSoftwarePreferredCalls == 0) {
mCreationTime = TimeStamp::NowLoRes();
}
mGPUPreferredCalls++;
break;
}
}
// If we return true, the observer is done making the decisions...
bool CanvasDrawObserver::FrameEnd() {
mFramesRendered++;
// We log the first mMinFramesBeforeDecision frames of any
// canvas object then make a call to determine whether it should
// be GPU or CPU backed
if ((mFramesRendered >= mMinFramesBeforeDecision) ||
((TimeStamp::NowLoRes() - mCreationTime).ToSeconds()) >
mMinSecondsBeforeDecision) {
// If we don't have enough data, don't bother changing...
if (mGPUPreferredCalls > mMinCallsBeforeDecision ||
mSoftwarePreferredCalls > mMinCallsBeforeDecision) {
CanvasRenderingContext2D::RenderingMode switchToMode;
if (mGPUPreferredCalls >= mSoftwarePreferredCalls) {
switchToMode =
CanvasRenderingContext2D::RenderingMode::OpenGLBackendMode;
} else {
switchToMode =
CanvasRenderingContext2D::RenderingMode::SoftwareBackendMode;
}
if (switchToMode != mCanvasContext->mRenderingMode) {
if (!mCanvasContext->SwitchRenderingMode(switchToMode)) {
gfxDebug() << "Canvas acceleration failed mode switch to "
<< switchToMode;
}
}
}
// If we ever redesign this class to constantly monitor the functions
// and keep making decisions, we would probably want to reset the counters
// and the timers here...
return true;
}
return false;
}
class CanvasRenderingContext2DUserData : public LayerUserData { class CanvasRenderingContext2DUserData : public LayerUserData {
public: public:
explicit CanvasRenderingContext2DUserData(CanvasRenderingContext2D* aContext) explicit CanvasRenderingContext2DUserData(CanvasRenderingContext2D* aContext)
@ -884,12 +773,6 @@ class CanvasRenderingContext2DUserData : public LayerUserData {
static_cast<CanvasRenderingContext2D*>(aData); static_cast<CanvasRenderingContext2D*>(aData);
if (context) { if (context) {
context->MarkContextClean(); context->MarkContextClean();
if (context->mDrawObserver) {
if (context->mDrawObserver->FrameEnd()) {
// Note that this call deletes and nulls out mDrawObserver:
context->RemoveDrawObserver();
}
}
} }
} }
bool IsForContext(CanvasRenderingContext2D* aContext) { bool IsForContext(CanvasRenderingContext2D* aContext) {
@ -909,7 +792,6 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(CanvasRenderingContext2D)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D)
// Make sure we remove ourselves from the list of demotable contexts (raw // Make sure we remove ourselves from the list of demotable contexts (raw
// pointers), since we're logically destructed at this point. // pointers), since we're logically destructed at this point.
CanvasRenderingContext2D::RemoveDemotableContext(tmp);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) { for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
@ -1000,15 +882,10 @@ NS_INTERFACE_MAP_END
// Initialize our static variables. // Initialize our static variables.
uintptr_t CanvasRenderingContext2D::sNumLivingContexts = 0; uintptr_t CanvasRenderingContext2D::sNumLivingContexts = 0;
DrawTarget* CanvasRenderingContext2D::sErrorTarget = nullptr; DrawTarget* CanvasRenderingContext2D::sErrorTarget = nullptr;
static bool sMaxContextsInitialized = false;
static int32_t sMaxContexts = 0;
CanvasRenderingContext2D::CanvasRenderingContext2D( CanvasRenderingContext2D::CanvasRenderingContext2D(
layers::LayersBackend aCompositorBackend) layers::LayersBackend aCompositorBackend)
: mRenderingMode(RenderingMode::OpenGLBackendMode), : // these are the default values from the Canvas spec
mCompositorBackend(aCompositorBackend)
// these are the default values from the Canvas spec
,
mWidth(0), mWidth(0),
mHeight(0), mHeight(0),
mZero(false), mZero(false),
@ -1017,35 +894,21 @@ CanvasRenderingContext2D::CanvasRenderingContext2D(
mOpaque(false), mOpaque(false),
mResetLayer(true), mResetLayer(true),
mIPC(false), mIPC(false),
mIsSkiaGL(false),
mHasPendingStableStateCallback(false), mHasPendingStableStateCallback(false),
mDrawObserver(nullptr),
mIsEntireFrameInvalid(false), mIsEntireFrameInvalid(false),
mPredictManyRedrawCalls(false), mPredictManyRedrawCalls(false),
mIsCapturedFrameInvalid(false), mIsCapturedFrameInvalid(false),
mPathTransformWillUpdate(false), mPathTransformWillUpdate(false),
mInvalidateCount(0), mInvalidateCount(0),
mWriteOnly(false) { mWriteOnly(false) {
if (!sMaxContextsInitialized) {
sMaxContexts = gfxPrefs::CanvasAzureAcceleratedLimit();
sMaxContextsInitialized = true;
}
sNumLivingContexts++; sNumLivingContexts++;
mShutdownObserver = new CanvasShutdownObserver(this); mShutdownObserver = new CanvasShutdownObserver(this);
nsContentUtils::RegisterShutdownObserver(mShutdownObserver); nsContentUtils::RegisterShutdownObserver(mShutdownObserver);
// The default is to use OpenGL mode
if (AllowOpenGLCanvas()) {
mDrawObserver = new CanvasDrawObserver(this);
} else {
mRenderingMode = RenderingMode::SoftwareBackendMode;
}
} }
CanvasRenderingContext2D::~CanvasRenderingContext2D() { CanvasRenderingContext2D::~CanvasRenderingContext2D() {
RemoveDrawObserver();
RemovePostRefreshObserver(); RemovePostRefreshObserver();
RemoveShutdownObserver(); RemoveShutdownObserver();
Reset(); Reset();
@ -1057,7 +920,6 @@ CanvasRenderingContext2D::~CanvasRenderingContext2D() {
if (!sNumLivingContexts) { if (!sNumLivingContexts) {
NS_IF_RELEASE(sErrorTarget); NS_IF_RELEASE(sErrorTarget);
} }
RemoveDemotableContext(this);
} }
JSObject* CanvasRenderingContext2D::WrapObject( JSObject* CanvasRenderingContext2D::WrapObject(
@ -1228,13 +1090,6 @@ void CanvasRenderingContext2D::Redraw(const gfx::Rect& aR) {
} }
void CanvasRenderingContext2D::DidRefresh() { void CanvasRenderingContext2D::DidRefresh() {
if (IsTargetValid() && mIsSkiaGL) {
SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
MOZ_ASSERT(glue);
auto gl = glue->GetGLContext();
gl->FlushIfHeavyGLCallsSinceLastFlush();
}
} }
void CanvasRenderingContext2D::RedrawUser(const gfxRect& aR) { void CanvasRenderingContext2D::RedrawUser(const gfxRect& aR) {
@ -1249,70 +1104,6 @@ void CanvasRenderingContext2D::RedrawUser(const gfxRect& aR) {
Redraw(newr); Redraw(newr);
} }
bool CanvasRenderingContext2D::AllowOpenGLCanvas() const {
// If we somehow didn't have the correct compositor in the constructor,
// we could do something like this to get it:
//
// HTMLCanvasElement* el = GetCanvas();
// if (el) {
// mCompositorBackend = el->GetCompositorBackendType();
// }
//
// We could have LAYERS_NONE if there was no widget at the time of
// canvas creation, but in that case the
// HTMLCanvasElement::GetCompositorBackendType would return LAYERS_NONE
// as well, so it wouldn't help much.
//
// XXX Disable SkiaGL on WebRender, since there is a case that R8G8B8X8
// is used, but WebRender does not support R8G8B8X8.
return (mCompositorBackend == LayersBackend::LAYERS_OPENGL) &&
gfxPlatform::GetPlatform()->AllowOpenGLCanvas();
}
bool CanvasRenderingContext2D::SwitchRenderingMode(
RenderingMode aRenderingMode) {
if (!(IsTargetValid() || mBufferProvider) ||
mRenderingMode == aRenderingMode) {
return false;
}
MOZ_ASSERT(mBufferProvider);
#ifdef USE_SKIA_GPU
// Do not attempt to switch into GL mode if the platform doesn't allow it.
if ((aRenderingMode == RenderingMode::OpenGLBackendMode) &&
!AllowOpenGLCanvas()) {
return false;
}
#endif
RefPtr<PersistentBufferProvider> oldBufferProvider = mBufferProvider;
// Return the old target to the buffer provider.
// We need to do this before calling EnsureTarget.
ReturnTarget();
mTarget = nullptr;
mBufferProvider = nullptr;
mResetLayer = true;
// Recreate mTarget using the new rendering mode
RenderingMode attemptedMode = EnsureTarget(nullptr, aRenderingMode);
if (!IsTargetValid()) {
return false;
}
if (oldBufferProvider && mTarget) {
CopyBufferProvider(*oldBufferProvider, *mTarget,
IntRect(0, 0, mWidth, mHeight));
}
// We succeeded, so update mRenderingMode to reflect reality
mRenderingMode = attemptedMode;
return true;
}
bool CanvasRenderingContext2D::CopyBufferProvider( bool CanvasRenderingContext2D::CopyBufferProvider(
PersistentBufferProvider& aOld, DrawTarget& aTarget, IntRect aCopyRect) { PersistentBufferProvider& aOld, DrawTarget& aTarget, IntRect aCopyRect) {
// Borrowing the snapshot must be done after ReturnTarget. // Borrowing the snapshot must be done after ReturnTarget.
@ -1328,119 +1119,6 @@ bool CanvasRenderingContext2D::CopyBufferProvider(
} }
void CanvasRenderingContext2D::Demote() { void CanvasRenderingContext2D::Demote() {
if (SwitchRenderingMode(RenderingMode::SoftwareBackendMode)) {
RemoveDemotableContext(this);
}
}
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;
}
void CanvasRenderingContext2D::DemoteOldestContextIfNecessary() {
MOZ_ASSERT(sMaxContextsInitialized);
if (sMaxContexts <= 0) {
return;
}
std::vector<CanvasRenderingContext2D*>& contexts = DemotableContexts();
if (contexts.size() < (size_t)sMaxContexts) return;
CanvasRenderingContext2D* oldest = contexts.front();
if (oldest->SwitchRenderingMode(RenderingMode::SoftwareBackendMode)) {
RemoveDemotableContext(oldest);
}
}
void CanvasRenderingContext2D::AddDemotableContext(
CanvasRenderingContext2D* aContext) {
MOZ_ASSERT(sMaxContextsInitialized);
if (sMaxContexts <= 0) return;
std::vector<CanvasRenderingContext2D*>::iterator iter = std::find(
DemotableContexts().begin(), DemotableContexts().end(), aContext);
if (iter != DemotableContexts().end()) return;
DemotableContexts().push_back(aContext);
}
void CanvasRenderingContext2D::RemoveDemotableContext(
CanvasRenderingContext2D* aContext) {
MOZ_ASSERT(sMaxContextsInitialized);
if (sMaxContexts <= 0) return;
std::vector<CanvasRenderingContext2D*>::iterator iter = std::find(
DemotableContexts().begin(), DemotableContexts().end(), aContext);
if (iter != DemotableContexts().end()) DemotableContexts().erase(iter);
}
#define MIN_SKIA_GL_DIMENSION 16
bool CanvasRenderingContext2D::CheckSizeForSkiaGL(IntSize aSize) {
MOZ_ASSERT(NS_IsMainThread());
int minsize = Preferences::GetInt("gfx.canvas.min-size-for-skia-gl", 128);
if (aSize.width < MIN_SKIA_GL_DIMENSION ||
aSize.height < MIN_SKIA_GL_DIMENSION ||
(aSize.width * aSize.height < minsize * minsize)) {
return false;
}
// Maximum pref allows 3 different options:
// 0 means unlimited size
// > 0 means use value as an absolute threshold
// < 0 means use the number of screen pixels as a threshold
int maxsize = Preferences::GetInt("gfx.canvas.max-size-for-skia-gl", 0);
// unlimited max size
if (!maxsize) {
return true;
}
// absolute max size threshold
if (maxsize > 0) {
return aSize.width <= maxsize && aSize.height <= maxsize;
}
// Cache the number of pixels on the primary screen
static int32_t gScreenPixels = -1;
if (gScreenPixels < 0) {
// Default to historical mobile screen size of 980x480, like FishIEtank.
// In addition, allow skia use up to this size even if the screen is
// smaller. A lot content expects this size to work well. See Bug 999841
if (gfxPlatform::GetPlatform()->HasEnoughTotalSystemMemoryForSkiaGL()) {
gScreenPixels = 980 * 480;
}
nsCOMPtr<nsIScreenManager> screenManager =
do_GetService("@mozilla.org/gfx/screenmanager;1");
if (screenManager) {
nsCOMPtr<nsIScreen> primaryScreen;
screenManager->GetPrimaryScreen(getter_AddRefs(primaryScreen));
if (primaryScreen) {
int32_t x, y, width, height;
primaryScreen->GetRect(&x, &y, &width, &height);
gScreenPixels = std::max(gScreenPixels, width * height);
}
}
}
// Just always use a scale of 1.0. It can be changed if a lot of contents need
// it.
static double gDefaultScale = 1.0;
double scale = gDefaultScale > 0 ? gDefaultScale : 1.0;
int32_t threshold = ceil(scale * scale * gScreenPixels);
// screen size acts as max threshold
return threshold < 0 || (aSize.width * aSize.height) <= threshold;
} }
void CanvasRenderingContext2D::ScheduleStableStateCallback() { void CanvasRenderingContext2D::ScheduleStableStateCallback() {
@ -1489,30 +1167,22 @@ void CanvasRenderingContext2D::RestoreClipsAndTransformToTarget() {
} }
} }
CanvasRenderingContext2D::RenderingMode CanvasRenderingContext2D::EnsureTarget( bool CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect) {
const gfx::Rect* aCoveredRect, RenderingMode aRenderingMode) {
if (AlreadyShutDown()) { if (AlreadyShutDown()) {
gfxCriticalError() << "Attempt to render into a Canvas2d after shutdown."; gfxCriticalError() << "Attempt to render into a Canvas2d after shutdown.";
SetErrorState(); SetErrorState();
return aRenderingMode; return false;
} }
// This would make no sense, so make sure we don't get ourselves in a mess if (mTarget) {
MOZ_ASSERT(mRenderingMode != RenderingMode::DefaultBackendMode); return true;
RenderingMode mode = (aRenderingMode == RenderingMode::DefaultBackendMode)
? mRenderingMode
: aRenderingMode;
if (mTarget && mode == mRenderingMode) {
return mRenderingMode;
} }
// Check that the dimensions are sane // Check that the dimensions are sane
if (mWidth > gfxPrefs::MaxCanvasSize() || if (mWidth > gfxPrefs::MaxCanvasSize() ||
mHeight > gfxPrefs::MaxCanvasSize() || mWidth < 0 || mHeight < 0) { mHeight > gfxPrefs::MaxCanvasSize() || mWidth < 0 || mHeight < 0) {
SetErrorState(); SetErrorState();
return aRenderingMode; return false;
} }
// If the next drawing command covers the entire canvas, we can skip copying // If the next drawing command covers the entire canvas, we can skip copying
@ -1542,7 +1212,7 @@ CanvasRenderingContext2D::RenderingMode CanvasRenderingContext2D::EnsureTarget(
IntRect persistedRect = IntRect persistedRect =
canDiscardContent ? IntRect() : IntRect(0, 0, mWidth, mHeight); canDiscardContent ? IntRect() : IntRect(0, 0, mWidth, mHeight);
if (mBufferProvider && mode == mRenderingMode) { if (mBufferProvider) {
mTarget = mBufferProvider->BorrowDrawTarget(persistedRect); mTarget = mBufferProvider->BorrowDrawTarget(persistedRect);
if (mTarget && !mBufferProvider->PreservesDrawingState()) { if (mTarget && !mBufferProvider->PreservesDrawingState()) {
@ -1550,28 +1220,21 @@ CanvasRenderingContext2D::RenderingMode CanvasRenderingContext2D::EnsureTarget(
} }
if (mTarget) { if (mTarget) {
return mode; return true;
} }
} }
RefPtr<DrawTarget> newTarget; RefPtr<DrawTarget> newTarget;
RefPtr<PersistentBufferProvider> newProvider; RefPtr<PersistentBufferProvider> newProvider;
if (mode == RenderingMode::OpenGLBackendMode && if (!TrySharedTarget(newTarget, newProvider) &&
!TrySkiaGLTarget(newTarget, newProvider)) {
// Fall back to software.
mode = RenderingMode::SoftwareBackendMode;
}
if (mode == RenderingMode::SoftwareBackendMode &&
!TrySharedTarget(newTarget, newProvider) &&
!TryBasicTarget(newTarget, newProvider)) { !TryBasicTarget(newTarget, newProvider)) {
gfxCriticalError( gfxCriticalError(
CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(GetSize()))) CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(GetSize())))
<< "Failed borrow shared and basic targets."; << "Failed borrow shared and basic targets.";
SetErrorState(); SetErrorState();
return mode; return false;
} }
MOZ_ASSERT(newTarget); MOZ_ASSERT(newTarget);
@ -1612,7 +1275,7 @@ CanvasRenderingContext2D::RenderingMode CanvasRenderingContext2D::EnsureTarget(
// canvas is already invalid, which can speed up future drawing. // canvas is already invalid, which can speed up future drawing.
Redraw(); Redraw();
return mode; return true;
} }
void CanvasRenderingContext2D::SetInitialState() { void CanvasRenderingContext2D::SetInitialState() {
@ -1672,57 +1335,6 @@ static already_AddRefed<LayerManager> LayerManagerFromCanvasElement(
aCanvasElement->OwnerDoc()); aCanvasElement->OwnerDoc());
} }
bool CanvasRenderingContext2D::TrySkiaGLTarget(
RefPtr<gfx::DrawTarget>& aOutDT,
RefPtr<layers::PersistentBufferProvider>& aOutProvider) {
aOutDT = nullptr;
aOutProvider = nullptr;
mIsSkiaGL = false;
IntSize size(mWidth, mHeight);
if (!AllowOpenGLCanvas() || !CheckSizeForSkiaGL(size)) {
return false;
}
RefPtr<LayerManager> layerManager =
LayerManagerFromCanvasElement(mCanvasElement);
if (!layerManager) {
return false;
}
DemoteOldestContextIfNecessary();
mBufferProvider = nullptr;
#ifdef USE_SKIA_GPU
SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
if (!glue || !glue->GetGrContext() || !glue->GetGLContext()) {
return false;
}
SurfaceFormat format = GetSurfaceFormat();
aOutDT = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(),
size, format);
if (!aOutDT) {
gfxCriticalNote
<< "Failed to create a SkiaGL DrawTarget, falling back to software\n";
return false;
}
MOZ_ASSERT(aOutDT->GetType() == DrawTargetType::HARDWARE_RASTER);
AddDemotableContext(this);
aOutProvider = new PersistentBufferProviderBasic(aOutDT);
mIsSkiaGL = true;
// Drop a note in the debug builds if we ever use accelerated Skia canvas.
gfxWarningOnce() << "Using SkiaGL canvas.";
#endif
// could still be null if USE_SKIA_GPU is not #defined.
return !!aOutDT;
}
bool CanvasRenderingContext2D::TrySharedTarget( bool CanvasRenderingContext2D::TrySharedTarget(
RefPtr<gfx::DrawTarget>& aOutDT, RefPtr<gfx::DrawTarget>& aOutDT,
RefPtr<layers::PersistentBufferProvider>& aOutProvider) { RefPtr<layers::PersistentBufferProvider>& aOutProvider) {
@ -1912,16 +1524,6 @@ CanvasRenderingContext2D::SetContextOptions(JSContext* aCx,
return NS_ERROR_UNEXPECTED; return NS_ERROR_UNEXPECTED;
} }
if (Preferences::GetBool("gfx.canvas.willReadFrequently.enable", false)) {
// Use software when there is going to be a lot of readback
if (attributes.mWillReadFrequently) {
// We want to lock into software, so remove the observer that
// may potentially change that...
RemoveDrawObserver();
mRenderingMode = RenderingMode::SoftwareBackendMode;
}
}
mContextAttributesHasAlpha = attributes.mAlpha; mContextAttributesHasAlpha = attributes.mAlpha;
UpdateIsOpaque(); UpdateIsOpaque();
@ -4537,7 +4139,7 @@ CanvasRenderingContext2D::CachedSurfaceFromElement(Element* aElement) {
return res; return res;
} }
res.mSourceSurface = CanvasImageCache::LookupAllCanvas(aElement, mIsSkiaGL); res.mSourceSurface = CanvasImageCache::LookupAllCanvas(aElement);
if (!res.mSourceSurface) { if (!res.mSourceSurface) {
return res; return res;
} }
@ -4576,10 +4178,6 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
double aDw, double aDh, double aDw, double aDh,
uint8_t aOptional_argc, uint8_t aOptional_argc,
ErrorResult& aError) { ErrorResult& aError) {
if (mDrawObserver) {
mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::DrawImage);
}
MOZ_ASSERT(aOptional_argc == 0 || aOptional_argc == 2 || aOptional_argc == 6); MOZ_ASSERT(aOptional_argc == 0 || aOptional_argc == 2 || aOptional_argc == 6);
if (!ValidateRect(aDx, aDy, aDw, aDh, true)) { if (!ValidateRect(aDx, aDy, aDw, aDh, true)) {
@ -4640,8 +4238,7 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
element = video; element = video;
} }
srcSurf = CanvasImageCache::LookupCanvas(element, mCanvasElement, &imgSize, srcSurf = CanvasImageCache::LookupCanvas(element, mCanvasElement, &imgSize);
mIsSkiaGL);
} }
nsLayoutUtils::DirectDrawInfo drawInfo; nsLayoutUtils::DirectDrawInfo drawInfo;
@ -4692,7 +4289,7 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
if (res.mSourceSurface) { if (res.mSourceSurface) {
if (res.mImageRequest) { if (res.mImageRequest) {
CanvasImageCache::NotifyDrawImage( CanvasImageCache::NotifyDrawImage(
element, mCanvasElement, res.mSourceSurface, imgSize, mIsSkiaGL); element, mCanvasElement, res.mSourceSurface, imgSize);
} }
srcSurf = res.mSourceSurface; srcSurf = res.mSourceSurface;
} else { } else {
@ -5118,10 +4715,6 @@ void CanvasRenderingContext2D::DrawWindow(nsGlobalWindowInner& aWindow,
already_AddRefed<ImageData> CanvasRenderingContext2D::GetImageData( already_AddRefed<ImageData> CanvasRenderingContext2D::GetImageData(
JSContext* aCx, double aSx, double aSy, double aSw, double aSh, JSContext* aCx, double aSx, double aSy, double aSw, double aSh,
nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) { nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
if (mDrawObserver) {
mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::GetImageData);
}
if (!mCanvasElement && !mDocShell) { if (!mCanvasElement && !mDocShell) {
NS_ERROR("No canvas element and no docshell in GetImageData!!!"); NS_ERROR("No canvas element and no docshell in GetImageData!!!");
aError.Throw(NS_ERROR_DOM_SECURITY_ERR); aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
@ -5190,10 +4783,6 @@ already_AddRefed<ImageData> CanvasRenderingContext2D::GetImageData(
nsresult CanvasRenderingContext2D::GetImageDataArray( nsresult CanvasRenderingContext2D::GetImageDataArray(
JSContext* aCx, int32_t aX, int32_t aY, uint32_t aWidth, uint32_t aHeight, JSContext* aCx, int32_t aX, int32_t aY, uint32_t aWidth, uint32_t aHeight,
nsIPrincipal& aSubjectPrincipal, JSObject** aRetval) { nsIPrincipal& aSubjectPrincipal, JSObject** aRetval) {
if (mDrawObserver) {
mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::GetImageData);
}
MOZ_ASSERT(aWidth && aHeight); MOZ_ASSERT(aWidth && aHeight);
CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4; CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
@ -5349,10 +4938,6 @@ nsresult CanvasRenderingContext2D::PutImageData_explicit(
int32_t aX, int32_t aY, uint32_t aW, uint32_t aH, int32_t aX, int32_t aY, uint32_t aW, uint32_t aH,
dom::Uint8ClampedArray* aArray, bool aHasDirtyRect, int32_t aDirtyX, dom::Uint8ClampedArray* aArray, bool aHasDirtyRect, int32_t aDirtyX,
int32_t aDirtyY, int32_t aDirtyWidth, int32_t aDirtyHeight) { int32_t aDirtyY, int32_t aDirtyWidth, int32_t aDirtyHeight) {
if (mDrawObserver) {
mDrawObserver->DidDrawCall(CanvasDrawObserver::DrawCallType::PutImageData);
}
if (aW == 0 || aH == 0) { if (aW == 0 || aH == 0) {
return NS_ERROR_DOM_INVALID_STATE_ERR; return NS_ERROR_DOM_INVALID_STATE_ERR;
} }
@ -5520,29 +5105,11 @@ already_AddRefed<ImageData> CanvasRenderingContext2D::CreateImageData(
static uint8_t g2DContextLayerUserData; static uint8_t g2DContextLayerUserData;
uint32_t CanvasRenderingContext2D::SkiaGLTex() const {
if (!mTarget) {
return 0;
}
MOZ_ASSERT(IsTargetValid());
return (uint32_t)(uintptr_t)mTarget->GetNativeSurface(
NativeSurfaceType::OPENGL_TEXTURE);
}
void CanvasRenderingContext2D::RemoveDrawObserver() {
if (mDrawObserver) {
delete mDrawObserver;
mDrawObserver = nullptr;
}
}
already_AddRefed<Layer> CanvasRenderingContext2D::GetCanvasLayer( already_AddRefed<Layer> CanvasRenderingContext2D::GetCanvasLayer(
nsDisplayListBuilder* aBuilder, Layer* aOldLayer, LayerManager* aManager) { nsDisplayListBuilder* aBuilder, Layer* aOldLayer, LayerManager* aManager) {
if (mOpaque || mIsSkiaGL) { if (mOpaque) {
// If we're opaque then make sure we have a surface so we paint black // If we're opaque then make sure we have a surface so we paint black
// instead of transparent. // instead of transparent.
// If we're using SkiaGL, then SkiaGLTex() below needs the target to
// be accessible.
EnsureTarget(); EnsureTarget();
} }
@ -5563,16 +5130,6 @@ already_AddRefed<Layer> CanvasRenderingContext2D::GetCanvasLayer(
CanvasInitializeData data; CanvasInitializeData data;
if (mIsSkiaGL) {
GLuint skiaGLTex = SkiaGLTex();
if (skiaGLTex) {
SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
MOZ_ASSERT(glue);
data.mGLContext = glue->GetGLContext();
data.mFrontbufferGLTex = skiaGLTex;
}
}
data.mBufferProvider = mBufferProvider; data.mBufferProvider = mBufferProvider;
if (userData && userData->IsForContext(this) && if (userData && userData->IsForContext(this) &&
@ -5619,11 +5176,9 @@ already_AddRefed<Layer> CanvasRenderingContext2D::GetCanvasLayer(
bool CanvasRenderingContext2D::UpdateWebRenderCanvasData( bool CanvasRenderingContext2D::UpdateWebRenderCanvasData(
nsDisplayListBuilder* aBuilder, WebRenderCanvasData* aCanvasData) { nsDisplayListBuilder* aBuilder, WebRenderCanvasData* aCanvasData) {
if (mOpaque || mIsSkiaGL) { if (mOpaque) {
// If we're opaque then make sure we have a surface so we paint black // If we're opaque then make sure we have a surface so we paint black
// instead of transparent. // instead of transparent.
// If we're using SkiaGL, then SkiaGLTex() below needs the target to
// be accessible.
EnsureTarget(); EnsureTarget();
} }
@ -5645,15 +5200,6 @@ bool CanvasRenderingContext2D::UpdateWebRenderCanvasData(
if (!mResetLayer && renderer) { if (!mResetLayer && renderer) {
CanvasInitializeData data; CanvasInitializeData data;
if (mIsSkiaGL) {
GLuint skiaGLTex = SkiaGLTex();
if (skiaGLTex) {
SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
MOZ_ASSERT(glue);
data.mGLContext = glue->GetGLContext();
data.mFrontbufferGLTex = skiaGLTex;
}
}
data.mBufferProvider = mBufferProvider; data.mBufferProvider = mBufferProvider;
if (renderer->IsDataValid(data)) { if (renderer->IsDataValid(data)) {
@ -5695,16 +5241,6 @@ bool CanvasRenderingContext2D::InitializeCanvasRenderer(
} }
} }
if (mIsSkiaGL) {
GLuint skiaGLTex = SkiaGLTex();
if (skiaGLTex) {
SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
MOZ_ASSERT(glue);
data.mGLContext = glue->GetGLContext();
data.mFrontbufferGLTex = skiaGLTex;
}
}
data.mBufferProvider = mBufferProvider; data.mBufferProvider = mBufferProvider;
aRenderer->Initialize(data); aRenderer->Initialize(data);

View File

@ -376,14 +376,6 @@ class CanvasRenderingContext2D final : public nsICanvasRenderingContextInternal,
double aH, const nsAString& aBgColor, uint32_t aFlags, double aH, const nsAString& aBgColor, uint32_t aFlags,
mozilla::ErrorResult& aError); mozilla::ErrorResult& aError);
enum RenderingMode {
SoftwareBackendMode,
OpenGLBackendMode,
DefaultBackendMode
};
bool SwitchRenderingMode(RenderingMode aRenderingMode);
// Eventually this should be deprecated. Keeping for now to keep the binding // Eventually this should be deprecated. Keeping for now to keep the binding
// functional. // functional.
void Demote(); void Demote();
@ -509,9 +501,6 @@ class CanvasRenderingContext2D final : public nsICanvasRenderingContextInternal,
void OnShutdown(); void OnShutdown();
// Check the global setup, as well as the compositor type:
bool AllowOpenGLCanvas() const;
/** /**
* Update CurrentState().filter with the filter description for * Update CurrentState().filter with the filter description for
* CurrentState().filterChain. * CurrentState().filterChain.
@ -616,17 +605,12 @@ class CanvasRenderingContext2D final : public nsICanvasRenderingContextInternal,
* is in turn an error in creating the sErrorTarget then they would both * is in turn an error in creating the sErrorTarget then they would both
* be null so IsTargetValid() would still return null. * be null so IsTargetValid() would still return null.
* *
* Returns the actual rendering mode being used by the created target. * Returns true on success.
*/ */
RenderingMode EnsureTarget( bool EnsureTarget(const gfx::Rect* aCoveredRect = nullptr);
const gfx::Rect* aCoveredRect = nullptr,
RenderingMode aRenderMode = RenderingMode::DefaultBackendMode);
void RestoreClipsAndTransformToTarget(); void RestoreClipsAndTransformToTarget();
bool TrySkiaGLTarget(RefPtr<gfx::DrawTarget>& aOutDT,
RefPtr<layers::PersistentBufferProvider>& aOutProvider);
bool TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT, bool TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT,
RefPtr<layers::PersistentBufferProvider>& aOutProvider); RefPtr<layers::PersistentBufferProvider>& aOutProvider);
@ -704,19 +688,6 @@ class CanvasRenderingContext2D final : public nsICanvasRenderingContextInternal,
return CurrentState().font; 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();
static void AddDemotableContext(CanvasRenderingContext2D* aContext);
static void RemoveDemotableContext(CanvasRenderingContext2D* aContext);
RenderingMode mRenderingMode;
layers::LayersBackend mCompositorBackend;
// Member vars // Member vars
int32_t mWidth, mHeight; int32_t mWidth, mHeight;
@ -742,9 +713,6 @@ class CanvasRenderingContext2D final : public nsICanvasRenderingContextInternal,
bool mResetLayer; bool mResetLayer;
// This is needed for drawing in drawAsyncXULElement // This is needed for drawing in drawAsyncXULElement
bool mIPC; bool mIPC;
// True if the current DrawTarget is using skia-gl, used so we can avoid
// requesting the DT from mBufferProvider to check.
bool mIsSkiaGL;
bool mHasPendingStableStateCallback; bool mHasPendingStableStateCallback;
@ -760,14 +728,6 @@ class CanvasRenderingContext2D final : public nsICanvasRenderingContextInternal,
RefPtr<mozilla::layers::PersistentBufferProvider> mBufferProvider; RefPtr<mozilla::layers::PersistentBufferProvider> mBufferProvider;
uint32_t SkiaGLTex() const;
// This observes our draw calls at the beginning of the canvas
// lifetime and switches to software or GPU mode depending on
// what it thinks is best
CanvasDrawObserver* mDrawObserver;
void RemoveDrawObserver();
RefPtr<CanvasShutdownObserver> mShutdownObserver; RefPtr<CanvasShutdownObserver> mShutdownObserver;
void RemoveShutdownObserver(); void RemoveShutdownObserver();
bool AlreadyShutDown() const { return !mShutdownObserver; } bool AlreadyShutDown() const { return !mShutdownObserver; }
@ -919,8 +879,6 @@ class CanvasRenderingContext2D final : public nsICanvasRenderingContextInternal,
const Optional<double>& aMaxWidth, const Optional<double>& aMaxWidth,
TextDrawOperation aOp, float* aWidth); TextDrawOperation aOp, float* aWidth);
bool CheckSizeForSkiaGL(mozilla::gfx::IntSize aSize);
// A clip or a transform, recorded and restored in order. // A clip or a transform, recorded and restored in order.
struct ClipState { struct ClipState {
explicit ClipState(mozilla::gfx::Path* aClip) : clip(aClip) {} explicit ClipState(mozilla::gfx::Path* aClip) : clip(aClip) {}

View File

@ -16,6 +16,7 @@
#include "mozilla/Telemetry.h" #include "mozilla/Telemetry.h"
#include "CanvasRenderingContext2D.h" #include "CanvasRenderingContext2D.h"
#include "CanvasUtils.h" #include "CanvasUtils.h"
#include "GLContext.h"
#include "GLScreenBuffer.h" #include "GLScreenBuffer.h"
#include "WebGL1Context.h" #include "WebGL1Context.h"
#include "WebGL2Context.h" #include "WebGL2Context.h"

View File

@ -43,17 +43,6 @@ function IsAzureSkia() {
return enabled; return enabled;
} }
function IsAcceleratedSkia() {
var enabled = false;
try {
var props = Cc["@mozilla.org/gfx/info;1"].getService(SpecialPowers.Ci.nsIGfxInfo).getInfo();
enabled = props.AzureCanvasBackend == "skia" && props.AzureCanvasAccelerated;
} catch(e) { }
return enabled;
}
function IsAzureCairo() { function IsAzureCairo() {
var enabled = false; var enabled = false;
@ -6585,9 +6574,6 @@ isPixel(ctx, 98,48, 0,255,0,255, 0);
function test_2d_gradient_radial_inside1() { function test_2d_gradient_radial_inside1() {
if (IsAcceleratedSkia())
return;
var canvas = document.getElementById('c240'); var canvas = document.getElementById('c240');
var ctx = canvas.getContext('2d'); var ctx = canvas.getContext('2d');

View File

@ -20,12 +20,31 @@
#include "mozilla/dom/TimeRanges.h" #include "mozilla/dom/TimeRanges.h"
#include "AudioStream.h" #include "AudioStream.h"
NS_IMPL_NS_NEW_HTML_ELEMENT(Audio) nsGenericHTMLElement* NS_NewHTMLAudioElement(
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
mozilla::dom::FromParser aFromParser) {
mozilla::dom::HTMLAudioElement* element =
new mozilla::dom::HTMLAudioElement(std::move(aNodeInfo));
element->Init();
return element;
}
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
NS_IMPL_ELEMENT_CLONE(HTMLAudioElement) nsresult HTMLAudioElement::Clone(mozilla::dom::NodeInfo* aNodeInfo,
nsINode** aResult) const {
*aResult = nullptr;
RefPtr<mozilla::dom::NodeInfo> ni(aNodeInfo);
HTMLAudioElement* it = new HTMLAudioElement(ni.forget());
it->Init();
nsCOMPtr<nsINode> kungFuDeathGrip = it;
nsresult rv = const_cast<HTMLAudioElement*>(this)->CopyInnerTo(it);
if (NS_SUCCEEDED(rv)) {
kungFuDeathGrip.swap(*aResult);
}
return rv;
}
HTMLAudioElement::HTMLAudioElement(already_AddRefed<NodeInfo>&& aNodeInfo) HTMLAudioElement::HTMLAudioElement(already_AddRefed<NodeInfo>&& aNodeInfo)
: HTMLMediaElement(std::move(aNodeInfo)) { : HTMLMediaElement(std::move(aNodeInfo)) {
@ -54,7 +73,8 @@ already_AddRefed<HTMLAudioElement> HTMLAudioElement::Audio(
RefPtr<mozilla::dom::NodeInfo> nodeInfo = doc->NodeInfoManager()->GetNodeInfo( RefPtr<mozilla::dom::NodeInfo> nodeInfo = doc->NodeInfoManager()->GetNodeInfo(
nsGkAtoms::audio, nullptr, kNameSpaceID_XHTML, ELEMENT_NODE); nsGkAtoms::audio, nullptr, kNameSpaceID_XHTML, ELEMENT_NODE);
RefPtr<HTMLAudioElement> audio = new HTMLAudioElement(nodeInfo.forget()); RefPtr<HTMLAudioElement> audio =
static_cast<HTMLAudioElement*>(NS_NewHTMLAudioElement(nodeInfo.forget()));
audio->SetHTMLAttr(nsGkAtoms::preload, NS_LITERAL_STRING("auto"), aRv); audio->SetHTMLAttr(nsGkAtoms::preload, NS_LITERAL_STRING("auto"), aRv);
if (aRv.Failed()) { if (aRv.Failed()) {
return nullptr; return nullptr;

View File

@ -3499,13 +3499,27 @@ HTMLMediaElement::HTMLMediaElement(
mShutdownObserver(new ShutdownObserver), mShutdownObserver(new ShutdownObserver),
mPlayed(new TimeRanges(ToSupports(OwnerDoc()))), mPlayed(new TimeRanges(ToSupports(OwnerDoc()))),
mPaused(true, "HTMLMediaElement::mPaused"), mPaused(true, "HTMLMediaElement::mPaused"),
mAudioTrackList(new AudioTrackList(OwnerDoc()->GetParentObject(), this)),
mVideoTrackList(new VideoTrackList(OwnerDoc()->GetParentObject(), this)),
mErrorSink(new ErrorSink(this)), mErrorSink(new ErrorSink(this)),
mAudioChannelWrapper(new AudioChannelAgentCallback(this)), mAudioChannelWrapper(new AudioChannelAgentCallback(this)),
mSink(MakePair(nsString(), RefPtr<AudioDeviceInfo>())) { mSink(MakePair(nsString(), RefPtr<AudioDeviceInfo>())) {
MOZ_ASSERT(mMainThreadEventTarget); MOZ_ASSERT(mMainThreadEventTarget);
MOZ_ASSERT(mAbstractMainThread); MOZ_ASSERT(mAbstractMainThread);
// Please don't add anything to this constructor or the initialization
// list that can cause AddRef to be called. This prevents subclasses
// from overriding AddRef in a way that works with our refcount
// logging mechanisms. Put these things inside of the ::Init method
// instead.
}
void HTMLMediaElement::Init() {
MOZ_ASSERT(mRefCnt == 0 && !mRefCnt.IsPurple(),
"HTMLMediaElement::Init called when AddRef has been called "
"at least once already, probably in the constructor. Please "
"see the documentation in the HTMLMediaElement constructor.");
MOZ_ASSERT(!mRefCnt.IsPurple());
mAudioTrackList = new AudioTrackList(OwnerDoc()->GetParentObject(), this);
mVideoTrackList = new VideoTrackList(OwnerDoc()->GetParentObject(), this);
DecoderDoctorLogger::LogConstruction(this); DecoderDoctorLogger::LogConstruction(this);
@ -3526,9 +3540,12 @@ HTMLMediaElement::HTMLMediaElement(
MediaShutdownManager::InitStatics(); MediaShutdownManager::InitStatics();
mShutdownObserver->Subscribe(this); mShutdownObserver->Subscribe(this);
mInitialized = true;
} }
HTMLMediaElement::~HTMLMediaElement() { HTMLMediaElement::~HTMLMediaElement() {
MOZ_ASSERT(mInitialized,
"HTMLMediaElement must be initialized before it is destroyed.");
NS_ASSERTION( NS_ASSERTION(
!mHasSelfReference, !mHasSelfReference,
"How can we be destroyed if we're still holding a self reference?"); "How can we be destroyed if we're still holding a self reference?");

View File

@ -117,6 +117,7 @@ class HTMLMediaElement : public nsGenericHTMLElement,
explicit HTMLMediaElement( explicit HTMLMediaElement(
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
void Init();
void ReportCanPlayTelemetry(); void ReportCanPlayTelemetry();
@ -1750,6 +1751,9 @@ class HTMLMediaElement : public nsGenericHTMLElement,
// threshold. // threshold.
void ReportPlayedTimeAfterBlockedTelemetry(); void ReportPlayedTimeAfterBlockedTelemetry();
// True if Init() has been called after construction
bool mInitialized = false;
// True if user has called load(), seek() or element has started playing // True if user has called load(), seek() or element has started playing
// before. It's *only* use for checking autoplay policy // before. It's *only* use for checking autoplay policy
bool mIsBlessed = false; bool mIsBlessed = false;

View File

@ -35,14 +35,33 @@
#include <algorithm> #include <algorithm>
#include <limits> #include <limits>
NS_IMPL_NS_NEW_HTML_ELEMENT(Video) nsGenericHTMLElement* NS_NewHTMLVideoElement(
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
mozilla::dom::FromParser aFromParser) {
mozilla::dom::HTMLVideoElement* element =
new mozilla::dom::HTMLVideoElement(std::move(aNodeInfo));
element->Init();
return element;
}
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
static bool sVideoStatsEnabled; static bool sVideoStatsEnabled;
NS_IMPL_ELEMENT_CLONE(HTMLVideoElement) nsresult HTMLVideoElement::Clone(mozilla::dom::NodeInfo* aNodeInfo,
nsINode** aResult) const {
*aResult = nullptr;
RefPtr<mozilla::dom::NodeInfo> ni(aNodeInfo);
HTMLVideoElement* it = new HTMLVideoElement(ni.forget());
it->Init();
nsCOMPtr<nsINode> kungFuDeathGrip = it;
nsresult rv = const_cast<HTMLVideoElement*>(this)->CopyInnerTo(it);
if (NS_SUCCEEDED(rv)) {
kungFuDeathGrip.swap(*aResult);
}
return rv;
}
HTMLVideoElement::HTMLVideoElement(already_AddRefed<NodeInfo>&& aNodeInfo) HTMLVideoElement::HTMLVideoElement(already_AddRefed<NodeInfo>&& aNodeInfo)
: HTMLMediaElement(std::move(aNodeInfo)), mIsOrientationLocked(false) { : HTMLMediaElement(std::move(aNodeInfo)), mIsOrientationLocked(false) {
@ -308,7 +327,8 @@ void HTMLVideoElement::ReleaseVideoWakeLockIfExists() {
} }
} }
void HTMLVideoElement::Init() { /* static */
void HTMLVideoElement::InitStatics() {
Preferences::AddBoolVarCache(&sVideoStatsEnabled, Preferences::AddBoolVarCache(&sVideoStatsEnabled,
"media.video_stats.enabled"); "media.video_stats.enabled");
} }

View File

@ -38,7 +38,7 @@ class HTMLVideoElement final : public HTMLMediaElement {
nsAttrValue& aResult) override; nsAttrValue& aResult) override;
NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override; NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
static void Init(); static void InitStatics();
virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction()
const override; const override;

View File

@ -1636,6 +1636,7 @@ void nsTextEditorState::SetSelectionRange(
props.SetEnd(aEnd); props.SetEnd(aEnd);
props.SetDirection(aDirection); props.SetDirection(aDirection);
} else { } else {
MOZ_ASSERT(mBoundFrame, "Our frame should still be valid");
WeakPtr<nsTextEditorState> self(this); WeakPtr<nsTextEditorState> self(this);
aRv = mBoundFrame->SetSelectionRange(aStart, aEnd, aDirection); aRv = mBoundFrame->SetSelectionRange(aStart, aEnd, aDirection);
if (aRv.Failed() || !self.get()) { if (aRv.Failed() || !self.get()) {
@ -2461,9 +2462,8 @@ bool nsTextEditorState::SetValue(const nsAString& aValue,
// TODO(emilio): It seems wrong to pass ValueChangeKind::Script if // TODO(emilio): It seems wrong to pass ValueChangeKind::Script if
// BySetUserInput is in aFlags. // BySetUserInput is in aFlags.
auto changeKind = (aFlags & eSetValue_Internal) auto changeKind = (aFlags & eSetValue_Internal) ? ValueChangeKind::Internal
? ValueChangeKind::Internal : ValueChangeKind::Script;
: ValueChangeKind::Script;
// XXX Should we stop notifying "value changed" if mTextCtrlElement has // XXX Should we stop notifying "value changed" if mTextCtrlElement has
// been cleared? // been cleared?

View File

@ -1273,7 +1273,8 @@ void ContentChild::InitXPCOM(
mozilla::ipc::IPCResult ContentChild::RecvRequestMemoryReport( mozilla::ipc::IPCResult ContentChild::RecvRequestMemoryReport(
const uint32_t& aGeneration, const bool& aAnonymize, const uint32_t& aGeneration, const bool& aAnonymize,
const bool& aMinimizeMemoryUsage, const MaybeFileDesc& aDMDFile) { const bool& aMinimizeMemoryUsage,
const Maybe<mozilla::ipc::FileDescriptor>& aDMDFile) {
nsCString process; nsCString process;
GetProcessName(process); GetProcessName(process);
AppendProcessId(process); AppendProcessId(process);
@ -1611,7 +1612,7 @@ static bool StartMacOSContentSandbox() {
#endif #endif
mozilla::ipc::IPCResult ContentChild::RecvSetProcessSandbox( mozilla::ipc::IPCResult ContentChild::RecvSetProcessSandbox(
const MaybeFileDesc& aBroker) { const Maybe<mozilla::ipc::FileDescriptor>& aBroker) {
// We may want to move the sandbox initialization somewhere else // We may want to move the sandbox initialization somewhere else
// at some point; see bug 880808. // at some point; see bug 880808.
#if defined(MOZ_CONTENT_SANDBOX) #if defined(MOZ_CONTENT_SANDBOX)
@ -3918,4 +3919,4 @@ nsresult GetObjDir(nsIFile** aObjDir) {
} }
#endif /* XP_MACOSX */ #endif /* XP_MACOSX */
} // namespace mozilla } // namespace mozilla

View File

@ -188,12 +188,12 @@ class ContentChild final : public PContentChild,
Endpoint<PVideoDecoderManagerChild>&& aVideoManager, Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
nsTArray<uint32_t>&& namespaces); nsTArray<uint32_t>&& namespaces);
virtual mozilla::ipc::IPCResult RecvAudioDefaultDeviceChange(); mozilla::ipc::IPCResult RecvAudioDefaultDeviceChange();
mozilla::ipc::IPCResult RecvReinitRenderingForDeviceReset(); mozilla::ipc::IPCResult RecvReinitRenderingForDeviceReset();
virtual mozilla::ipc::IPCResult RecvSetProcessSandbox( mozilla::ipc::IPCResult RecvSetProcessSandbox(
const MaybeFileDesc& aBroker); const Maybe<FileDescriptor>& aBroker);
PBrowserChild* AllocPBrowserChild(const TabId& aTabId, PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
const TabId& aSameTabGroupAs, const TabId& aSameTabGroupAs,
@ -361,7 +361,7 @@ class ContentChild final : public PContentChild,
mozilla::ipc::IPCResult RecvNotifyAlertsObserver(const nsCString& aType, mozilla::ipc::IPCResult RecvNotifyAlertsObserver(const nsCString& aType,
const nsString& aData); const nsString& aData);
virtual mozilla::ipc::IPCResult RecvLoadProcessScript(const nsString& aURL); mozilla::ipc::IPCResult RecvLoadProcessScript(const nsString& aURL);
mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& aMsg, mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& aMsg,
InfallibleTArray<CpowEntry>&& aCpows, InfallibleTArray<CpowEntry>&& aCpows,
@ -576,7 +576,7 @@ class ContentChild final : public PContentChild,
mozilla::ipc::IPCResult RecvRequestMemoryReport( mozilla::ipc::IPCResult RecvRequestMemoryReport(
const uint32_t& generation, const bool& anonymize, const uint32_t& generation, const bool& anonymize,
const bool& minimizeMemoryUsage, const MaybeFileDesc& DMDFile); const bool& minimizeMemoryUsage, const Maybe<FileDescriptor>& DMDFile);
mozilla::ipc::IPCResult RecvSetXPCOMProcessAttributes( mozilla::ipc::IPCResult RecvSetXPCOMProcessAttributes(
const XPCOMInitData& aXPCOMInit, const StructuredCloneData& aInitialData, const XPCOMInitData& aXPCOMInit, const StructuredCloneData& aInitialData,
@ -822,4 +822,4 @@ uint64_t NextWindowID();
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla
#endif // mozilla_dom_ContentChild_h #endif // mozilla_dom_ContentChild_h

View File

@ -2568,7 +2568,7 @@ void ContentParent::InitInternal(ProcessPriority aInitialPriority) {
#ifdef MOZ_CONTENT_SANDBOX #ifdef MOZ_CONTENT_SANDBOX
bool shouldSandbox = true; bool shouldSandbox = true;
MaybeFileDesc brokerFd = void_t(); Maybe<FileDescriptor> brokerFd;
// XXX: Checking the pref here makes it possible to enable/disable sandboxing // XXX: Checking the pref here makes it possible to enable/disable sandboxing
// during an active session. Currently the pref is only used for testing // during an active session. Currently the pref is only used for testing
// purpose. If the decision is made to permanently rely on the pref, this // purpose. If the decision is made to permanently rely on the pref, this
@ -2583,14 +2583,14 @@ void ContentParent::InitInternal(ProcessPriority aInitialPriority) {
UniquePtr<SandboxBroker::Policy> policy = UniquePtr<SandboxBroker::Policy> policy =
sSandboxBrokerPolicyFactory->GetContentPolicy(Pid(), isFileProcess); sSandboxBrokerPolicyFactory->GetContentPolicy(Pid(), isFileProcess);
if (policy) { if (policy) {
brokerFd = FileDescriptor(); brokerFd = Some(FileDescriptor());
mSandboxBroker = mSandboxBroker =
SandboxBroker::Create(std::move(policy), Pid(), brokerFd); SandboxBroker::Create(std::move(policy), Pid(), brokerFd.ref());
if (!mSandboxBroker) { if (!mSandboxBroker) {
KillHard("SandboxBroker::Create failed"); KillHard("SandboxBroker::Create failed");
return; return;
} }
MOZ_ASSERT(static_cast<const FileDescriptor&>(brokerFd).IsValid()); MOZ_ASSERT(brokerFd.ref().IsValid());
} }
} }
# endif # endif
@ -3455,10 +3455,9 @@ bool ContentParent::DeallocPHeapSnapshotTempFileHelperParent(
return true; return true;
} }
bool ContentParent::SendRequestMemoryReport(const uint32_t& aGeneration, bool ContentParent::SendRequestMemoryReport(
const bool& aAnonymize, const uint32_t& aGeneration, const bool& aAnonymize,
const bool& aMinimizeMemoryUsage, const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile) {
const MaybeFileDesc& aDMDFile) {
// This automatically cancels the previous request. // This automatically cancels the previous request.
mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration); mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration);
Unused << PContentParent::SendRequestMemoryReport( Unused << PContentParent::SendRequestMemoryReport(
@ -5923,4 +5922,4 @@ ParentIdleListener::Observe(nsISupports*, const char* aTopic,
mozilla::Unused << mParent->SendNotifyIdleObserver( mozilla::Unused << mParent->SendNotifyIdleObserver(
mObserver, nsDependentCString(aTopic), nsDependentString(aData)); mObserver, nsDependentCString(aTopic), nsDependentString(aData));
return NS_OK; return NS_OK;
} }

View File

@ -1170,7 +1170,7 @@ class ContentParent final : public PContentParent,
bool SendRequestMemoryReport(const uint32_t& aGeneration, bool SendRequestMemoryReport(const uint32_t& aGeneration,
const bool& aAnonymize, const bool& aAnonymize,
const bool& aMinimizeMemoryUsage, const bool& aMinimizeMemoryUsage,
const MaybeFileDesc& aDMDFile) override; const Maybe<FileDescriptor>& aDMDFile) override;
nsresult SaveRecording(nsIFile* aFile, bool* aRetval); nsresult SaveRecording(nsIFile* aFile, bool* aRetval);

View File

@ -50,13 +50,11 @@ MemoryReportRequestHost::~MemoryReportRequestHost() {
NS_IMPL_ISUPPORTS(MemoryReportRequestClient, nsIRunnable) NS_IMPL_ISUPPORTS(MemoryReportRequestClient, nsIRunnable)
/* static */ /* static */ void MemoryReportRequestClient::Start(
void MemoryReportRequestClient::Start(uint32_t aGeneration, bool aAnonymize, uint32_t aGeneration, bool aAnonymize, bool aMinimizeMemoryUsage,
bool aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile, const nsACString& aProcessString,
const MaybeFileDesc& aDMDFile, const ReportCallback& aReportCallback,
const nsACString& aProcessString, const FinishCallback& aFinishCallback) {
const ReportCallback& aReportCallback,
const FinishCallback& aFinishCallback) {
RefPtr<MemoryReportRequestClient> request = new MemoryReportRequestClient( RefPtr<MemoryReportRequestClient> request = new MemoryReportRequestClient(
aGeneration, aAnonymize, aDMDFile, aProcessString, aReportCallback, aGeneration, aAnonymize, aDMDFile, aProcessString, aReportCallback,
aFinishCallback); aFinishCallback);
@ -75,16 +73,17 @@ void MemoryReportRequestClient::Start(uint32_t aGeneration, bool aAnonymize,
} }
MemoryReportRequestClient::MemoryReportRequestClient( MemoryReportRequestClient::MemoryReportRequestClient(
uint32_t aGeneration, bool aAnonymize, const MaybeFileDesc& aDMDFile, uint32_t aGeneration, bool aAnonymize,
const nsACString& aProcessString, const ReportCallback& aReportCallback, const Maybe<FileDescriptor>& aDMDFile, const nsACString& aProcessString,
const ReportCallback& aReportCallback,
const FinishCallback& aFinishCallback) const FinishCallback& aFinishCallback)
: mGeneration(aGeneration), : mGeneration(aGeneration),
mAnonymize(aAnonymize), mAnonymize(aAnonymize),
mProcessString(aProcessString), mProcessString(aProcessString),
mReportCallback(aReportCallback), mReportCallback(aReportCallback),
mFinishCallback(aFinishCallback) { mFinishCallback(aFinishCallback) {
if (aDMDFile.type() == MaybeFileDesc::TFileDescriptor) { if (aDMDFile.isSome()) {
mDMDFile = aDMDFile.get_FileDescriptor(); mDMDFile = aDMDFile.value();
} }
} }

View File

@ -18,7 +18,6 @@ class nsMemoryReporterManager;
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
class MaybeFileDesc;
class MemoryReport; class MemoryReport;
class MemoryReportRequestHost final { class MemoryReportRequestHost final {
@ -44,7 +43,8 @@ class MemoryReportRequestClient final : public nsIRunnable {
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
static void Start(uint32_t aGeneration, bool aAnonymize, static void Start(uint32_t aGeneration, bool aAnonymize,
bool aMinimizeMemoryUsage, const MaybeFileDesc& aDMDFile, bool aMinimizeMemoryUsage,
const Maybe<mozilla::ipc::FileDescriptor>& aDMDFile,
const nsACString& aProcessString, const nsACString& aProcessString,
const ReportCallback& aReportCallback, const ReportCallback& aReportCallback,
const FinishCallback& aFinishCallback); const FinishCallback& aFinishCallback);
@ -53,7 +53,7 @@ class MemoryReportRequestClient final : public nsIRunnable {
private: private:
MemoryReportRequestClient(uint32_t aGeneration, bool aAnonymize, MemoryReportRequestClient(uint32_t aGeneration, bool aAnonymize,
const MaybeFileDesc& aDMDFile, const Maybe<mozilla::ipc::FileDescriptor>& aDMDFile,
const nsACString& aProcessString, const nsACString& aProcessString,
const ReportCallback& aReportCallback, const ReportCallback& aReportCallback,
const FinishCallback& aFinishCallback); const FinishCallback& aFinishCallback);

View File

@ -18,10 +18,5 @@ struct MemoryReport {
nsCString desc; nsCString desc;
}; };
union MaybeFileDesc {
FileDescriptor;
void_t;
};
} }
} }

View File

@ -435,12 +435,12 @@ child:
* usually only be performed zero or one times. The child may * usually only be performed zero or one times. The child may
* abnormally exit if this fails; the details are OS-specific. * abnormally exit if this fails; the details are OS-specific.
*/ */
async SetProcessSandbox(MaybeFileDesc aBroker); async SetProcessSandbox(FileDescriptor? aBroker);
async RequestMemoryReport(uint32_t generation, async RequestMemoryReport(uint32_t generation,
bool anonymize, bool anonymize,
bool minimizeMemoryUsage, bool minimizeMemoryUsage,
MaybeFileDesc DMDFile); FileDescriptor? DMDFile);
async RequestPerformanceMetrics(nsID aID); async RequestPerformanceMetrics(nsID aID);
/** /**
@ -1294,4 +1294,4 @@ both:
}; };
} }
} }

View File

@ -5,55 +5,38 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/PermissionMessageUtils.h"
#include "nsISerializable.h" #include "mozilla/ipc/BackgroundUtils.h"
#include "nsSerializationHelper.h"
namespace IPC { namespace mozilla {
namespace ipc {
void ParamTraits<nsIPrincipal>::Write(Message* aMsg, nsIPrincipal* aParam) { void IPDLParamTraits<nsIPrincipal>::Write(IPC::Message* aMsg, IProtocol* aActor,
bool isNull = !aParam; nsIPrincipal* aParam) {
WriteParam(aMsg, isNull); MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
if (isNull) {
return; Maybe<PrincipalInfo> info;
if (aParam) {
info.emplace();
nsresult rv = PrincipalToPrincipalInfo(aParam, info.ptr());
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
} }
nsCString principalString; WriteIPDLParam(aMsg, aActor, info);
nsresult rv = NS_SerializeToString(aParam, principalString);
if (NS_FAILED(rv)) {
MOZ_CRASH("Unable to serialize principal.");
return;
}
WriteParam(aMsg, principalString);
} }
bool ParamTraits<nsIPrincipal>::Read(const Message* aMsg, PickleIterator* aIter, bool IPDLParamTraits<nsIPrincipal>::Read(const IPC::Message* aMsg,
RefPtr<nsIPrincipal>* aResult) { PickleIterator* aIter,
bool isNull; IProtocol* aActor,
if (!ReadParam(aMsg, aIter, &isNull)) { RefPtr<nsIPrincipal>* aResult) {
Maybe<PrincipalInfo> info;
if (!ReadIPDLParam(aMsg, aIter, aActor, &info)) {
return false; return false;
} }
if (isNull) { nsresult rv = NS_OK;
*aResult = nullptr; *aResult = info ? PrincipalInfoToPrincipal(info.ref(), &rv) : nullptr;
return true; return NS_SUCCEEDED(rv);
}
nsCString principalString;
if (!ReadParam(aMsg, aIter, &principalString)) {
return false;
}
nsCOMPtr<nsISupports> iSupports;
nsresult rv =
NS_DeserializeObject(principalString, getter_AddRefs(iSupports));
NS_ENSURE_SUCCESS(rv, false);
nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(iSupports);
NS_ENSURE_TRUE(principal, false);
*aResult = principal.forget();
return true;
} }
} // namespace IPC } // namespace ipc
} // namespace mozilla

View File

@ -7,24 +7,18 @@
#ifndef mozilla_dom_permission_message_utils_h__ #ifndef mozilla_dom_permission_message_utils_h__
#define mozilla_dom_permission_message_utils_h__ #define mozilla_dom_permission_message_utils_h__
#include "mozilla/ipc/IPDLParamTraits.h"
#include "ipc/IPCMessageUtils.h" #include "ipc/IPCMessageUtils.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsIPrincipal.h" #include "nsIPrincipal.h"
namespace IPC { namespace IPC {
template <>
struct ParamTraits<nsIPrincipal> {
static void Write(Message* aMsg, nsIPrincipal* aParam);
static bool Read(const Message* aMsg, PickleIterator* aIter,
RefPtr<nsIPrincipal>* aResult);
};
/** /**
* Legacy IPC::Principal type. Use nsIPrincipal directly in new IPDL code. * Legacy IPC::Principal type. Use nsIPrincipal directly in new IPDL code.
*/ */
class Principal { class Principal {
friend struct ParamTraits<Principal>; friend struct mozilla::ipc::IPDLParamTraits<Principal>;
public: public:
Principal() : mPrincipal(nullptr) {} Principal() : mPrincipal(nullptr) {}
@ -42,18 +36,44 @@ class Principal {
RefPtr<nsIPrincipal> mPrincipal; RefPtr<nsIPrincipal> mPrincipal;
}; };
} // namespace IPC
namespace mozilla {
namespace ipc {
template <> template <>
struct ParamTraits<Principal> { struct IPDLParamTraits<nsIPrincipal> {
typedef Principal paramType; static void Write(IPC::Message* aMsg, IProtocol* aActor,
static void Write(Message* aMsg, const paramType& aParam) { nsIPrincipal* aParam);
WriteParam(aMsg, aParam.mPrincipal); static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
} IProtocol* aActor, RefPtr<nsIPrincipal>* aResult);
static bool Read(const Message* aMsg, PickleIterator* aIter,
paramType* aResult) { // Overload to support deserializing nsCOMPtr<nsIPrincipal> directly.
return ReadParam(aMsg, aIter, &aResult->mPrincipal); static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
IProtocol* aActor, nsCOMPtr<nsIPrincipal>* aResult) {
RefPtr<nsIPrincipal> result;
if (!Read(aMsg, aIter, aActor, &result)) {
return false;
}
*aResult = result.forget();
return true;
} }
}; };
} // namespace IPC template <>
struct IPDLParamTraits<IPC::Principal> {
typedef IPC::Principal paramType;
static void Write(IPC::Message* aMsg, IProtocol* aActor,
const paramType& aParam) {
WriteIPDLParam(aMsg, aActor, aParam.mPrincipal);
}
static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
IProtocol* aActor, paramType* aResult) {
return ReadIPDLParam(aMsg, aIter, aActor, &aResult->mPrincipal);
}
};
} // namespace ipc
} // namespace mozilla
#endif // mozilla_dom_permission_message_utils_h__ #endif // mozilla_dom_permission_message_utils_h__

View File

@ -7,6 +7,7 @@
#include "MediaFormatReader.h" #include "MediaFormatReader.h"
#include "AllocationPolicy.h" #include "AllocationPolicy.h"
#include "GeckoProfiler.h"
#include "MediaData.h" #include "MediaData.h"
#include "MediaInfo.h" #include "MediaInfo.h"
#include "VideoFrameContainer.h" #include "VideoFrameContainer.h"
@ -2631,6 +2632,7 @@ void MediaFormatReader::SkipVideoDemuxToNextKeyFrame(TimeUnit aTimeThreshold) {
} }
void MediaFormatReader::VideoSkipReset(uint32_t aSkipped) { void MediaFormatReader::VideoSkipReset(uint32_t aSkipped) {
PROFILER_ADD_MARKER("SkippedVideoDecode", GRAPHICS);
MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(OnTaskQueue());
// Some frames may have been output by the decoder since we initiated the // Some frames may have been output by the decoder since we initiated the

View File

@ -21,7 +21,7 @@ protocol PRDD
parent: parent:
// args TBD, sent by UI process to initiate core settings // args TBD, sent by UI process to initiate core settings
async Init(MaybeFileDesc sandboxBroker); async Init(FileDescriptor? sandboxBroker);
async InitProfiler(Endpoint<PProfilerChild> endpoint); async InitProfiler(Endpoint<PProfilerChild> endpoint);
@ -31,7 +31,7 @@ parent:
async RequestMemoryReport(uint32_t generation, async RequestMemoryReport(uint32_t generation,
bool anonymize, bool anonymize,
bool minimizeMemoryUsage, bool minimizeMemoryUsage,
MaybeFileDesc DMDFile); FileDescriptor? DMDFile);
child: child:
// args TBD, sent when init complete. Occurs once, after Init(). // args TBD, sent when init complete. Occurs once, after Init().

View File

@ -29,20 +29,20 @@ RDDChild::RDDChild(RDDProcessHost* aHost) : mHost(aHost), mRDDReady(false) {
RDDChild::~RDDChild() { MOZ_COUNT_DTOR(RDDChild); } RDDChild::~RDDChild() { MOZ_COUNT_DTOR(RDDChild); }
bool RDDChild::Init() { bool RDDChild::Init() {
MaybeFileDesc brokerFd = void_t(); Maybe<FileDescriptor> brokerFd;
#if defined(XP_LINUX) && defined(MOZ_SANDBOX) #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
auto policy = SandboxBrokerPolicyFactory::GetUtilityPolicy(OtherPid()); auto policy = SandboxBrokerPolicyFactory::GetUtilityPolicy(OtherPid());
if (policy != nullptr) { if (policy != nullptr) {
brokerFd = FileDescriptor(); brokerFd = Some(FileDescriptor());
mSandboxBroker = mSandboxBroker =
SandboxBroker::Create(std::move(policy), OtherPid(), brokerFd); SandboxBroker::Create(std::move(policy), OtherPid(), brokerFd.ref());
// This is unlikely to fail and probably indicates OS resource // This is unlikely to fail and probably indicates OS resource
// exhaustion, but we can at least try to recover. // exhaustion, but we can at least try to recover.
if (NS_WARN_IF(mSandboxBroker == nullptr)) { if (NS_WARN_IF(mSandboxBroker == nullptr)) {
return false; return false;
} }
MOZ_ASSERT(static_cast<const FileDescriptor&>(brokerFd).IsValid()); MOZ_ASSERT(brokerFd.ref().IsValid());
} }
#endif // XP_LINUX && MOZ_SANDBOX #endif // XP_LINUX && MOZ_SANDBOX
@ -85,7 +85,7 @@ mozilla::ipc::IPCResult RDDChild::RecvInitCrashReporter(
bool RDDChild::SendRequestMemoryReport(const uint32_t& aGeneration, bool RDDChild::SendRequestMemoryReport(const uint32_t& aGeneration,
const bool& aAnonymize, const bool& aAnonymize,
const bool& aMinimizeMemoryUsage, const bool& aMinimizeMemoryUsage,
const MaybeFileDesc& aDMDFile) { const Maybe<FileDescriptor>& aDMDFile) {
mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration); mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration);
Unused << PRDDChild::SendRequestMemoryReport(aGeneration, aAnonymize, Unused << PRDDChild::SendRequestMemoryReport(aGeneration, aAnonymize,
aMinimizeMemoryUsage, aDMDFile); aMinimizeMemoryUsage, aDMDFile);

View File

@ -49,7 +49,7 @@ class RDDChild final : public PRDDChild {
bool SendRequestMemoryReport(const uint32_t& aGeneration, bool SendRequestMemoryReport(const uint32_t& aGeneration,
const bool& aAnonymize, const bool& aAnonymize,
const bool& aMinimizeMemoryUsage, const bool& aMinimizeMemoryUsage,
const MaybeFileDesc& aDMDFile); const Maybe<ipc::FileDescriptor>& aDMDFile);
static void Destroy(UniquePtr<RDDChild>&& aChild); static void Destroy(UniquePtr<RDDChild>&& aChild);

View File

@ -128,15 +128,16 @@ static void StartRDDMacSandbox() {
} }
#endif #endif
mozilla::ipc::IPCResult RDDParent::RecvInit(const MaybeFileDesc& aBrokerFd) { mozilla::ipc::IPCResult RDDParent::RecvInit(
const Maybe<FileDescriptor>& aBrokerFd) {
Unused << SendInitComplete(); Unused << SendInitComplete();
#if defined(MOZ_SANDBOX) #if defined(MOZ_SANDBOX)
# if defined(XP_MACOSX) # if defined(XP_MACOSX)
StartRDDMacSandbox(); StartRDDMacSandbox();
# elif defined(XP_LINUX) # elif defined(XP_LINUX)
int fd = -1; int fd = -1;
if (aBrokerFd.type() == MaybeFileDesc::TFileDescriptor) { if (aBrokerFd.isSome()) {
fd = aBrokerFd.get_FileDescriptor().ClonePlatformHandle().release(); fd = aBrokerFd.value().ClonePlatformHandle().release();
} }
SetRemoteDataDecoderSandbox(fd); SetRemoteDataDecoderSandbox(fd);
# endif // XP_MACOSX/XP_LINUX # endif // XP_MACOSX/XP_LINUX
@ -163,7 +164,7 @@ mozilla::ipc::IPCResult RDDParent::RecvNewContentRemoteDecoderManager(
mozilla::ipc::IPCResult RDDParent::RecvRequestMemoryReport( mozilla::ipc::IPCResult RDDParent::RecvRequestMemoryReport(
const uint32_t& aGeneration, const bool& aAnonymize, const uint32_t& aGeneration, const bool& aAnonymize,
const bool& aMinimizeMemoryUsage, const MaybeFileDesc& aDMDFile) { const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile) {
nsPrintfCString processName("RDD (pid %u)", (unsigned)getpid()); nsPrintfCString processName("RDD (pid %u)", (unsigned)getpid());
mozilla::dom::MemoryReportRequestClient::Start( mozilla::dom::MemoryReportRequestClient::Start(

View File

@ -24,7 +24,7 @@ class RDDParent final : public PRDDParent {
bool Init(base::ProcessId aParentPid, const char* aParentBuildID, bool Init(base::ProcessId aParentPid, const char* aParentBuildID,
MessageLoop* aIOLoop, IPC::Channel* aChannel); MessageLoop* aIOLoop, IPC::Channel* aChannel);
mozilla::ipc::IPCResult RecvInit(const MaybeFileDesc& aBrokerFd); mozilla::ipc::IPCResult RecvInit(const Maybe<ipc::FileDescriptor>& aBrokerFd);
mozilla::ipc::IPCResult RecvInitProfiler( mozilla::ipc::IPCResult RecvInitProfiler(
Endpoint<PProfilerChild>&& aEndpoint); Endpoint<PProfilerChild>&& aEndpoint);
@ -32,7 +32,8 @@ class RDDParent final : public PRDDParent {
Endpoint<PRemoteDecoderManagerParent>&& aEndpoint); Endpoint<PRemoteDecoderManagerParent>&& aEndpoint);
mozilla::ipc::IPCResult RecvRequestMemoryReport( mozilla::ipc::IPCResult RecvRequestMemoryReport(
const uint32_t& generation, const bool& anonymize, const uint32_t& generation, const bool& anonymize,
const bool& minimizeMemoryUsage, const MaybeFileDesc& DMDFile); const bool& minimizeMemoryUsage,
const Maybe<ipc::FileDescriptor>& DMDFile);
void ActorDestroy(ActorDestroyReason aWhy) override; void ActorDestroy(ActorDestroyReason aWhy) override;

View File

@ -221,10 +221,10 @@ class RDDMemoryReporter : public MemoryReportingProcess {
bool IsAlive() const override { return !!GetChild(); } bool IsAlive() const override { return !!GetChild(); }
bool SendRequestMemoryReport(const uint32_t& aGeneration, bool SendRequestMemoryReport(
const bool& aAnonymize, const uint32_t& aGeneration, const bool& aAnonymize,
const bool& aMinimizeMemoryUsage, const bool& aMinimizeMemoryUsage,
const dom::MaybeFileDesc& aDMDFile) override { const Maybe<ipc::FileDescriptor>& aDMDFile) override {
RDDChild* child = GetChild(); RDDChild* child = GetChild();
if (!child) { if (!child) {
return false; return false;

View File

@ -10,8 +10,10 @@
# include "mmsystem.h" # include "mmsystem.h"
#endif #endif
#include "MediaQueue.h"
#include "VideoSink.h" #include "VideoSink.h"
#include "GeckoProfiler.h"
#include "MediaQueue.h"
#include "VideoUtils.h" #include "VideoUtils.h"
#include "mozilla/IntegerPrintfMacros.h" #include "mozilla/IntegerPrintfMacros.h"
@ -451,6 +453,10 @@ void VideoSink::UpdateRenderedVideoFrames() {
} }
} }
if (droppedCount > 0) {
PROFILER_ADD_MARKER("DroppedUncompositedVideoFrames", GRAPHICS);
}
if (droppedCount || sentToCompositorCount) { if (droppedCount || sentToCompositorCount) {
uint32_t totalCompositorDroppedCount = mContainer->GetDroppedImageCount(); uint32_t totalCompositorDroppedCount = mContainer->GetDroppedImageCount();
uint32_t compositorDroppedCount = uint32_t compositorDroppedCount =

View File

@ -26,7 +26,6 @@
("%s: " arg, __func__, ##__VA_ARGS__)) ("%s: " arg, __func__, ##__VA_ARGS__))
using namespace mozilla; using namespace mozilla;
using namespace mozilla::gl;
using namespace mozilla::java::sdk; using namespace mozilla::java::sdk;
using media::TimeUnit; using media::TimeUnit;

View File

@ -114,7 +114,7 @@ nsresult CreatePrincipalInfo(nsILineInputStream* aStream,
// CSP will be applied during the script load. // CSP will be applied during the script load.
nsTArray<mozilla::ipc::ContentSecurityPolicy> policies; nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
aEntry->principal() = mozilla::ipc::ContentPrincipalInfo( aEntry->principal() = mozilla::ipc::ContentPrincipalInfo(
attrs, origin, aEntry->scope(), std::move(policies)); attrs, origin, aEntry->scope(), Nothing(), std::move(policies));
return NS_OK; return NS_OK;
} }

View File

@ -45,8 +45,7 @@ class CompareCallback {
nsresult Compare(ServiceWorkerRegistrationInfo* aRegistration, nsresult Compare(ServiceWorkerRegistrationInfo* aRegistration,
nsIPrincipal* aPrincipal, const nsAString& aCacheName, nsIPrincipal* aPrincipal, const nsAString& aCacheName,
const nsAString& aURL, CompareCallback* aCallback, const nsAString& aURL, CompareCallback* aCallback);
nsILoadGroup* aLoadGroup);
} // namespace serviceWorkerScriptCache } // namespace serviceWorkerScriptCache

View File

@ -274,7 +274,8 @@ TEST(ServiceWorkerRegistrar, TestWriteData) {
nsTArray<mozilla::ipc::ContentSecurityPolicy> policies; nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
reg.principal() = mozilla::ipc::ContentPrincipalInfo( reg.principal() = mozilla::ipc::ContentPrincipalInfo(
mozilla::OriginAttributes(i, i % 2), spec, spec, std::move(policies)); mozilla::OriginAttributes(i, i % 2), spec, spec, mozilla::Nothing(),
std::move(policies));
swr->TestRegisterServiceWorker(reg); swr->TestRegisterServiceWorker(reg);
} }
@ -863,7 +864,8 @@ TEST(ServiceWorkerRegistrar, TestDedupeWrite) {
nsTArray<mozilla::ipc::ContentSecurityPolicy> policies; nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
reg.principal() = mozilla::ipc::ContentPrincipalInfo( reg.principal() = mozilla::ipc::ContentPrincipalInfo(
mozilla::OriginAttributes(0, false), spec, spec, std::move(policies)); mozilla::OriginAttributes(0, false), spec, spec, mozilla::Nothing(),
std::move(policies));
swr->TestRegisterServiceWorker(reg); swr->TestRegisterServiceWorker(reg);
} }

View File

@ -66,7 +66,6 @@ struct IDWriteRenderingParams;
struct IDWriteFontFace; struct IDWriteFontFace;
struct IDWriteFontCollection; struct IDWriteFontCollection;
class GrContext;
class SkCanvas; class SkCanvas;
struct gfxFontStyle; struct gfxFontStyle;
@ -1508,13 +1507,6 @@ class DrawTarget : public external::AtomicRefCounted<DrawTarget> {
*/ */
virtual void DetachAllSnapshots() = 0; virtual void DetachAllSnapshots() = 0;
#ifdef USE_SKIA_GPU
virtual bool InitWithGrContext(GrContext *aGrContext, const IntSize &aSize,
SurfaceFormat aFormat) {
MOZ_CRASH("GFX: InitWithGrContext");
}
#endif
protected: protected:
UserData mUserData; UserData mUserData;
Matrix mTransform; Matrix mTransform;
@ -1749,11 +1741,6 @@ class GFX2D_API Factory {
static Config *sConfig; static Config *sConfig;
public: public:
#ifdef USE_SKIA_GPU
static already_AddRefed<DrawTarget> CreateDrawTargetSkiaWithGrContext(
GrContext *aGrContext, const IntSize &aSize, SurfaceFormat aFormat);
#endif
static void PurgeAllCaches(); static void PurgeAllCaches();
static already_AddRefed<DrawTarget> CreateDualDrawTarget(DrawTarget *targetA, static already_AddRefed<DrawTarget> CreateDualDrawTarget(DrawTarget *targetA,

View File

@ -29,13 +29,6 @@
#include "Swizzle.h" #include "Swizzle.h"
#include <algorithm> #include <algorithm>
#ifdef USE_SKIA_GPU
# include "GLDefs.h"
# include "skia/include/gpu/GrContext.h"
# include "skia/include/gpu/GrTexture.h"
# include "skia/include/gpu/gl/GrGLInterface.h"
#endif
#ifdef MOZ_WIDGET_COCOA #ifdef MOZ_WIDGET_COCOA
# include "BorrowedContext.h" # include "BorrowedContext.h"
# include <ApplicationServices/ApplicationServices.h> # include <ApplicationServices/ApplicationServices.h>
@ -649,11 +642,6 @@ void DrawTargetSkia::DrawSurface(SourceSurface* aSurface, const Rect& aDest,
} }
DrawTargetType DrawTargetSkia::GetType() const { DrawTargetType DrawTargetSkia::GetType() const {
#ifdef USE_SKIA_GPU
if (mGrContext) {
return DrawTargetType::HARDWARE_RASTER;
}
#endif
return DrawTargetType::SOFTWARE_RASTER; return DrawTargetType::SOFTWARE_RASTER;
} }
@ -700,10 +688,9 @@ void DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface* aSurface,
auto shadowDest = IntPoint::Round(aDest + aOffset); auto shadowDest = IntPoint::Round(aDest + aOffset);
SkBitmap blurMask; SkBitmap blurMask;
if (!UsingSkiaGPU() && ExtractAlphaBitmap(image, &blurMask)) { if (ExtractAlphaBitmap(image, &blurMask)) {
// Prefer using our own box blur instead of Skia's when we're // Prefer using our own box blur instead of Skia's. It currently performs
// not using the GPU. It currently performs much better than // much better than SkBlurImageFilter or SkBlurMaskFilter on the CPU.
// SkBlurImageFilter or SkBlurMaskFilter on the CPU.
AlphaBoxBlur blur(Rect(0, 0, blurMask.width(), blurMask.height()), AlphaBoxBlur blur(Rect(0, 0, blurMask.width(), blurMask.height()),
int32_t(blurMask.rowBytes()), aSigma, aSigma); int32_t(blurMask.rowBytes()), aSigma, aSigma);
blur.Blur(reinterpret_cast<uint8_t*>(blurMask.getPixels())); blur.Blur(reinterpret_cast<uint8_t*>(blurMask.getPixels()));
@ -1587,18 +1574,6 @@ already_AddRefed<SourceSurface> DrawTargetSkia::CreateSourceSurfaceFromData(
already_AddRefed<DrawTarget> DrawTargetSkia::CreateSimilarDrawTarget( already_AddRefed<DrawTarget> DrawTargetSkia::CreateSimilarDrawTarget(
const IntSize& aSize, SurfaceFormat aFormat) const { const IntSize& aSize, SurfaceFormat aFormat) const {
RefPtr<DrawTargetSkia> target = new DrawTargetSkia(); RefPtr<DrawTargetSkia> target = new DrawTargetSkia();
#ifdef USE_SKIA_GPU
if (UsingSkiaGPU()) {
// Try to create a GPU draw target first if we're currently using the GPU.
// Mark the DT as cached so that shadow DTs, extracted subrects, and similar
// can be reused.
if (target->InitWithGrContext(mGrContext.get(), aSize, aFormat, true)) {
return target.forget();
}
// Otherwise, just fall back to a software draw target.
}
#endif
#ifdef DEBUG #ifdef DEBUG
if (!IsBackedByPixels(mCanvas)) { if (!IsBackedByPixels(mCanvas)) {
// If our canvas is backed by vector storage such as PDF then we want to // If our canvas is backed by vector storage such as PDF then we want to
@ -1620,57 +1595,9 @@ bool DrawTargetSkia::CanCreateSimilarDrawTarget(const IntSize& aSize,
return size_t(std::max(aSize.width, aSize.height)) < GetMaxSurfaceSize(); return size_t(std::max(aSize.width, aSize.height)) < GetMaxSurfaceSize();
} }
bool DrawTargetSkia::UsingSkiaGPU() const {
#ifdef USE_SKIA_GPU
return !!mGrContext;
#else
return false;
#endif
}
#ifdef USE_SKIA_GPU
already_AddRefed<SourceSurface> DrawTargetSkia::OptimizeGPUSourceSurface(
SourceSurface* aSurface) const {
// Check if the underlying SkImage already has an associated GrTexture.
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
if (!image || image->isTextureBacked()) {
RefPtr<SourceSurface> surface(aSurface);
return surface.forget();
}
// Upload the SkImage to a GrTexture otherwise.
sk_sp<SkImage> texture = image->makeTextureImage(mGrContext.get(), nullptr);
if (texture) {
// Create a new SourceSurfaceSkia whose SkImage contains the GrTexture.
RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia();
if (surface->InitFromImage(texture, aSurface->GetFormat())) {
return surface.forget();
}
}
// The data was too big to fit in a GrTexture.
if (aSurface->GetType() == SurfaceType::SKIA) {
// It is already a Skia source surface, so just reuse it as-is.
RefPtr<SourceSurface> surface(aSurface);
return surface.forget();
}
// Wrap it in a Skia source surface so that can do tiled uploads on-demand.
RefPtr<SourceSurfaceSkia> surface = new SourceSurfaceSkia();
surface->InitFromImage(image);
return surface.forget();
}
#endif
already_AddRefed<SourceSurface> already_AddRefed<SourceSurface>
DrawTargetSkia::OptimizeSourceSurfaceForUnknownAlpha( DrawTargetSkia::OptimizeSourceSurfaceForUnknownAlpha(
SourceSurface* aSurface) const { SourceSurface* aSurface) const {
#ifdef USE_SKIA_GPU
if (UsingSkiaGPU()) {
return OptimizeGPUSourceSurface(aSurface);
}
#endif
if (aSurface->GetType() == SurfaceType::SKIA) { if (aSurface->GetType() == SurfaceType::SKIA) {
RefPtr<SourceSurface> surface(aSurface); RefPtr<SourceSurface> surface(aSurface);
return surface.forget(); return surface.forget();
@ -1690,12 +1617,6 @@ DrawTargetSkia::OptimizeSourceSurfaceForUnknownAlpha(
already_AddRefed<SourceSurface> DrawTargetSkia::OptimizeSourceSurface( already_AddRefed<SourceSurface> DrawTargetSkia::OptimizeSourceSurface(
SourceSurface* aSurface) const { SourceSurface* aSurface) const {
#ifdef USE_SKIA_GPU
if (UsingSkiaGPU()) {
return OptimizeGPUSourceSurface(aSurface);
}
#endif
if (aSurface->GetType() == SurfaceType::SKIA) { if (aSurface->GetType() == SurfaceType::SKIA) {
RefPtr<SourceSurface> surface(aSurface); RefPtr<SourceSurface> surface(aSurface);
return surface.forget(); return surface.forget();
@ -1714,48 +1635,9 @@ already_AddRefed<SourceSurface> DrawTargetSkia::OptimizeSourceSurface(
return dataSurface.forget(); return dataSurface.forget();
} }
#ifdef USE_SKIA_GPU
static inline GrGLenum GfxFormatToGrGLFormat(SurfaceFormat format) {
switch (format) {
case SurfaceFormat::B8G8R8A8:
return LOCAL_GL_BGRA8_EXT;
case SurfaceFormat::B8G8R8X8:
// We probably need to do something here.
return LOCAL_GL_BGRA8_EXT;
case SurfaceFormat::R5G6B5_UINT16:
return LOCAL_GL_RGB565;
case SurfaceFormat::A8:
return LOCAL_GL_ALPHA8;
default:
return LOCAL_GL_RGBA8;
}
}
#endif
already_AddRefed<SourceSurface> already_AddRefed<SourceSurface>
DrawTargetSkia::CreateSourceSurfaceFromNativeSurface( DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(
const NativeSurface& aSurface) const { const NativeSurface& aSurface) const {
#ifdef USE_SKIA_GPU
if (aSurface.mType == NativeSurfaceType::OPENGL_TEXTURE && UsingSkiaGPU()) {
// Wrap the OpenGL texture id in a Skia texture handle.
GrGLTextureInfo texInfo;
texInfo.fTarget = LOCAL_GL_TEXTURE_2D;
texInfo.fID = (GrGLuint)(uintptr_t)aSurface.mSurface;
texInfo.fFormat = GfxFormatToGrGLFormat(aSurface.mFormat);
GrBackendTexture texDesc(aSurface.mSize.width, aSurface.mSize.height,
GrMipMapped::kNo, texInfo);
sk_sp<SkImage> texture = SkImage::MakeFromAdoptedTexture(
mGrContext.get(), texDesc, kTopLeft_GrSurfaceOrigin,
GfxFormatToSkiaColorType(aSurface.mFormat),
GfxFormatToSkiaAlphaType(aSurface.mFormat));
RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
if (texture && newSurf->InitFromImage(texture, aSurface.mFormat)) {
return newSurf.forget();
}
return nullptr;
}
#endif
return nullptr; return nullptr;
} }
@ -1838,55 +1720,6 @@ bool DrawTargetSkia::Init(SkCanvas* aCanvas) {
return true; return true;
} }
#ifdef USE_SKIA_GPU
/** Indicating a DT should be cached means that space will be reserved in Skia's
* cache for the render target at creation time, with any unused resources
* exceeding the cache limits being purged. When the DT is freed, it will then
* be guaranteed to be kept around for subsequent allocations until it gets
* incidentally purged.
*
* If it is not marked as cached, no space will be purged to make room for the
* render target in the cache. When the DT is freed, If there is space within
* the resource limits it may be added to the cache, otherwise it will be freed
* immediately if the cache is already full.
*
* If you want to ensure that the resources will be kept around for reuse, it is
* better to mark them as cached. Such resources should be short-lived to ensure
* they don't permanently tie up cache resource limits. Long-lived resources
* should generally be left as uncached.
*
* In neither case will cache resource limits affect whether the resource
* allocation succeeds. The amount of in-use GPU resources is allowed to exceed
* the size of the cache. Thus, only hard GPU out-of-memory conditions will
* cause resource allocation to fail.
*/
bool DrawTargetSkia::InitWithGrContext(GrContext* aGrContext,
const IntSize& aSize,
SurfaceFormat aFormat, bool aCached) {
MOZ_ASSERT(aGrContext, "null GrContext");
if (size_t(std::max(aSize.width, aSize.height)) > GetMaxSurfaceSize()) {
return false;
}
// Create a GPU rendertarget/texture using the supplied GrContext.
// MakeRenderTarget also implicitly clears the underlying texture on creation.
mSurface = SkSurface::MakeRenderTarget(aGrContext, SkBudgeted(aCached),
MakeSkiaImageInfo(aSize, aFormat));
if (!mSurface) {
return false;
}
mGrContext = sk_ref_sp(aGrContext);
mSize = aSize;
mFormat = aFormat;
mCanvas = mSurface->getCanvas();
SetPermitSubpixelAA(IsOpaque(mFormat));
return true;
}
#endif
bool DrawTargetSkia::Init(unsigned char* aData, const IntSize& aSize, bool DrawTargetSkia::Init(unsigned char* aData, const IntSize& aSize,
int32_t aStride, SurfaceFormat aFormat, int32_t aStride, SurfaceFormat aFormat,
bool aUninitialized) { bool aUninitialized) {
@ -1914,16 +1747,6 @@ void DrawTargetSkia::SetTransform(const Matrix& aTransform) {
} }
void* DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType) { void* DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType) {
#ifdef USE_SKIA_GPU
if (aType == NativeSurfaceType::OPENGL_TEXTURE && mSurface) {
GrBackendTexture tex =
mSurface->getBackendTexture(SkSurface::kFlushRead_BackendHandleAccess);
GrGLTextureInfo info;
if (tex.getGLTextureInfo(&info)) {
return (void*)(uintptr_t)info.fID;
}
}
#endif
return nullptr; return nullptr;
} }

View File

@ -130,18 +130,6 @@ class DrawTargetSkia : public DrawTarget {
SurfaceFormat aFormat, bool aUninitialized = false); SurfaceFormat aFormat, bool aUninitialized = false);
bool Init(SkCanvas *aCanvas); bool Init(SkCanvas *aCanvas);
#ifdef USE_SKIA_GPU
bool InitWithGrContext(GrContext *aGrContext, const IntSize &aSize,
SurfaceFormat aFormat, bool aCached);
virtual bool InitWithGrContext(GrContext *aGrContext, const IntSize &aSize,
SurfaceFormat aFormat) override {
return InitWithGrContext(aGrContext, aSize, aFormat, false);
}
already_AddRefed<SourceSurface> OptimizeGPUSourceSurface(
SourceSurface *aSurface) const;
#endif
// Skia assumes that texture sizes fit in 16-bit signed integers. // Skia assumes that texture sizes fit in 16-bit signed integers.
static size_t GetMaxSurfaceSize() { return 32767; } static size_t GetMaxSurfaceSize() { return 32767; }
@ -163,8 +151,6 @@ class DrawTargetSkia : public DrawTarget {
const StrokeOptions *aStrokeOptions = nullptr, const StrokeOptions *aStrokeOptions = nullptr,
const DrawOptions &aOptions = DrawOptions()); const DrawOptions &aOptions = DrawOptions());
bool UsingSkiaGPU() const;
struct PushedLayer { struct PushedLayer {
PushedLayer(bool aOldPermitSubpixelAA, SourceSurface *aMask) PushedLayer(bool aOldPermitSubpixelAA, SourceSurface *aMask)
: mOldPermitSubpixelAA(aOldPermitSubpixelAA), mMask(aMask) {} : mOldPermitSubpixelAA(aOldPermitSubpixelAA), mMask(aMask) {}
@ -173,10 +159,6 @@ class DrawTargetSkia : public DrawTarget {
}; };
std::vector<PushedLayer> mPushedLayers; std::vector<PushedLayer> mPushedLayers;
#ifdef USE_SKIA_GPU
sk_sp<GrContext> mGrContext;
#endif
IntSize mSize; IntSize mSize;
sk_sp<SkSurface> mSurface; sk_sp<SkSurface> mSurface;
SkCanvas *mCanvas; SkCanvas *mCanvas;

View File

@ -920,18 +920,6 @@ already_AddRefed<ScaledFont> Factory::CreateScaledFontForDWriteFont(
#endif // WIN32 #endif // WIN32
#ifdef USE_SKIA_GPU
already_AddRefed<DrawTarget> Factory::CreateDrawTargetSkiaWithGrContext(
GrContext* aGrContext, const IntSize& aSize, SurfaceFormat aFormat) {
RefPtr<DrawTarget> newTarget = new DrawTargetSkia();
if (!newTarget->InitWithGrContext(aGrContext, aSize, aFormat)) {
return nullptr;
}
return newTarget.forget();
}
#endif // USE_SKIA_GPU
#ifdef USE_SKIA #ifdef USE_SKIA
already_AddRefed<DrawTarget> Factory::CreateDrawTargetWithSkCanvas( already_AddRefed<DrawTarget> Factory::CreateDrawTargetWithSkCanvas(
SkCanvas* aCanvas) { SkCanvas* aCanvas) {

View File

@ -125,16 +125,6 @@ uint8_t* SourceSurfaceSkia::GetData() {
if (!mImage) { if (!mImage) {
return nullptr; return nullptr;
} }
#ifdef USE_SKIA_GPU
if (mImage->isTextureBacked()) {
if (sk_sp<SkImage> raster =
ReadSkImage(mImage, MakeSkiaImageInfo(mSize, mFormat), mStride)) {
mImage = raster;
} else {
gfxCriticalError() << "Failed making Skia raster image for GPU surface";
}
}
#endif
SkPixmap pixmap; SkPixmap pixmap;
if (!mImage->peekPixels(&pixmap)) { if (!mImage->peekPixels(&pixmap)) {
gfxCriticalError() << "Failed accessing pixels for Skia raster image"; gfxCriticalError() << "Failed accessing pixels for Skia raster image";

View File

@ -1447,7 +1447,7 @@ void GLContext::LoadMoreSymbols(const SymbolLoader& loader) {
CORE_SYMBOL(GetTexLevelParameteriv), CORE_SYMBOL(GetTexLevelParameteriv),
END_SYMBOLS}; END_SYMBOLS};
const bool warnOnFailures = ShouldSpew(); const bool warnOnFailures = ShouldSpew();
loader.LoadSymbols(devSymbols, warnOnFailures); (void)loader.LoadSymbols(devSymbols, warnOnFailures);
} }
#undef CORE_SYMBOL #undef CORE_SYMBOL

View File

@ -1,375 +0,0 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 4; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "skia/include/gpu/GrContext.h"
#include "skia/include/gpu/gl/GrGLInterface.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/ThreadLocal.h"
#include "mozilla/DebugOnly.h"
/* SkPostConfig.h includes windows.h, which includes windef.h
* which redefines min/max. We don't want that. */
#ifdef _WIN32
# undef min
# undef max
#endif
#include "GLContext.h"
#include "SkiaGLGlue.h"
using mozilla::gl::GLContext;
using mozilla::gl::GLFeature;
using mozilla::gl::SkiaGLGlue;
template <typename R, typename... A>
static inline GrGLFunction<R (*)(A...)> WrapGL(RefPtr<GLContext> aContext,
R (GLContext::*aFunc)(A...)) {
return [aContext, aFunc](A... args) -> R {
aContext->MakeCurrent();
return (aContext->*aFunc)(args...);
};
}
template <typename R, typename... A>
static inline GrGLFunction<R (*)(A...)> WrapGL(RefPtr<GLContext> aContext,
R (*aFunc)(GLContext*, A...)) {
return [aContext, aFunc](A... args) -> R {
aContext->MakeCurrent();
return (*aFunc)(aContext, args...);
};
}
// Core GL functions required by Ganesh
static const GLubyte* glGetString_mozilla(GLContext* aContext, GrGLenum aName) {
// GLContext only exposes a OpenGL 2.0 style API, so we have to intercept a
// bunch of checks that Ganesh makes to determine which capabilities are
// present on the GL implementation and change them to match what GLContext
// actually exposes.
if (aName == LOCAL_GL_VERSION) {
if (aContext->IsGLES()) {
return reinterpret_cast<const GLubyte*>("OpenGL ES 2.0");
}
return reinterpret_cast<const GLubyte*>("2.0");
} else if (aName == LOCAL_GL_EXTENSIONS) {
// Only expose the bare minimum extensions we want to support to ensure a
// functional Ganesh as GLContext only exposes certain extensions
static bool extensionsStringBuilt = false;
static char extensionsString[1024];
if (!extensionsStringBuilt) {
extensionsString[0] = '\0';
if (aContext->IsGLES()) {
// OES is only applicable to GLES2
if (aContext->IsExtensionSupported(
GLContext::OES_packed_depth_stencil)) {
strcat(extensionsString, "GL_OES_packed_depth_stencil ");
}
if (aContext->IsExtensionSupported(GLContext::OES_rgb8_rgba8)) {
strcat(extensionsString, "GL_OES_rgb8_rgba8 ");
}
if (aContext->IsExtensionSupported(GLContext::OES_texture_npot)) {
strcat(extensionsString, "GL_OES_texture_npot ");
}
if (aContext->IsExtensionSupported(
GLContext::OES_vertex_array_object)) {
strcat(extensionsString, "GL_OES_vertex_array_object ");
}
if (aContext->IsSupported(GLFeature::standard_derivatives)) {
strcat(extensionsString, "GL_OES_standard_derivatives ");
}
} else {
if (aContext->IsSupported(GLFeature::framebuffer_object)) {
strcat(extensionsString, "GL_ARB_framebuffer_object ");
} else if (aContext->IsExtensionSupported(
GLContext::EXT_framebuffer_object)) {
strcat(extensionsString, "GL_EXT_framebuffer_object ");
}
if (aContext->IsSupported(GLFeature::texture_rg)) {
strcat(extensionsString, "GL_ARB_texture_rg ");
}
}
if (aContext->IsExtensionSupported(
GLContext::EXT_texture_format_BGRA8888)) {
strcat(extensionsString, "GL_EXT_texture_format_BGRA8888 ");
}
if (aContext->IsExtensionSupported(GLContext::EXT_packed_depth_stencil)) {
strcat(extensionsString, "GL_EXT_packed_depth_stencil ");
}
if (aContext->IsExtensionSupported(GLContext::EXT_bgra)) {
strcat(extensionsString, "GL_EXT_bgra ");
}
if (aContext->IsExtensionSupported(GLContext::EXT_read_format_bgra)) {
strcat(extensionsString, "GL_EXT_read_format_bgra ");
}
extensionsStringBuilt = true;
#ifdef DEBUG
printf_stderr("Exported SkiaGL extensions: %s\n", extensionsString);
#endif
}
return reinterpret_cast<const GLubyte*>(extensionsString);
} else if (aName == LOCAL_GL_SHADING_LANGUAGE_VERSION) {
if (aContext->IsGLES()) {
return reinterpret_cast<const GLubyte*>("OpenGL ES GLSL ES 1.0");
}
return reinterpret_cast<const GLubyte*>("1.10");
}
return aContext->fGetString(aName);
}
static GrGLInterface* CreateGrGLInterfaceFromGLContext(GLContext* context) {
auto* i = new GrGLInterface();
context->MakeCurrent();
// We support both desktop GL and GLES2
if (context->IsGLES()) {
i->fStandard = kGLES_GrGLStandard;
} else {
i->fStandard = kGL_GrGLStandard;
}
GrGLFunction<GrGLGetStringFn> getString =
WrapGL(context, &glGetString_mozilla);
GrGLFunction<GrGLGetIntegervFn> getIntegerv =
WrapGL(context, &GLContext::fGetIntegerv);
GrGLExtensions extensions;
if (!extensions.init(i->fStandard, getString, nullptr, getIntegerv)) {
delete i;
return nullptr;
}
i->fExtensions.swap(&extensions);
// Core GL functions required by Ganesh
i->fFunctions.fActiveTexture = WrapGL(context, &GLContext::fActiveTexture);
i->fFunctions.fAttachShader = WrapGL(context, &GLContext::fAttachShader);
i->fFunctions.fBindAttribLocation =
WrapGL(context, &GLContext::fBindAttribLocation);
i->fFunctions.fBindBuffer = WrapGL(context, &GLContext::fBindBuffer);
i->fFunctions.fBindFramebuffer =
WrapGL(context, &GLContext::fBindFramebuffer);
i->fFunctions.fBindRenderbuffer =
WrapGL(context, &GLContext::fBindRenderbuffer);
i->fFunctions.fBindTexture = WrapGL(context, &GLContext::fBindTexture);
i->fFunctions.fBlendFunc = WrapGL(context, &GLContext::fBlendFunc);
i->fFunctions.fBlendColor = WrapGL(context, &GLContext::fBlendColor);
i->fFunctions.fBlendEquation = WrapGL(context, &GLContext::fBlendEquation);
i->fFunctions.fBufferData = WrapGL(context, &GLContext::fBufferData);
i->fFunctions.fBufferSubData = WrapGL(context, &GLContext::fBufferSubData);
i->fFunctions.fCheckFramebufferStatus =
WrapGL(context, &GLContext::fCheckFramebufferStatus);
i->fFunctions.fClear = WrapGL(context, &GLContext::fClear);
i->fFunctions.fClearColor = WrapGL(context, &GLContext::fClearColor);
i->fFunctions.fClearStencil = WrapGL(context, &GLContext::fClearStencil);
i->fFunctions.fColorMask = WrapGL(context, &GLContext::fColorMask);
i->fFunctions.fCompileShader = WrapGL(context, &GLContext::fCompileShader);
i->fFunctions.fCompressedTexImage2D =
WrapGL(context, &GLContext::fCompressedTexImage2D);
i->fFunctions.fCompressedTexSubImage2D =
WrapGL(context, &GLContext::fCompressedTexSubImage2D);
i->fFunctions.fCopyTexSubImage2D =
WrapGL(context, &GLContext::fCopyTexSubImage2D);
i->fFunctions.fCreateProgram = WrapGL(context, &GLContext::fCreateProgram);
i->fFunctions.fCreateShader = WrapGL(context, &GLContext::fCreateShader);
i->fFunctions.fCullFace = WrapGL(context, &GLContext::fCullFace);
i->fFunctions.fDeleteBuffers = WrapGL(context, &GLContext::fDeleteBuffers);
i->fFunctions.fDeleteFramebuffers =
WrapGL(context, &GLContext::fDeleteFramebuffers);
i->fFunctions.fDeleteProgram = WrapGL(context, &GLContext::fDeleteProgram);
i->fFunctions.fDeleteRenderbuffers =
WrapGL(context, &GLContext::fDeleteRenderbuffers);
i->fFunctions.fDeleteShader = WrapGL(context, &GLContext::fDeleteShader);
i->fFunctions.fDeleteTextures = WrapGL(context, &GLContext::fDeleteTextures);
i->fFunctions.fDepthMask = WrapGL(context, &GLContext::fDepthMask);
i->fFunctions.fDisable = WrapGL(context, &GLContext::fDisable);
i->fFunctions.fDisableVertexAttribArray =
WrapGL(context, &GLContext::fDisableVertexAttribArray);
i->fFunctions.fDrawArrays = WrapGL(context, &GLContext::fDrawArrays);
i->fFunctions.fDrawElements = WrapGL(context, &GLContext::fDrawElements);
i->fFunctions.fDrawRangeElements =
WrapGL(context, &GLContext::fDrawRangeElements);
i->fFunctions.fEnable = WrapGL(context, &GLContext::fEnable);
i->fFunctions.fEnableVertexAttribArray =
WrapGL(context, &GLContext::fEnableVertexAttribArray);
i->fFunctions.fFinish = WrapGL(context, &GLContext::fFinish);
i->fFunctions.fFlush = WrapGL(context, &GLContext::fFlush);
i->fFunctions.fFramebufferRenderbuffer =
WrapGL(context, &GLContext::fFramebufferRenderbuffer);
i->fFunctions.fFramebufferTexture2D =
WrapGL(context, &GLContext::fFramebufferTexture2D);
i->fFunctions.fFrontFace = WrapGL(context, &GLContext::fFrontFace);
i->fFunctions.fGenBuffers = WrapGL(context, &GLContext::fGenBuffers);
i->fFunctions.fGenFramebuffers =
WrapGL(context, &GLContext::fGenFramebuffers);
i->fFunctions.fGenRenderbuffers =
WrapGL(context, &GLContext::fGenRenderbuffers);
i->fFunctions.fGetFramebufferAttachmentParameteriv =
WrapGL(context, &GLContext::fGetFramebufferAttachmentParameteriv);
i->fFunctions.fGenTextures = WrapGL(context, &GLContext::fGenTextures);
i->fFunctions.fGenerateMipmap = WrapGL(context, &GLContext::fGenerateMipmap);
i->fFunctions.fGetBufferParameteriv =
WrapGL(context, &GLContext::fGetBufferParameteriv);
i->fFunctions.fGetError = WrapGL(context, &GLContext::fGetError);
i->fFunctions.fGetIntegerv = getIntegerv;
i->fFunctions.fGetProgramInfoLog =
WrapGL(context, &GLContext::fGetProgramInfoLog);
i->fFunctions.fGetProgramiv = WrapGL(context, &GLContext::fGetProgramiv);
i->fFunctions.fGetRenderbufferParameteriv =
WrapGL(context, &GLContext::fGetRenderbufferParameteriv);
i->fFunctions.fGetShaderInfoLog =
WrapGL(context, &GLContext::fGetShaderInfoLog);
i->fFunctions.fGetShaderiv = WrapGL(context, &GLContext::fGetShaderiv);
i->fFunctions.fGetShaderPrecisionFormat =
WrapGL(context, &GLContext::fGetShaderPrecisionFormat);
i->fFunctions.fGetString = getString;
i->fFunctions.fGetUniformLocation =
WrapGL(context, &GLContext::fGetUniformLocation);
i->fFunctions.fIsTexture = WrapGL(context, &GLContext::fIsTexture);
i->fFunctions.fLineWidth = WrapGL(context, &GLContext::fLineWidth);
i->fFunctions.fLinkProgram = WrapGL(context, &GLContext::fLinkProgram);
i->fFunctions.fPixelStorei = WrapGL(context, &GLContext::fPixelStorei);
i->fFunctions.fPolygonMode = WrapGL(context, &GLContext::fPolygonMode);
i->fFunctions.fReadPixels = WrapGL(context, &GLContext::fReadPixels);
i->fFunctions.fRenderbufferStorage =
WrapGL(context, &GLContext::fRenderbufferStorage);
i->fFunctions.fScissor = WrapGL(context, &GLContext::fScissor);
i->fFunctions.fShaderSource = WrapGL(context, &GLContext::fShaderSource);
i->fFunctions.fStencilFunc = WrapGL(context, &GLContext::fStencilFunc);
i->fFunctions.fStencilFuncSeparate =
WrapGL(context, &GLContext::fStencilFuncSeparate);
i->fFunctions.fStencilMask = WrapGL(context, &GLContext::fStencilMask);
i->fFunctions.fStencilMaskSeparate =
WrapGL(context, &GLContext::fStencilMaskSeparate);
i->fFunctions.fStencilOp = WrapGL(context, &GLContext::fStencilOp);
i->fFunctions.fStencilOpSeparate =
WrapGL(context, &GLContext::fStencilOpSeparate);
i->fFunctions.fTexImage2D = WrapGL(context, &GLContext::fTexImage2D);
i->fFunctions.fTexParameteri = WrapGL(context, &GLContext::fTexParameteri);
i->fFunctions.fTexParameteriv = WrapGL(context, &GLContext::fTexParameteriv);
i->fFunctions.fTexSubImage2D = WrapGL(context, &GLContext::fTexSubImage2D);
i->fFunctions.fUniform1f = WrapGL(context, &GLContext::fUniform1f);
i->fFunctions.fUniform1i = WrapGL(context, &GLContext::fUniform1i);
i->fFunctions.fUniform1fv = WrapGL(context, &GLContext::fUniform1fv);
i->fFunctions.fUniform1iv = WrapGL(context, &GLContext::fUniform1iv);
i->fFunctions.fUniform2f = WrapGL(context, &GLContext::fUniform2f);
i->fFunctions.fUniform2i = WrapGL(context, &GLContext::fUniform2i);
i->fFunctions.fUniform2fv = WrapGL(context, &GLContext::fUniform2fv);
i->fFunctions.fUniform2iv = WrapGL(context, &GLContext::fUniform2iv);
i->fFunctions.fUniform3f = WrapGL(context, &GLContext::fUniform3f);
i->fFunctions.fUniform3i = WrapGL(context, &GLContext::fUniform3i);
i->fFunctions.fUniform3fv = WrapGL(context, &GLContext::fUniform3fv);
i->fFunctions.fUniform3iv = WrapGL(context, &GLContext::fUniform3iv);
i->fFunctions.fUniform4f = WrapGL(context, &GLContext::fUniform4f);
i->fFunctions.fUniform4i = WrapGL(context, &GLContext::fUniform4i);
i->fFunctions.fUniform4fv = WrapGL(context, &GLContext::fUniform4fv);
i->fFunctions.fUniform4iv = WrapGL(context, &GLContext::fUniform4iv);
i->fFunctions.fUniformMatrix2fv =
WrapGL(context, &GLContext::fUniformMatrix2fv);
i->fFunctions.fUniformMatrix3fv =
WrapGL(context, &GLContext::fUniformMatrix3fv);
i->fFunctions.fUniformMatrix4fv =
WrapGL(context, &GLContext::fUniformMatrix4fv);
i->fFunctions.fUseProgram = WrapGL(context, &GLContext::fUseProgram);
i->fFunctions.fVertexAttrib1f = WrapGL(context, &GLContext::fVertexAttrib1f);
i->fFunctions.fVertexAttrib2fv =
WrapGL(context, &GLContext::fVertexAttrib2fv);
i->fFunctions.fVertexAttrib3fv =
WrapGL(context, &GLContext::fVertexAttrib3fv);
i->fFunctions.fVertexAttrib4fv =
WrapGL(context, &GLContext::fVertexAttrib4fv);
i->fFunctions.fVertexAttribPointer =
WrapGL(context, &GLContext::fVertexAttribPointer);
i->fFunctions.fViewport = WrapGL(context, &GLContext::fViewport);
// Required for either desktop OpenGL 2.0 or OpenGL ES 2.0
i->fFunctions.fStencilFuncSeparate =
WrapGL(context, &GLContext::fStencilFuncSeparate);
i->fFunctions.fStencilMaskSeparate =
WrapGL(context, &GLContext::fStencilMaskSeparate);
i->fFunctions.fStencilOpSeparate =
WrapGL(context, &GLContext::fStencilOpSeparate);
// GLContext supports glMapBuffer
i->fFunctions.fMapBuffer = WrapGL(context, &GLContext::fMapBuffer);
i->fFunctions.fUnmapBuffer = WrapGL(context, &GLContext::fUnmapBuffer);
// GLContext supports glRenderbufferStorageMultisample/glBlitFramebuffer
i->fFunctions.fRenderbufferStorageMultisample =
WrapGL(context, &GLContext::fRenderbufferStorageMultisample);
i->fFunctions.fBlitFramebuffer =
WrapGL(context, &GLContext::fBlitFramebuffer);
// GLContext supports glCompressedTexImage2D
i->fFunctions.fCompressedTexImage2D =
WrapGL(context, &GLContext::fCompressedTexImage2D);
// GL_OES_vertex_array_object
i->fFunctions.fBindVertexArray =
WrapGL(context, &GLContext::fBindVertexArray);
i->fFunctions.fDeleteVertexArrays =
WrapGL(context, &GLContext::fDeleteVertexArrays);
i->fFunctions.fGenVertexArrays =
WrapGL(context, &GLContext::fGenVertexArrays);
// Desktop GL
i->fFunctions.fGetTexLevelParameteriv =
WrapGL(context, &GLContext::fGetTexLevelParameteriv);
i->fFunctions.fDrawBuffer = WrapGL(context, &GLContext::fDrawBuffer);
i->fFunctions.fReadBuffer = WrapGL(context, &GLContext::fReadBuffer);
// Desktop OpenGL > 1.5
i->fFunctions.fGenQueries = WrapGL(context, &GLContext::fGenQueries);
i->fFunctions.fDeleteQueries = WrapGL(context, &GLContext::fDeleteQueries);
i->fFunctions.fBeginQuery = WrapGL(context, &GLContext::fBeginQuery);
i->fFunctions.fEndQuery = WrapGL(context, &GLContext::fEndQuery);
i->fFunctions.fGetQueryiv = WrapGL(context, &GLContext::fGetQueryiv);
i->fFunctions.fGetQueryObjectiv =
WrapGL(context, &GLContext::fGetQueryObjectiv);
i->fFunctions.fGetQueryObjectuiv =
WrapGL(context, &GLContext::fGetQueryObjectuiv);
// Desktop OpenGL > 2.0
i->fFunctions.fDrawBuffers = WrapGL(context, &GLContext::fDrawBuffers);
return i;
}
SkiaGLGlue::SkiaGLGlue(GLContext* context) : mGLContext(context) {
mGrGLInterface.reset(CreateGrGLInterfaceFromGLContext(mGLContext));
mGrContext = GrContext::MakeGL(mGrGLInterface);
}
SkiaGLGlue::~SkiaGLGlue() {
/*
* These members have inter-dependencies, but do not keep each other alive, so
* destruction order is very important here: mGrContext uses mGrGLInterface,
* and through it, uses mGLContext
*/
mGrContext = nullptr;
if (mGrGLInterface) {
// Ensure that no references to the GLContext remain, even if the GrContext
// still lives.
mGrGLInterface->fFunctions = GrGLInterface::Functions();
mGrGLInterface = nullptr;
}
mGLContext = nullptr;
}

View File

@ -1,63 +0,0 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 4; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef SKIA_GL_GLUE_H_
#define SKIA_GL_GLUE_H_
#ifdef USE_SKIA_GPU
# include "skia/include/core/SkRefCnt.h"
# include "mozilla/RefPtr.h"
struct GrGLInterface;
class GrContext;
namespace mozilla {
namespace gl {
class GLContext;
class SkiaGLGlue : public GenericAtomicRefCounted {
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SkiaGLGlue, override)
explicit SkiaGLGlue(GLContext* context);
GLContext* GetGLContext() const { return mGLContext.get(); }
GrContext* GetGrContext() const { return mGrContext.get(); }
protected:
virtual ~SkiaGLGlue();
private:
RefPtr<GLContext> mGLContext;
sk_sp<GrGLInterface> mGrGLInterface;
sk_sp<GrContext> mGrContext;
};
} // namespace gl
} // namespace mozilla
#else // USE_SKIA_GPU
class GrContext;
namespace mozilla {
namespace gl {
class GLContext;
class SkiaGLGlue : public GenericAtomicRefCounted {
public:
SkiaGLGlue(GLContext* context);
GLContext* GetGLContext() const { return nullptr; }
GrContext* GetGrContext() const { return nullptr; }
};
} // namespace gl
} // namespace mozilla
#endif // USE_SKIA_GPU
#endif // SKIA_GL_GLUE_H_

View File

@ -74,14 +74,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'SharedSurfaceANGLE.cpp', 'SharedSurfaceANGLE.cpp',
'SharedSurfaceD3D11Interop.cpp', 'SharedSurfaceD3D11Interop.cpp',
] ]
if CONFIG['MOZ_ENABLE_SKIA_GPU']:
EXPORTS += ['SkiaGLGlue.h']
SOURCES += [
'SkiaGLGlue.cpp',
]
if CONFIG['CC_TYPE'] == 'clang':
# Suppress warnings from Skia header files.
SOURCES['SkiaGLGlue.cpp'].flags += ['-Wno-implicit-fallthrough']
if gl_provider == 'CGL': if gl_provider == 'CGL':
# These files include Mac headers that are unfriendly to unified builds # These files include Mac headers that are unfriendly to unified builds

View File

@ -240,7 +240,7 @@ mozilla::ipc::IPCResult GPUChild::RecvNotifyDeviceReset(
bool GPUChild::SendRequestMemoryReport(const uint32_t& aGeneration, bool GPUChild::SendRequestMemoryReport(const uint32_t& aGeneration,
const bool& aAnonymize, const bool& aAnonymize,
const bool& aMinimizeMemoryUsage, const bool& aMinimizeMemoryUsage,
const MaybeFileDesc& aDMDFile) { const Maybe<FileDescriptor>& aDMDFile) {
mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration); mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration);
Unused << PGPUChild::SendRequestMemoryReport(aGeneration, aAnonymize, Unused << PGPUChild::SendRequestMemoryReport(aGeneration, aAnonymize,
aMinimizeMemoryUsage, aDMDFile); aMinimizeMemoryUsage, aDMDFile);

View File

@ -78,7 +78,7 @@ class GPUChild final : public PGPUChild, public gfxVarReceiver {
bool SendRequestMemoryReport(const uint32_t& aGeneration, bool SendRequestMemoryReport(const uint32_t& aGeneration,
const bool& aAnonymize, const bool& aAnonymize,
const bool& aMinimizeMemoryUsage, const bool& aMinimizeMemoryUsage,
const MaybeFileDesc& aDMDFile); const Maybe<ipc::FileDescriptor>& aDMDFile);
static void Destroy(UniquePtr<GPUChild>&& aChild); static void Destroy(UniquePtr<GPUChild>&& aChild);

View File

@ -444,7 +444,7 @@ void GPUParent::GetGPUProcessName(nsACString& aStr) {
mozilla::ipc::IPCResult GPUParent::RecvRequestMemoryReport( mozilla::ipc::IPCResult GPUParent::RecvRequestMemoryReport(
const uint32_t& aGeneration, const bool& aAnonymize, const uint32_t& aGeneration, const bool& aAnonymize,
const bool& aMinimizeMemoryUsage, const MaybeFileDesc& aDMDFile) { const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile) {
nsAutoCString processName; nsAutoCString processName;
GetGPUProcessName(processName); GetGPUProcessName(processName);

View File

@ -74,7 +74,8 @@ class GPUParent final : public PGPUParent {
mozilla::ipc::IPCResult RecvNotifyGpuObservers(const nsCString& aTopic); mozilla::ipc::IPCResult RecvNotifyGpuObservers(const nsCString& aTopic);
mozilla::ipc::IPCResult RecvRequestMemoryReport( mozilla::ipc::IPCResult RecvRequestMemoryReport(
const uint32_t& generation, const bool& anonymize, const uint32_t& generation, const bool& anonymize,
const bool& minimizeMemoryUsage, const MaybeFileDesc& DMDFile); const bool& minimizeMemoryUsage,
const Maybe<ipc::FileDescriptor>& DMDFile);
mozilla::ipc::IPCResult RecvShutdownVR(); mozilla::ipc::IPCResult RecvShutdownVR();
void ActorDestroy(ActorDestroyReason aWhy) override; void ActorDestroy(ActorDestroyReason aWhy) override;

View File

@ -1037,7 +1037,7 @@ class GPUMemoryReporter : public MemoryReportingProcess {
bool SendRequestMemoryReport(const uint32_t& aGeneration, bool SendRequestMemoryReport(const uint32_t& aGeneration,
const bool& aAnonymize, const bool& aAnonymize,
const bool& aMinimizeMemoryUsage, const bool& aMinimizeMemoryUsage,
const dom::MaybeFileDesc& aDMDFile) override { const Maybe<FileDescriptor>& aDMDFile) override {
GPUChild* child = GetChild(); GPUChild* child = GetChild();
if (!child) { if (!child) {
return false; return false;

Some files were not shown because too many files have changed in this diff Show More