Merge autoland to mozilla-central. a=merge

This commit is contained in:
Narcis Beleuzu 2018-01-24 23:54:03 +02:00
commit f8bb2d707a
213 changed files with 6861 additions and 2091 deletions

View File

@ -1183,6 +1183,7 @@ pref("services.sync.prefs.sync.browser.tabs.loadInBackground", true);
pref("services.sync.prefs.sync.browser.tabs.warnOnClose", true);
pref("services.sync.prefs.sync.browser.tabs.warnOnOpen", true);
pref("services.sync.prefs.sync.browser.urlbar.autocomplete.enabled", true);
pref("services.sync.prefs.sync.browser.urlbar.matchBuckets", true);
pref("services.sync.prefs.sync.browser.urlbar.maxRichResults", true);
pref("services.sync.prefs.sync.browser.urlbar.suggest.bookmark", true);
pref("services.sync.prefs.sync.browser.urlbar.suggest.history", true);
@ -1338,6 +1339,10 @@ pref("security.insecure_field_warning.contextual.enabled", true);
pref("security.insecure_connection_icon.enabled", false);
pref("security.insecure_connection_icon.pbmode.enabled", false);
// Show "Not Secure" text for http pages; disabled for now
pref("security.insecure_connection_text.enabled", false);
pref("security.insecure_connection_text.pbmode.enabled", false);
// 1 = allow MITM for certificate pinning checks.
pref("security.cert_pinning.enforcement_level", 1);

View File

@ -7517,8 +7517,15 @@ var gIdentityHandler = {
(Services.prefs.getBoolPref("security.insecure_connection_icon.pbmode.enabled") &&
PrivateBrowsingUtils.isWindowPrivate(window));
let className = warnOnInsecure ? "notSecure" : "unknownIdentity";
this._identityBox.className = className;
let warnTextOnInsecure = Services.prefs.getBoolPref("security.insecure_connection_text.enabled") ||
(Services.prefs.getBoolPref("security.insecure_connection_text.pbmode.enabled") &&
PrivateBrowsingUtils.isWindowPrivate(window));
if (warnTextOnInsecure) {
icon_label = gNavigatorBundle.getString("identity.notSecure.label");
this._identityBox.classList.add("notSecureText");
}
}
if (this._hasInsecureLoginForms) {
// Insecure login forms can only be present on "unknown identity"

View File

@ -6,6 +6,7 @@
const DUMMY = "browser/browser/base/content/test/siteIdentity/dummy_page.html";
const INSECURE_ICON_PREF = "security.insecure_connection_icon.enabled";
const INSECURE_TEXT_PREF = "security.insecure_connection_text.enabled";
const INSECURE_PBMODE_ICON_PREF = "security.insecure_connection_icon.pbmode.enabled";
function loadNewTab(url) {
@ -56,6 +57,69 @@ add_task(async function test_webpage() {
await webpageTest(true);
});
async function webpageTestTextWarning(secureCheck) {
await SpecialPowers.pushPrefEnv({set: [[INSECURE_TEXT_PREF, secureCheck]]});
let oldTab = gBrowser.selectedTab;
let newTab = await loadNewTab("http://example.com/" + DUMMY);
if (secureCheck) {
is(getIdentityMode(), "unknownIdentity notSecureText", "Identity should have not secure text");
} else {
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
}
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = newTab;
if (secureCheck) {
is(getIdentityMode(), "unknownIdentity notSecureText", "Identity should have not secure text");
} else {
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
}
gBrowser.removeTab(newTab);
await SpecialPowers.popPrefEnv();
}
add_task(async function test_webpage_text_warning() {
await webpageTestTextWarning(false);
await webpageTestTextWarning(true);
});
async function webpageTestTextWarningCombined(secureCheck) {
await SpecialPowers.pushPrefEnv({set: [
[INSECURE_TEXT_PREF, secureCheck],
[INSECURE_ICON_PREF, secureCheck]
]});
let oldTab = gBrowser.selectedTab;
let newTab = await loadNewTab("http://example.com/" + DUMMY);
if (secureCheck) {
is(getIdentityMode(), "notSecure notSecureText", "Identity should be not secure");
} else {
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
}
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = newTab;
if (secureCheck) {
is(getIdentityMode(), "notSecure notSecureText", "Identity should be not secure");
} else {
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
}
gBrowser.removeTab(newTab);
await SpecialPowers.popPrefEnv();
}
add_task(async function test_webpage_text_warning_combined() {
await webpageTestTextWarning(false);
await webpageTestTextWarning(true);
});
async function blankPageTest(secureCheck) {
let oldTab = gBrowser.selectedTab;
await SpecialPowers.pushPrefEnv({set: [[INSECURE_ICON_PREF, secureCheck]]});

View File

@ -179,9 +179,11 @@ let runWorkerTest = async function(data) {
if (e.data.type == "status") {
ok(e.data.status, e.data.msg);
} else if (e.data.type == "finish") {
worker.terminate();
resolve();
} else {
ok(false, "Unknown message type");
worker.terminate();
resolve();
}
};

View File

@ -57,8 +57,10 @@ function setSignedInUser(data) {
uid: "1234@lcip.org",
assertion: "foobar",
sessionToken: "dead",
kA: "beef",
kB: "cafe",
kSync: "beef",
kXCS: "cafe",
kExtSync: "bacon",
kExtKbHash: "cheese",
verified: true
};
}

View File

@ -1,5 +1,6 @@
[DEFAULT]
firefox-appdir = browser
headless = true
head = head.js
support-files =
../fixtures/**

View File

@ -515,6 +515,11 @@ identity.identified.verifier=Verified by: %S
identity.identified.verified_by_you=You have added a security exception for this site.
identity.identified.state_and_country=%S, %S
# LOCALIZATION NOTE (identity.notSecure.label):
# Keep this string as short as possible, this is displayed in the URL bar
# use a synonym for "safe" or "private" if "secure" is too long.
identity.notSecure.label=Not Secure
identity.icon.tooltip=Show site information
identity.extension.label=Extension (%S)
identity.extension.tooltip=Loaded by extension: %S

View File

@ -1272,19 +1272,6 @@ toolbarpaletteitem[place="menu-panel"] > .subviewbutton-nav::after {
margin-inline-end: 0;
}
%ifdef XP_WIN
/* Overrides from menu.css to prevent items in the bookmarks popup from being too tall.
These won't be necessary once menu.css is loaded as a UA style (Bug 1420229). */
#BMB_bookmarksPopup .subviewbutton > .menu-iconic-left {
padding-top: 0;
-moz-appearance: none;
}
#BMB_bookmarksPopup .subviewbutton {
-moz-appearance: none;
}
%endif
menuitem[checked="true"].subviewbutton > .menu-iconic-left {
visibility: hidden;
}

View File

@ -44,13 +44,14 @@
#identity-icon-labels:-moz-locale-dir(rtl) {
padding-right: 4px;
}
#identity-box:not(.chromeUI):not(.extensionPage) {
#identity-box:not(.chromeUI):not(.extensionPage):not(.notSecureText) {
--urlbar-separator-color: transparent;
}
#urlbar[pageproxystate=valid] > #identity-box.verifiedIdentity {
--urlbar-separator-color: rgba(18, 188, 0, .5);
}
#urlbar[pageproxystate=valid] > #identity-box.notSecureText,
#urlbar[pageproxystate=valid] > #identity-box.verifiedIdentity,
#urlbar[pageproxystate=valid] > #identity-box.chromeUI,
#urlbar[pageproxystate=valid] > #identity-box.extensionPage,
@ -61,6 +62,7 @@
border-image-slice: 1;
}
#urlbar[pageproxystate=valid] > #identity-box.notSecureText,
#urlbar[pageproxystate=valid] > #identity-box.verifiedIdentity,
#urlbar[pageproxystate=valid] > #identity-box.chromeUI,
#urlbar[pageproxystate=valid] > #identity-box.extensionPage {

View File

@ -356,6 +356,8 @@ tabbrowser {
.tab-label {
margin-inline-end: 0;
margin-inline-start: 0;
/* Maintain consistent alignment in case of font fallback for non-Latin characters. */
line-height: 1.7em;
}
.tab-close-button {

View File

@ -76,13 +76,16 @@ nsChromeProtocolHandler::NewURI(const nsACString &aSpec,
// Chrome: URLs (currently) have no additional structure beyond that provided
// by standard URLs, so there is no "outer" given to CreateInstance
RefPtr<mozilla::net::nsStandardURL> surl = new mozilla::net::nsStandardURL();
nsresult rv = surl->Init(nsIStandardURL::URLTYPE_STANDARD, -1, aSpec,
aCharset, aBaseURI);
if (NS_FAILED(rv))
nsresult rv;
nsCOMPtr<nsIURL> surl;
rv = NS_MutateURI(new mozilla::net::nsStandardURL::Mutator())
.Apply<nsIStandardURLMutator>(&nsIStandardURLMutator::Init,
nsIStandardURL::URLTYPE_STANDARD, -1,
nsCString(aSpec), aCharset, aBaseURI, nullptr)
.Finalize(surl);
if (NS_FAILED(rv)) {
return rv;
}
// Canonify the "chrome:" URL; e.g., so that we collapse
// "chrome://navigator/content/" and "chrome://navigator/content"
@ -92,7 +95,7 @@ nsChromeProtocolHandler::NewURI(const nsACString &aSpec,
if (NS_FAILED(rv))
return rv;
surl->SetMutable(false);
NS_TryToSetImmutable(surl);
surl.forget(result);
return NS_OK;

View File

@ -15,7 +15,7 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
ifndef RESFILE
RCFILE=./module.rc
RESFILE=./module.res
_RC_STRING = -QUIET 1 -DEPTH $(DEPTH) -TOPSRCDIR $(MOZILLA_DIR) -OBJDIR . -SRCDIR $(srcdir) -DISPNAME $(MOZ_APP_DISPLAYNAME) -APPVERSION $(MOZ_APP_VERSION)
_RC_STRING = -QUIET 1 -DEPTH $(DEPTH) -TOPSRCDIR $(MOZILLA_DIR) -OBJDIR . -SRCDIR $(srcdir) -DISPNAME "$(MOZ_APP_DISPLAYNAME)" -APPVERSION $(MOZ_APP_VERSION)
ifdef MOZILLA_OFFICIAL
_RC_STRING += -OFFICIAL 1
endif

View File

@ -372,13 +372,13 @@
/* Duration column */
.requests-list-duration {
.requests-list-duration-time {
width: 8%;
}
/* Latency column */
.requests-list-latency {
.requests-list-latency-time {
width: 8%;
}

View File

@ -1,39 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Component } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { getFormattedTime } = require("../utils/format-utils");
const { div } = dom;
class RequestListColumnDuration extends Component {
static get propTypes() {
return {
item: PropTypes.object.isRequired,
};
}
shouldComponentUpdate(nextProps) {
return this.props.item.totalTime !== nextProps.item.totalTime;
}
render() {
let { totalTime } = this.props.item;
let duration = getFormattedTime(totalTime);
return (
div({
className: "requests-list-column requests-list-duration",
title: duration,
},
duration
)
);
}
}
module.exports = RequestListColumnDuration;

View File

@ -1,42 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Component } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { getFormattedTime } = require("../utils/format-utils");
const { getEndTime } = require("../utils/request-utils");
const { div } = dom;
class RequestListColumnEndTime extends Component {
static get propTypes() {
return {
firstRequestStartedMillis: PropTypes.number.isRequired,
item: PropTypes.object.isRequired,
};
}
shouldComponentUpdate(nextProps) {
return getEndTime(this.props.item) !== getEndTime(nextProps.item);
}
render() {
let { firstRequestStartedMillis, item } = this.props;
let endTime = getFormattedTime(getEndTime(item, firstRequestStartedMillis));
return (
div({
className: "requests-list-column requests-list-end-time",
title: endTime,
},
endTime
)
);
}
}
module.exports = RequestListColumnEndTime;

View File

@ -1,42 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Component } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { getFormattedTime } = require("../utils/format-utils");
const { div } = dom;
class RequestListColumnLatency extends Component {
static get propTypes() {
return {
item: PropTypes.object.isRequired,
};
}
shouldComponentUpdate(nextProps) {
let { eventTimings: currEventTimings = { timings: {} } } = this.props.item;
let { eventTimings: nextEventTimings = { timings: {} } } = nextProps.item;
return currEventTimings.timings.wait !== nextEventTimings.timings.wait;
}
render() {
let { eventTimings = { timings: {} } } = this.props.item;
let latency = getFormattedTime(eventTimings.timings.wait);
return (
div({
className: "requests-list-column requests-list-latency",
title: latency,
},
latency
)
);
}
}
module.exports = RequestListColumnLatency;

View File

@ -1,43 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Component } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { getFormattedTime } = require("../utils/format-utils");
const { getResponseTime } = require("../utils/request-utils");
const { div } = dom;
class RequestListColumnResponseTime extends Component {
static get propTypes() {
return {
firstRequestStartedMillis: PropTypes.number.isRequired,
item: PropTypes.object.isRequired,
};
}
shouldComponentUpdate(nextProps) {
return getResponseTime(this.props.item) !== getResponseTime(nextProps.item);
}
render() {
let { firstRequestStartedMillis, item } = this.props;
let responseTime = getFormattedTime(
getResponseTime(item, firstRequestStartedMillis));
return (
div({
className: "requests-list-column requests-list-response-time",
title: responseTime,
},
responseTime
)
);
}
}
module.exports = RequestListColumnResponseTime;

View File

@ -1,43 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Component } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { getFormattedTime } = require("../utils/format-utils");
const { getStartTime } = require("../utils/request-utils");
const { div } = dom;
class RequestListColumnStartTime extends Component {
static get propTypes() {
return {
firstRequestStartedMillis: PropTypes.number.isRequired,
item: PropTypes.object.isRequired,
};
}
shouldComponentUpdate(nextProps) {
return getStartTime(this.props.item, this.props.firstRequestStartedMillis)
!== getStartTime(nextProps.item, nextProps.firstRequestStartedMillis);
}
render() {
let { firstRequestStartedMillis, item } = this.props;
let startTime = getFormattedTime(getStartTime(item, firstRequestStartedMillis));
return (
div({
className: "requests-list-column requests-list-start-time",
title: startTime,
},
startTime
)
);
}
}
module.exports = RequestListColumnStartTime;

View File

@ -0,0 +1,98 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Component } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { getFormattedTime } = require("../utils/format-utils");
const {
fetchNetworkUpdatePacket,
getResponseTime,
getStartTime,
getEndTime,
} = require("../utils/request-utils");
const { div } = dom;
/**
* This component represents a column displaying selected
* timing value. There are following possible values this
* column can render:
* - Start Time
* - End Time
* - Response Time
* - Duration Time
* - Latency Time
*/
class RequestListColumnTime extends Component {
static get propTypes() {
return {
connector: PropTypes.object.isRequired,
firstRequestStartedMillis: PropTypes.number.isRequired,
item: PropTypes.object.isRequired,
type: PropTypes.oneOf([
"start",
"end",
"response",
"duration",
"latency",
]).isRequired,
};
}
componentDidMount() {
let { item, connector } = this.props;
fetchNetworkUpdatePacket(connector.requestData, item, ["eventTimings"]);
}
componentWillReceiveProps(nextProps) {
let { item, connector } = nextProps;
fetchNetworkUpdatePacket(connector.requestData, item, ["eventTimings"]);
}
shouldComponentUpdate(nextProps) {
return this.getTime(this.props) !== this.getTime(nextProps);
}
getTime(props) {
let {
firstRequestStartedMillis,
item,
type,
} = props;
switch (type) {
case "start":
return getStartTime(item, firstRequestStartedMillis);
case "end":
return getEndTime(item, firstRequestStartedMillis);
case "response":
return getResponseTime(item, firstRequestStartedMillis);
case "duration":
return item.totalTime;
case "latency":
return item.eventTimings ? item.eventTimings.timings.wait : undefined;
}
return 0;
}
render() {
let { type } = this.props;
let time = getFormattedTime(this.getTime(this.props));
return (
div({
className: "requests-list-column requests-list-" + type + "-time",
title: time,
},
time
)
);
}
}
module.exports = RequestListColumnTime;

View File

@ -20,19 +20,15 @@ const { RESPONSE_HEADERS } = require("../constants");
RequestListColumnContentSize,
RequestListColumnCookies,
RequestListColumnDomain,
RequestListColumnDuration,
RequestListColumnEndTime,
RequestListColumnFile,
RequestListColumnLatency,
RequestListColumnMethod,
RequestListColumnProtocol,
RequestListColumnRemoteIP,
RequestListColumnResponseHeader,
RequestListColumnResponseTime,
RequestListColumnScheme,
RequestListColumnSetCookies,
RequestListColumnStartTime,
RequestListColumnStatus,
RequestListColumnTime,
RequestListColumnTransferredSize,
RequestListColumnType,
RequestListColumnWaterfall
@ -50,18 +46,9 @@ loader.lazyGetter(this, "RequestListColumnCookies", function () {
loader.lazyGetter(this, "RequestListColumnDomain", function () {
return createFactory(require("./RequestListColumnDomain"));
});
loader.lazyGetter(this, "RequestListColumnDuration", function () {
return createFactory(require("./RequestListColumnDuration"));
});
loader.lazyGetter(this, "RequestListColumnEndTime", function () {
return createFactory(require("./RequestListColumnEndTime"));
});
loader.lazyGetter(this, "RequestListColumnFile", function () {
return createFactory(require("./RequestListColumnFile"));
});
loader.lazyGetter(this, "RequestListColumnLatency", function () {
return createFactory(require("./RequestListColumnLatency"));
});
loader.lazyGetter(this, "RequestListColumnMethod", function () {
return createFactory(require("./RequestListColumnMethod"));
});
@ -74,8 +61,8 @@ loader.lazyGetter(this, "RequestListColumnRemoteIP", function () {
loader.lazyGetter(this, "RequestListColumnResponseHeader", function () {
return createFactory(require("./RequestListColumnResponseHeader"));
});
loader.lazyGetter(this, "RequestListColumnResponseTime", function () {
return createFactory(require("./RequestListColumnResponseTime"));
loader.lazyGetter(this, "RequestListColumnTime", function () {
return createFactory(require("./RequestListColumnTime"));
});
loader.lazyGetter(this, "RequestListColumnScheme", function () {
return createFactory(require("./RequestListColumnScheme"));
@ -83,9 +70,6 @@ loader.lazyGetter(this, "RequestListColumnScheme", function () {
loader.lazyGetter(this, "RequestListColumnSetCookies", function () {
return createFactory(require("./RequestListColumnSetCookies"));
});
loader.lazyGetter(this, "RequestListColumnStartTime", function () {
return createFactory(require("./RequestListColumnStartTime"));
});
loader.lazyGetter(this, "RequestListColumnStatus", function () {
return createFactory(require("./RequestListColumnStatus"));
});
@ -234,23 +218,50 @@ class RequestListItem extends Component {
columns.file && RequestListColumnFile({ item }),
columns.protocol && RequestListColumnProtocol({ item }),
columns.scheme && RequestListColumnScheme({ item }),
columns.domain && RequestListColumnDomain({ item,
onSecurityIconMouseDown }),
columns.domain && RequestListColumnDomain({
item,
onSecurityIconMouseDown
}),
columns.remoteip && RequestListColumnRemoteIP({ item }),
columns.cause && RequestListColumnCause({ item, onCauseBadgeMouseDown }),
columns.cause && RequestListColumnCause({
item,
onCauseBadgeMouseDown
}),
columns.type && RequestListColumnType({ item }),
columns.cookies && RequestListColumnCookies({ connector, item }),
columns.setCookies && RequestListColumnSetCookies({ connector, item }),
columns.transferred && RequestListColumnTransferredSize({ item }),
columns.contentSize && RequestListColumnContentSize({ item }),
columns.startTime &&
RequestListColumnStartTime({ item, firstRequestStartedMillis }),
columns.endTime &&
RequestListColumnEndTime({ item, firstRequestStartedMillis }),
columns.responseTime &&
RequestListColumnResponseTime({ item, firstRequestStartedMillis }),
columns.duration && RequestListColumnDuration({ item }),
columns.latency && RequestListColumnLatency({ item }),
columns.startTime && RequestListColumnTime({
connector,
item,
firstRequestStartedMillis,
type: "start",
}),
columns.endTime && RequestListColumnTime({
connector,
item,
firstRequestStartedMillis,
type: "end",
}),
columns.responseTime && RequestListColumnTime({
connector,
item,
firstRequestStartedMillis,
type: "response",
}),
columns.duration && RequestListColumnTime({
connector,
item,
firstRequestStartedMillis,
type: "duration",
}),
columns.latency && RequestListColumnTime({
connector,
item,
firstRequestStartedMillis,
type: "latency",
}),
...RESPONSE_HEADERS.filter(header => columns[header]).map(
header => RequestListColumnResponseHeader({ item, header }),
),

View File

@ -18,19 +18,15 @@ DevToolsModules(
'RequestListColumnContentSize.js',
'RequestListColumnCookies.js',
'RequestListColumnDomain.js',
'RequestListColumnDuration.js',
'RequestListColumnEndTime.js',
'RequestListColumnFile.js',
'RequestListColumnLatency.js',
'RequestListColumnMethod.js',
'RequestListColumnProtocol.js',
'RequestListColumnRemoteIP.js',
'RequestListColumnResponseHeader.js',
'RequestListColumnResponseTime.js',
'RequestListColumnScheme.js',
'RequestListColumnSetCookies.js',
'RequestListColumnStartTime.js',
'RequestListColumnStatus.js',
'RequestListColumnTime.js',
'RequestListColumnTransferredSize.js',
'RequestListColumnType.js',
'RequestListColumnWaterfall.js',

View File

@ -79,6 +79,7 @@ support-files =
[browser_net_columns_pref.js]
[browser_net_columns_reset.js]
[browser_net_columns_showhide.js]
[browser_net_columns_time.js]
[browser_net_complex-params.js]
[browser_net_content-type.js]
[browser_net_brotli.js]

View File

@ -14,15 +14,15 @@ add_task(function* () {
let { monitor } = yield initNetMonitor(SIMPLE_URL);
info("Starting test... ");
let { document, parent } = monitor.panelWin;
let { document } = monitor.panelWin;
ok(document.querySelector("#requests-list-status-button"),
"Status column should be shown");
ok(document.querySelector("#requests-list-contentSize-button"),
"Content size column should be shown");
yield hideColumn("status");
yield hideColumn("contentSize");
yield hideColumn(monitor, "status");
yield hideColumn(monitor, "contentSize");
let visibleColumns = JSON.parse(
Services.prefs.getCharPref("devtools.netmonitor.visibleColumns")
@ -33,7 +33,7 @@ add_task(function* () {
ok(!visibleColumns.includes("contentSize"),
"Pref should be synced for contentSize");
yield showColumn("status");
yield showColumn(monitor, "status");
visibleColumns = JSON.parse(
Services.prefs.getCharPref("devtools.netmonitor.visibleColumns")
@ -41,30 +41,4 @@ add_task(function* () {
ok(visibleColumns.includes("status"),
"Pref should be synced for status");
function* hideColumn(column) {
info(`Clicking context-menu item for ${column}`);
EventUtils.sendMouseEvent({ type: "contextmenu" },
document.querySelector(".devtools-toolbar.requests-list-headers"));
let onHeaderRemoved = waitForDOM(document, `#requests-list-${column}-button`, 0);
parent.document.querySelector(`#request-list-header-${column}-toggle`).click();
yield onHeaderRemoved;
ok(!document.querySelector(`#requests-list-${column}-button`),
`Column ${column} should be hidden`);
}
function* showColumn(column) {
info(`Clicking context-menu item for ${column}`);
EventUtils.sendMouseEvent({ type: "contextmenu" },
document.querySelector(".devtools-toolbar.requests-list-headers"));
let onHeaderAdded = waitForDOM(document, `#requests-list-${column}-button`, 1);
parent.document.querySelector(`#request-list-header-${column}-toggle`).click();
yield onHeaderAdded;
ok(document.querySelector(`#requests-list-${column}-button`),
`Column ${column} should be visible`);
}
});

View File

@ -6,7 +6,6 @@
/**
* Tests reset column menu item
*/
add_task(function* () {
let { monitor } = yield initNetMonitor(SIMPLE_URL);
info("Starting test... ");
@ -16,8 +15,8 @@ add_task(function* () {
let prefBefore = Prefs.visibleColumns;
hideColumn("status");
hideColumn("waterfall");
hideColumn(monitor, "status");
hideColumn(monitor, "waterfall");
EventUtils.sendMouseEvent({ type: "contextmenu" },
document.querySelector("#requests-list-contentSize-button"));
@ -26,17 +25,4 @@ add_task(function* () {
ok(JSON.stringify(prefBefore) === JSON.stringify(Prefs.visibleColumns),
"Reset columns item should reset columns pref");
function* hideColumn(column) {
info(`Clicking context-menu item for ${column}`);
EventUtils.sendMouseEvent({ type: "contextmenu" },
document.querySelector("#requests-list-contentSize-button"));
let onHeaderRemoved = waitForDOM(document, `#requests-list-${column}-button`, 0);
parent.document.querySelector(`#request-list-header-${column}-toggle`).click();
yield onHeaderRemoved;
ok(!document.querySelector(`#requests-list-${column}-button`),
`Column ${column} should be hidden`);
}
});

View File

@ -0,0 +1,49 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests for timings columns.
*/
add_task(async function () {
let { tab, monitor } = await initNetMonitor(SIMPLE_URL);
info("Starting test... ");
let { document, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
hideColumn(monitor, "waterfall");
showColumn(monitor, "endTime");
showColumn(monitor, "responseTime");
showColumn(monitor, "duration");
showColumn(monitor, "latency");
let onNetworkEvents = waitForNetworkEvents(monitor, 1);
let onEventTimings = waitFor(monitor.panelWin, EVENTS.RECEIVED_EVENT_TIMINGS);
tab.linkedBrowser.reload();
await Promise.all([onNetworkEvents, onEventTimings]);
// There should be one request in the list.
let requestItems = document.querySelectorAll(".request-list-item");
is(requestItems.length, 1, "There must be one visible item");
let item = requestItems[0];
let types = [
"end",
"response",
"duration",
"latency"
];
for (let t of types) {
await waitUntil(() => {
let node = item.querySelector(".requests-list-" + t + "-time");
let value = parseInt(node.textContent, 10);
return value > 0;
});
}
await teardown(monitor);
});

View File

@ -5,7 +5,7 @@
/* exported Toolbox, restartNetMonitor, teardown, waitForExplicitFinish,
verifyRequestItemTarget, waitFor, testFilterButtons, loadCommonFrameScript,
performRequestsInContent, waitForNetworkEvents, selectIndexAndWaitForSourceEditor,
testColumnsAlignment */
testColumnsAlignment, hideColumn, showColumn */
"use strict";
@ -475,16 +475,16 @@ function* verifyRequestItemTarget(document, requestList, requestItem, method,
is(target.querySelector(".requests-list-scheme").getAttribute("title"),
scheme, "The tooltip scheme is correct.");
is(target.querySelector(".requests-list-duration").textContent,
is(target.querySelector(".requests-list-duration-time").textContent,
duration, "The displayed duration is correct.");
is(target.querySelector(".requests-list-duration").getAttribute("title"),
is(target.querySelector(".requests-list-duration-time").getAttribute("title"),
duration, "The tooltip duration is correct.");
is(target.querySelector(".requests-list-latency").textContent,
is(target.querySelector(".requests-list-latency-time").textContent,
latency, "The displayed latency is correct.");
is(target.querySelector(".requests-list-latency").getAttribute("title"),
is(target.querySelector(".requests-list-latency-time").getAttribute("title"),
latency, "The tooltip latency is correct.");
if (status !== undefined) {
@ -706,6 +706,36 @@ function testColumnsAlignment(headers, requestList) {
}
}
function* hideColumn(monitor, column) {
let { document, parent } = monitor.panelWin;
info(`Clicking context-menu item for ${column}`);
EventUtils.sendMouseEvent({ type: "contextmenu" },
document.querySelector(".devtools-toolbar.requests-list-headers"));
let onHeaderRemoved = waitForDOM(document, `#requests-list-${column}-button`, 0);
parent.document.querySelector(`#request-list-header-${column}-toggle`).click();
yield onHeaderRemoved;
ok(!document.querySelector(`#requests-list-${column}-button`),
`Column ${column} should be hidden`);
}
function* showColumn(monitor, column) {
let { document, parent } = monitor.panelWin;
info(`Clicking context-menu item for ${column}`);
EventUtils.sendMouseEvent({ type: "contextmenu" },
document.querySelector(".devtools-toolbar.requests-list-headers"));
let onHeaderAdded = waitForDOM(document, `#requests-list-${column}-button`, 1);
parent.document.querySelector(`#request-list-header-${column}-toggle`).click();
yield onHeaderAdded;
ok(document.querySelector(`#requests-list-${column}-button`),
`Column ${column} should be visible`);
}
/**
* Select a request and switch to its response panel.
*

View File

@ -12,17 +12,7 @@ add_task(async function () {
let toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
let hud = toolbox.getCurrentPanel().hud;
const store = hud.ui.newConsoleOutput.getStore();
// Adding logging each time the store is modified in order to check
// the store state in case of failure.
store.subscribe(() => {
const messages = store.getState().messages.messagesById
.reduce(function (res, {id, type, parameters, messageText}) {
res.push({id, type, parameters, messageText});
return res;
}, []);
info("messages : " + JSON.stringify(messages));
});
logAllStoreChanges(hud);
info("console.dir on an array");
await ContentTask.spawn(gBrowser.selectedBrowser, null, function () {

View File

@ -13,23 +13,8 @@ const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/
add_task(async function () {
const hud = await openNewTabAndConsole(TEST_URI);
const store = hud.ui.newConsoleOutput.getStore();
// Adding logging each time the store is modified in order to check
// the store state in case of failure.
store.subscribe(() => {
const messages = [...store.getState().messages.messagesById]
.reduce(function (res, [id, message]) {
res.push({
id,
type: message.type,
parameters: message.parameters,
messageText: message.messageText
});
return res;
}, []);
info("messages : " + JSON.stringify(messages));
});
logAllStoreChanges(hud);
const onMessagesLogged = waitForMessage(hud, "log-6");
ContentTask.spawn(gBrowser.selectedBrowser, null, function () {

View File

@ -12,17 +12,7 @@ add_task(async function () {
let toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
let hud = toolbox.getCurrentPanel().hud;
const store = hud.ui.newConsoleOutput.getStore();
// Adding logging each time the store is modified in order to check
// the store state in case of failure.
store.subscribe(() => {
const messages = store.getState().messages.messagesById
.reduce(function (res, {id, type, parameters, messageText}) {
res.push({id, type, parameters, messageText});
return res;
}, []);
info("messages : " + JSON.stringify(messages));
});
logAllStoreChanges(hud);
await ContentTask.spawn(gBrowser.selectedBrowser, null, function () {
content.wrappedJSObject.console.log(

View File

@ -11,17 +11,8 @@ const {ELLIPSIS} = require("devtools/shared/l10n");
add_task(async function () {
const hud = await openNewTabAndConsole(TEST_URI);
const store = hud.ui.newConsoleOutput.getStore();
// Adding logging each time the store is modified in order to check
// the store state in case of failure.
store.subscribe(() => {
const messages = store.getState().messages.messagesById
.reduce(function (res, {id, type, parameters, messageText}) {
res.push({id, type, parameters, messageText});
return res;
}, []);
info("messages : " + JSON.stringify(messages));
});
logAllStoreChanges(hud);
await ContentTask.spawn(gBrowser.selectedBrowser, null, function () {
content.wrappedJSObject.console.log(

View File

@ -75,6 +75,26 @@ async function openNewTabAndConsole(url, clearJstermHistory = true) {
return hud;
}
/**
* Subscribe to the store and log out stringinfied versions of messages.
* This is a helper function for debugging, to make is easier to see what
* happened during the test in the log.
*
* @param object hud
*/
function logAllStoreChanges(hud) {
const store = hud.ui.newConsoleOutput.getStore();
// Adding logging each time the store is modified in order to check
// the store state in case of failure.
store.subscribe(() => {
const messages = [...store.getState().messages.messagesById.values()];
const debugMessages = messages.map(({id, type, parameters, messageText}) => {
return {id, type, parameters, messageText};
});
info("messages : " + JSON.stringify(debugMessages));
});
}
/**
* Wait for messages in the web console output, resolving once they are received.
*

View File

@ -216,6 +216,47 @@ URLParams::Parse(const nsACString& aInput, ForEachIterator& aIterator)
return true;
}
class MOZ_STACK_CLASS ExtractURLParam final
: public URLParams::ForEachIterator
{
public:
explicit ExtractURLParam(const nsAString& aName, nsAString& aValue)
: mName(aName), mValue(aValue)
{}
bool URLParamsIterator(const nsAString& aName,
const nsAString& aValue) override
{
if (mName == aName) {
mValue = aValue;
return false;
}
return true;
}
private:
const nsAString& mName;
nsAString& mValue;
};
/**
* Extracts the first form-urlencoded parameter named `aName` from `aInput`.
* @param aRange The input to parse.
* @param aName The name of the parameter to extract.
* @param aValue The value of the extracted parameter, void if not found.
* @return Whether the parameter was found in the form-urlencoded.
*/
/* static */ bool
URLParams::Extract(const nsACString& aInput,
const nsAString& aName,
nsAString& aValue)
{
aValue.SetIsVoid(true);
ExtractURLParam iterator(aName, aValue);
return !URLParams::Parse(aInput, iterator);
}
class MOZ_STACK_CLASS PopulateIterator final
: public URLParams::ForEachIterator
{

View File

@ -53,6 +53,9 @@ public:
static bool
Parse(const nsACString& aInput, ForEachIterator& aIterator);
static bool
Extract(const nsACString& aInput, const nsAString& aName, nsAString& aValue);
void
ParseInput(const nsACString& aInput);

View File

@ -632,8 +632,6 @@ URLWorker::Init(const nsAString& aURL, const Optional<nsAString>& aBase,
if (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) {
nsCOMPtr<nsIURI> baseURL;
if (aBase.WasPassed()) {
baseURL = new nsStandardURL();
// XXXcatalinb: SetSpec only writes a warning to the console on urls
// without a valid scheme. I can't fix that because we've come to rely
// on that behaviour in a bunch of different places.
@ -649,9 +647,17 @@ URLWorker::Init(const nsAString& aURL, const Optional<nsAString>& aBase,
return;
}
}
mStdURL = new nsStandardURL();
aRv = mStdURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1,
NS_ConvertUTF16toUTF8(aURL), nullptr, baseURL);
nsCOMPtr<nsIURI> uri;
rv = NS_MutateURI(new nsStandardURL::Mutator())
.Apply<nsIStandardURLMutator>(&nsIStandardURLMutator::Init,
nsIStandardURL::URLTYPE_STANDARD, -1,
NS_ConvertUTF16toUTF8(aURL),
nullptr, baseURL, nullptr)
.Finalize(uri);
aRv = rv;
if (NS_SUCCEEDED(rv)) {
mStdURL = static_cast<nsStandardURL*>(uri.get());
}
return;
}

View File

@ -175,4 +175,4 @@ Troubleshooting tips:
-------------------------------------------------------------------------------
The version of WebRender currently in the tree is:
e9269c7e06e20363be0b2a2a1be98d292ff7acca
c0943271eb8c6440a61db37e2f1e84201dcac2e3

View File

@ -19,7 +19,7 @@ bincode = "0.9"
byteorder = "1.0"
euclid = "0.16"
fxhash = "0.2.1"
gleam = "0.4.19"
gleam = "0.4.20"
lazy_static = "1"
log = "0.3"
num-traits = "0.1.32"
@ -29,6 +29,7 @@ webrender_api = {path = "../webrender_api"}
bitflags = "1.0"
thread_profiler = "0.1.1"
plane-split = "0.7"
png = { optional = true, version = "0.11" }
smallvec = "0.6"
ws = { optional = true, version = "0.7.3" }
serde_json = { optional = true, version = "1.0" }

View File

@ -51,10 +51,7 @@ impl webrender::OutputImageHandler for OutputHandler {
impl webrender::ExternalImageHandler for ExternalHandler {
fn lock(&mut self, _key: ExternalImageId, _channel_index: u8) -> webrender::ExternalImage {
webrender::ExternalImage {
u0: 0.0,
v0: 0.0,
u1: 1.0,
v1: 1.0,
uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
source: webrender::ExternalImageSource::NativeTexture(self.texture_id),
}
}
@ -77,7 +74,7 @@ impl App {
ImageData::External(ExternalImageData {
id: ExternalImageId(0),
channel_index: 0,
image_type: ExternalImageType::Texture2DHandle,
image_type: ExternalImageType::TextureHandle(TextureTarget::Default),
}),
None,
);

View File

@ -21,7 +21,7 @@ struct ImageGenerator {
}
impl ImageGenerator {
fn new() -> ImageGenerator {
fn new() -> Self {
ImageGenerator {
next_pattern: 0,
patterns: [
@ -63,10 +63,7 @@ impl webrender::ExternalImageHandler for ImageGenerator {
fn lock(&mut self, _key: ExternalImageId, channel_index: u8) -> webrender::ExternalImage {
self.generate_image(channel_index as u32);
webrender::ExternalImage {
u0: 0.0,
v0: 0.0,
u1: 1.0,
v1: 1.0,
uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
source: webrender::ExternalImageSource::RawData(&self.current_image),
}
}
@ -245,7 +242,7 @@ impl Example for App {
let image_data = ExternalImageData {
id: ExternalImageId(0),
channel_index: size as u8,
image_type: ExternalImageType::ExternalBuffer,
image_type: ExternalImageType::Buffer,
};
updates.add_image(

View File

@ -63,13 +63,13 @@ void brush_vs(
vec2 uv1 = uv0 + blur_task.common_data.task_rect.size;
#else
Picture pic = fetch_picture(prim_address);
ImageResource uv_rect = fetch_image_resource(user_data.x);
ImageResource res = fetch_image_resource(user_data.x);
vec2 texture_size = vec2(textureSize(sColor1, 0).xy);
vColor = pic.color;
vec2 uv0 = uv_rect.uv_rect.xy;
vec2 uv1 = uv_rect.uv_rect.zw;
vec2 src_size = (uv1 - uv0) * uv_rect.user_data.x;
vUv.z = uv_rect.layer;
vec2 uv0 = res.uv_rect.p0;
vec2 uv1 = res.uv_rect.p1;
vec2 src_size = (uv1 - uv0) * res.user_data.x;
vUv.z = res.layer;
#endif
// TODO(gw): In the future we'll probably draw these as segments

View File

@ -38,9 +38,9 @@ void main(void) {
vClipMaskUv = vec3((vPos.xy / vPos.z - local_rect.p0) / local_rect.size, 0.0);
vec2 texture_size = vec2(textureSize(sColor0, 0));
vClipMaskUvRect = vec4(res.uv_rect.xy, res.uv_rect.zw - res.uv_rect.xy) / texture_size.xyxy;
vClipMaskUvRect = vec4(res.uv_rect.p0, res.uv_rect.p1 - res.uv_rect.p0) / texture_size.xyxy;
// applying a half-texel offset to the UV boundaries to prevent linear samples from the outside
vec4 inner_rect = vec4(res.uv_rect.xy, res.uv_rect.zw);
vec4 inner_rect = vec4(res.uv_rect.p0, res.uv_rect.p1);
vClipMaskUvInnerRect = (inner_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
}
#endif

View File

@ -695,7 +695,7 @@ GlyphResource fetch_glyph_resource(int address) {
}
struct ImageResource {
vec4 uv_rect;
RectWithEndpoint uv_rect;
float layer;
vec3 user_data;
};
@ -703,12 +703,14 @@ struct ImageResource {
ImageResource fetch_image_resource(int address) {
//Note: number of blocks has to match `renderer::BLOCKS_PER_UV_RECT`
vec4 data[2] = fetch_from_resource_cache_2(address);
return ImageResource(data[0], data[1].x, data[1].yzw);
RectWithEndpoint uv_rect = RectWithEndpoint(data[0].xy, data[0].zw);
return ImageResource(uv_rect, data[1].x, data[1].yzw);
}
ImageResource fetch_image_resource_direct(ivec2 address) {
vec4 data[2] = fetch_from_resource_cache_2_direct(address);
return ImageResource(data[0], data[1].x, data[1].yzw);
RectWithEndpoint uv_rect = RectWithEndpoint(data[0].xy, data[0].zw);
return ImageResource(uv_rect, data[1].x, data[1].yzw);
}
struct TextRun {
@ -725,12 +727,11 @@ TextRun fetch_text_run(int address) {
struct Image {
vec4 stretch_size_and_tile_spacing; // Size of the actual image and amount of space between
// tiled instances of this image.
vec4 sub_rect; // If negative, ignored.
};
Image fetch_image(int address) {
vec4 data[2] = fetch_from_resource_cache_2(address);
return Image(data[0], data[1]);
vec4 data = fetch_from_resource_cache_1(address);
return Image(data);
}
void write_clip(vec2 global_pos, ClipArea area) {

View File

@ -50,15 +50,8 @@ void main(void) {
vec2 texture_size_normalization_factor = vec2(textureSize(sColor0, 0));
#endif
vec2 uv0, uv1;
if (image.sub_rect.x < 0.0) {
uv0 = res.uv_rect.xy;
uv1 = res.uv_rect.zw;
} else {
uv0 = res.uv_rect.xy + image.sub_rect.xy;
uv1 = res.uv_rect.xy + image.sub_rect.zw;
}
vec2 uv0 = res.uv_rect.p0;
vec2 uv1 = res.uv_rect.p1;
// vUv will contain how many times this image has wrapped around the image size.
vec2 st0 = uv0 / texture_size_normalization_factor;

View File

@ -70,8 +70,8 @@ void main(void) {
#else
vec2 y_texture_size_normalization_factor = vec2(textureSize(sColor0, 0));
#endif
vec2 y_st0 = y_rect.uv_rect.xy / y_texture_size_normalization_factor;
vec2 y_st1 = y_rect.uv_rect.zw / y_texture_size_normalization_factor;
vec2 y_st0 = y_rect.uv_rect.p0 / y_texture_size_normalization_factor;
vec2 y_st1 = y_rect.uv_rect.p1 / y_texture_size_normalization_factor;
vTextureSizeY = y_st1 - y_st0;
vTextureOffsetY = y_st0;
@ -83,11 +83,11 @@ void main(void) {
#else
vec2 uv_texture_size_normalization_factor = vec2(textureSize(sColor1, 0));
#endif
vec2 u_st0 = u_rect.uv_rect.xy / uv_texture_size_normalization_factor;
vec2 u_st1 = u_rect.uv_rect.zw / uv_texture_size_normalization_factor;
vec2 u_st0 = u_rect.uv_rect.p0 / uv_texture_size_normalization_factor;
vec2 u_st1 = u_rect.uv_rect.p1 / uv_texture_size_normalization_factor;
#ifndef WR_FEATURE_NV12
vec2 v_st0 = v_rect.uv_rect.xy / uv_texture_size_normalization_factor;
vec2 v_st0 = v_rect.uv_rect.p0 / uv_texture_size_normalization_factor;
#endif
vTextureSizeUv = u_st1 - u_st0;

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{AlphaType, DeviceIntRect, DeviceIntSize, ImageKey, LayerToWorldScale};
use api::{ExternalImageType, FilterOp, ImageRendering, LayerRect};
use api::{DeviceUintRect, DeviceUintPoint, DeviceUintSize, ExternalImageType, FilterOp, ImageRendering, LayerRect};
use api::{SubpixelDirection, TileOffset, YuvColorSpace, YuvFormat};
use api::{LayerToWorldTransform, WorldPixel};
use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind};
@ -11,21 +11,21 @@ use clip::{ClipSource, ClipStore};
use clip_scroll_tree::{CoordinateSystemId};
use euclid::{TypedTransform3D, vec3};
use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use gpu_cache::{GpuCache, GpuCacheAddress};
use gpu_types::{BrushImageKind, BrushInstance, ClipChainRectIndex};
use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex, PictureType};
use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance};
use internal_types::{FastHashMap, SourceTexture};
use picture::{PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface};
use plane_split::{BspSplitter, Polygon, Splitter};
use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
use prim_store::{ImageSource, PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, EdgeAaSegmentMask, PrimitiveRun};
use render_task::{ClipWorkItem};
use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind};
use render_task::{RenderTaskTree};
use renderer::{BlendMode, ImageBufferKind};
use renderer::BLOCKS_PER_UV_RECT;
use resource_cache::{GlyphFetchResult, ResourceCache};
use resource_cache::{CacheItem, GlyphFetchResult, ResourceCache};
use std::{usize, f32, i32};
use tiling::{RenderTargetContext, RenderTargetKind};
use util::{MatrixHelpers, TransformedRectKind};
@ -547,6 +547,24 @@ impl AlphaBatcher {
}
}
fn get_buffer_kind(texture: SourceTexture) -> ImageBufferKind {
match texture {
SourceTexture::External(ext_image) => {
match ext_image.image_type {
ExternalImageType::TextureHandle(target) => {
target.into()
}
ExternalImageType::Buffer => {
// The ExternalImageType::Buffer should be handled by resource_cache.
// It should go through the non-external case.
panic!("Unexpected non-texture handle type");
}
}
}
_ => ImageBufferKind::Texture2DArray,
}
}
// Adds a primitive to a batch.
// It can recursively call itself in some situations, for
// example if it encounters a picture where the items
@ -677,62 +695,41 @@ impl AlphaBatcher {
PrimitiveKind::Image => {
let image_cpu = &ctx.prim_store.cpu_images[prim_metadata.cpu_prim_index.0];
let (color_texture_id, uv_address) = resolve_image(
image_cpu.image_key,
image_cpu.image_rendering,
image_cpu.tile_offset,
let cache_item = match image_cpu.source {
ImageSource::Default => {
resolve_image(
image_cpu.key.image_key,
image_cpu.key.image_rendering,
image_cpu.key.tile_offset,
ctx.resource_cache,
gpu_cache,
deferred_resolves,
);
)
}
ImageSource::Cache { ref item, .. } => {
item.clone()
}
};
if color_texture_id == SourceTexture::Invalid {
if cache_item.texture_id == SourceTexture::Invalid {
warn!("Warnings: skip a PrimitiveKind::Image at {:?}.\n", item_bounding_rect);
return;
}
let batch_kind = match color_texture_id {
SourceTexture::External(ext_image) => {
match ext_image.image_type {
ExternalImageType::Texture2DHandle => {
TransformBatchKind::Image(ImageBufferKind::Texture2D)
}
ExternalImageType::Texture2DArrayHandle => {
TransformBatchKind::Image(ImageBufferKind::Texture2DArray)
}
ExternalImageType::TextureRectHandle => {
TransformBatchKind::Image(ImageBufferKind::TextureRect)
}
ExternalImageType::TextureExternalHandle => {
TransformBatchKind::Image(ImageBufferKind::TextureExternal)
}
ExternalImageType::ExternalBuffer => {
// The ExternalImageType::ExternalBuffer should be handled by resource_cache.
// It should go through the non-external case.
panic!(
"Non-texture handle type should be handled in other way"
);
}
}
}
_ => TransformBatchKind::Image(ImageBufferKind::Texture2DArray),
};
let textures = BatchTextures {
colors: [
color_texture_id,
SourceTexture::Invalid,
SourceTexture::Invalid,
],
};
let batch_kind = TransformBatchKind::Image(Self::get_buffer_kind(cache_item.texture_id));
let key = BatchKey::new(
BatchKind::Transformable(transform_kind, batch_kind),
blend_mode,
textures,
BatchTextures {
colors: [
cache_item.texture_id,
SourceTexture::Invalid,
SourceTexture::Invalid,
],
},
);
let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
batch.push(base_instance.build(uv_address.as_int(gpu_cache), 0, 0));
batch.push(base_instance.build(cache_item.uv_rect_handle.as_int(gpu_cache), 0, 0));
}
PrimitiveKind::TextRun => {
let text_cpu =
@ -1164,7 +1161,7 @@ impl AlphaBatcher {
for channel in 0 .. channel_count {
let image_key = image_yuv_cpu.yuv_key[channel];
let (texture, address) = resolve_image(
let cache_item = resolve_image(
image_key,
image_yuv_cpu.image_rendering,
None,
@ -1173,48 +1170,21 @@ impl AlphaBatcher {
deferred_resolves,
);
if texture == SourceTexture::Invalid {
if cache_item.texture_id == SourceTexture::Invalid {
warn!("Warnings: skip a PrimitiveKind::YuvImage at {:?}.\n", item_bounding_rect);
return;
}
textures.colors[channel] = texture;
uv_rect_addresses[channel] = address.as_int(gpu_cache);
textures.colors[channel] = cache_item.texture_id;
uv_rect_addresses[channel] = cache_item.uv_rect_handle.as_int(gpu_cache);
}
let get_buffer_kind = |texture: SourceTexture| {
match texture {
SourceTexture::External(ext_image) => {
match ext_image.image_type {
ExternalImageType::Texture2DHandle => {
ImageBufferKind::Texture2D
}
ExternalImageType::Texture2DArrayHandle => {
ImageBufferKind::Texture2DArray
}
ExternalImageType::TextureRectHandle => {
ImageBufferKind::TextureRect
}
ExternalImageType::TextureExternalHandle => {
ImageBufferKind::TextureExternal
}
ExternalImageType::ExternalBuffer => {
// The ExternalImageType::ExternalBuffer should be handled by resource_cache.
// It should go through the non-external case.
panic!("Unexpected non-texture handle type");
}
}
}
_ => ImageBufferKind::Texture2DArray,
}
};
// All yuv textures should be the same type.
let buffer_kind = get_buffer_kind(textures.colors[0]);
let buffer_kind = Self::get_buffer_kind(textures.colors[0]);
assert!(
textures.colors[1 .. image_yuv_cpu.format.get_plane_num()]
.iter()
.all(|&tid| buffer_kind == get_buffer_kind(tid))
.all(|&tid| buffer_kind == Self::get_buffer_kind(tid))
);
let kind = BatchKind::Transformable(
@ -1399,14 +1369,14 @@ impl AlphaBatchHelpers for PrimitiveStore {
}
}
fn resolve_image(
pub fn resolve_image(
image_key: ImageKey,
image_rendering: ImageRendering,
tile_offset: Option<TileOffset>,
resource_cache: &ResourceCache,
gpu_cache: &mut GpuCache,
deferred_resolves: &mut Vec<DeferredResolve>,
) -> (SourceTexture, GpuCacheHandle) {
) -> CacheItem {
match resource_cache.get_image_properties(image_key) {
Some(image_properties) => {
// Check if an external image that needs to be resolved
@ -1417,24 +1387,39 @@ fn resolve_image(
// the deferred resolves list to be patched by
// the render thread...
let cache_handle = gpu_cache.push_deferred_per_frame_blocks(BLOCKS_PER_UV_RECT);
let cache_item = CacheItem {
texture_id: SourceTexture::External(external_image),
uv_rect_handle: cache_handle,
uv_rect: DeviceUintRect::new(
DeviceUintPoint::zero(),
DeviceUintSize::new(
image_properties.descriptor.width,
image_properties.descriptor.height,
)
),
texture_layer: 0,
};
deferred_resolves.push(DeferredResolve {
image_properties,
address: gpu_cache.get_address(&cache_handle),
});
(SourceTexture::External(external_image), cache_handle)
cache_item
}
None => {
if let Ok(cache_item) = resource_cache.get_cached_image(image_key, image_rendering, tile_offset) {
(cache_item.texture_id, cache_item.uv_rect_handle)
cache_item
} else {
// There is no usable texture entry for the image key. Just return an invalid texture here.
(SourceTexture::Invalid, GpuCacheHandle::new())
CacheItem::invalid()
}
}
}
}
None => (SourceTexture::Invalid, GpuCacheHandle::new()),
None => {
CacheItem::invalid()
}
}
}

View File

@ -3,14 +3,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ColorF};
use api::{LayerPoint, LayerRect};
use api::{LayerPrimitiveInfo, LayerSize, NormalBorder, RepeatMode};
use api::{LayerPoint, LayerRect, LayerPrimitiveInfo, LayerSize};
use api::{NormalBorder, RepeatMode, TexelRect};
use clip::ClipSource;
use ellipse::Ellipse;
use frame_builder::FrameBuilder;
use gpu_cache::GpuDataRequest;
use prim_store::{BorderPrimitiveCpu, BrushSegment, BrushSegmentDescriptor};
use prim_store::{BrushClipMaskKind, EdgeAaSegmentMask, PrimitiveContainer, TexelRect};
use prim_store::{BrushClipMaskKind, EdgeAaSegmentMask, PrimitiveContainer};
use util::{lerp, pack_as_float};
#[repr(u8)]

View File

@ -6,7 +6,9 @@ use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use api::{CaptureBits, ExternalImageData, ImageDescriptor};
use api::{CaptureBits, ExternalImageData, ExternalImageId, ImageDescriptor, TexelRect};
#[cfg(feature = "png")]
use device::ReadPixelsFormat;
use ron::{de, ser};
use serde::{Deserialize, Serialize};
@ -58,11 +60,62 @@ impl CaptureConfig {
Some(de::from_str(&string)
.unwrap())
}
#[cfg(feature = "png")]
pub fn save_png(
path: PathBuf, size: (u32, u32), format: ReadPixelsFormat, data: &[u8],
) {
use api::ImageFormat;
use png::{BitDepth, ColorType, Encoder, HasParameters};
use std::io::BufWriter;
let color_type = match format {
ReadPixelsFormat::Rgba8 => ColorType::RGBA,
ReadPixelsFormat::Standard(ImageFormat::BGRA8) => {
warn!("Unable to swizzle PNG of BGRA8 type");
ColorType::RGBA
},
ReadPixelsFormat::Standard(ImageFormat::R8) => ColorType::Grayscale,
ReadPixelsFormat::Standard(ImageFormat::RG8) => ColorType::GrayscaleAlpha,
ReadPixelsFormat::Standard(fm) => {
error!("Unable to save PNG of {:?}", fm);
return;
}
};
let w = BufWriter::new(File::create(path).unwrap());
let mut enc = Encoder::new(w, size.0, size.1);
enc
.set(color_type)
.set(BitDepth::Eight);
enc
.write_header()
.unwrap()
.write_image_data(&data)
.unwrap();
}
}
/// An image that `ResourceCache` is unable to resolve during a capture.
/// The image has to be transferred to `Renderer` and locked with the
/// external image handler to get the actual contents and serialize them.
#[derive(Deserialize, Serialize)]
pub struct ExternalCaptureImage {
pub short_path: String,
pub descriptor: ImageDescriptor,
pub external: ExternalImageData,
}
/// A short description of an external image to be saved separately as
/// "externals/XX.ron", redirecting into a specific texture/blob with
/// the corresponding UV rectangle.
#[derive(Deserialize, Serialize)]
pub struct PlainExternalImage {
/// Path to the RON file describing the texel data.
pub data: String,
/// Public ID of the external image.
pub id: ExternalImageId,
/// Channel index of an external image.
pub channel_index: u8,
/// UV sub-rectangle of the image.
pub uv: TexelRect,
}

View File

@ -2,10 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ColorU, DeviceIntRect, DeviceUintSize, ImageFormat};
use api::{ColorU, DeviceIntRect, DeviceUintSize, ImageFormat, TextureTarget};
use debug_font_data;
use device::{Device, Program, Texture, TextureSlot, VertexDescriptor, VAO};
use device::{TextureFilter, TextureTarget, VertexAttribute, VertexAttributeKind, VertexUsageHint};
use device::{TextureFilter, VertexAttribute, VertexAttributeKind, VertexUsageHint};
use euclid::{Point2D, Rect, Size2D, Transform3D};
use internal_types::{ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE};
use std::f32;

View File

@ -5,6 +5,7 @@
use super::shader_source;
use api::{ColorF, ImageDescriptor, ImageFormat};
use api::{DeviceIntPoint, DeviceIntRect, DeviceUintRect, DeviceUintSize};
use api::TextureTarget;
use euclid::Transform3D;
use gleam::gl;
use internal_types::{FastHashMap, RenderTargetInfo};
@ -26,7 +27,7 @@ use std::thread;
pub struct FrameId(usize);
impl FrameId {
pub fn new(value: usize) -> FrameId {
pub fn new(value: usize) -> Self {
FrameId(value)
}
}
@ -62,25 +63,6 @@ pub enum DepthFunction {
LessEqual = gl::LEQUAL,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum TextureTarget {
Default,
Array,
Rect,
External,
}
impl TextureTarget {
pub fn to_gl_target(&self) -> gl::GLuint {
match *self {
TextureTarget::Default => gl::TEXTURE_2D,
TextureTarget::Array => gl::TEXTURE_2D_ARRAY,
TextureTarget::Rect => gl::TEXTURE_RECTANGLE,
TextureTarget::External => gl::TEXTURE_EXTERNAL_OES,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))]
pub enum TextureFilter {
@ -130,6 +112,15 @@ pub enum ReadPixelsFormat {
Rgba8,
}
pub fn get_gl_target(target: TextureTarget) -> gl::GLuint {
match target {
TextureTarget::Default => gl::TEXTURE_2D,
TextureTarget::Array => gl::TEXTURE_2D_ARRAY,
TextureTarget::Rect => gl::TEXTURE_RECTANGLE,
TextureTarget::External => gl::TEXTURE_EXTERNAL_OES,
}
}
pub fn get_gl_format_bgra(gl: &gl::Gl) -> gl::GLuint {
match gl.get_type() {
gl::GlType::Gl => GL_FORMAT_BGRA_GL,
@ -423,9 +414,14 @@ impl ExternalTexture {
pub fn new(id: u32, target: TextureTarget) -> Self {
ExternalTexture {
id,
target: target.to_gl_target(),
target: get_gl_target(target),
}
}
#[cfg(feature = "capture")]
pub fn internal_id(&self) -> gl::GLuint {
self.id
}
}
pub struct Texture {
@ -930,7 +926,7 @@ impl Device {
) -> Texture {
Texture {
id: self.gl.gen_textures(1)[0],
target: target.to_gl_target(),
target: get_gl_target(target),
width: 0,
height: 0,
layer_count: 0,
@ -1511,7 +1507,7 @@ impl Device {
)
}
/// Read rectangle of RGBA8 or BGRA8 pixels into the specified output slice.
/// Read rectangle of pixels into the specified output slice.
pub fn read_pixels_into(
&mut self,
rect: DeviceUintRect,
@ -1545,6 +1541,24 @@ impl Device {
);
}
/// Get texels of a texture into the specified output slice.
pub fn get_tex_image_into(
&mut self,
texture: &Texture,
format: ImageFormat,
output: &mut [u8],
) {
self.bind_texture(DEFAULT_TEXTURE, texture);
let desc = gl_describe_format(self.gl(), format);
self.gl.get_tex_image_into_buffer(
texture.target,
0,
desc.external,
desc.pixel_type,
output,
);
}
/// Attaches the provided texture to the current Read FBO binding.
fn attach_read_texture_raw(
&mut self, texture_id: gl::GLuint, target: gl::GLuint, layer_id: i32
@ -1575,7 +1589,7 @@ impl Device {
pub fn attach_read_texture_external(
&mut self, texture_id: gl::GLuint, target: TextureTarget, layer_id: i32
) {
self.attach_read_texture_raw(texture_id, target.to_gl_target(), layer_id)
self.attach_read_texture_raw(texture_id, get_gl_target(target), layer_id)
}
pub fn attach_read_texture(&mut self, texture: &Texture, layer_id: i32) {

View File

@ -419,8 +419,8 @@ impl<'a> FlattenContext<'a> {
self.builder.add_image(
clip_and_scroll,
&prim_info,
&info.stretch_size,
&info.tile_spacing,
info.stretch_size,
info.tile_spacing,
None,
info.image_key,
info.image_rendering,
@ -934,8 +934,8 @@ impl<'a> FlattenContext<'a> {
self.builder.add_image(
clip_and_scroll,
&prim_info,
&stretched_size,
&info.tile_spacing,
stretched_size,
info.tile_spacing,
None,
info.image_key,
info.image_rendering,
@ -1101,6 +1101,8 @@ impl FrameContext {
RenderedDocument::new(self.pipeline_epoch_map.clone(), nodes_bouncing_back, frame)
}
//TODO: this can probably be simplified if `build()` is called directly by RB.
// The only things it needs from the frame context is the CST and frame ID.
pub fn build_rendered_document(
&mut self,
frame_builder: &mut FrameBuilder,

View File

@ -6,11 +6,11 @@ use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayList, ClipAnd
use api::{ColorF, ColorU, DeviceIntPoint, DevicePixelScale, DeviceUintPoint, DeviceUintRect};
use api::{DeviceUintSize, DocumentLayer, ExtendMode, FontRenderMode, GlyphInstance, GlyphOptions};
use api::{GradientStop, HitTestFlags, HitTestItem, HitTestResult, ImageKey, ImageRendering};
use api::{ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize};
use api::{Epoch, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize};
use api::{LayerTransform, LayerVector2D, LayoutTransform, LayoutVector2D, LineOrientation};
use api::{LineStyle, LocalClip, PipelineId, PremultipliedColorF, PropertyBinding, RepeatMode};
use api::{ScrollSensitivity, Shadow, TileOffset, TransformStyle, WorldPoint, YuvColorSpace};
use api::YuvData;
use api::{ScrollSensitivity, Shadow, TexelRect, TileOffset, TransformStyle, WorldPoint};
use api::{DeviceIntRect, DeviceIntSize, YuvColorSpace, YuvData};
use app_units::Au;
use border::ImageBorderSegment;
use clip::{ClipRegion, ClipSource, ClipSources, ClipStore, Contains};
@ -23,8 +23,8 @@ use gpu_cache::GpuCache;
use gpu_types::{ClipScrollNodeData, PictureType};
use internal_types::{FastHashMap, FastHashSet, RenderPassIndex};
use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface};
use prim_store::{BrushKind, BrushPrimitive, TexelRect, YuvImagePrimitiveCpu};
use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, PrimitiveKind};
use prim_store::{BrushKind, BrushPrimitive, ImageCacheKey, YuvImagePrimitiveCpu};
use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, ImageSource, PrimitiveKind};
use prim_store::{PrimitiveContainer, PrimitiveIndex, SpecificPrimitiveIndex};
use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
use prim_store::{BrushSegmentDescriptor, TextRunPrimitiveCpu};
@ -1094,8 +1094,8 @@ impl FrameBuilder {
self.add_image(
clip_and_scroll,
&info,
&segment.stretch_size,
&segment.tile_spacing,
segment.stretch_size,
segment.tile_spacing,
Some(segment.sub_rect),
border.image_key,
ImageRendering::Auto,
@ -1422,41 +1422,45 @@ impl FrameBuilder {
&mut self,
clip_and_scroll: ClipAndScrollInfo,
info: &LayerPrimitiveInfo,
stretch_size: &LayerSize,
tile_spacing: &LayerSize,
stretch_size: LayerSize,
mut tile_spacing: LayerSize,
sub_rect: Option<TexelRect>,
image_key: ImageKey,
image_rendering: ImageRendering,
alpha_type: AlphaType,
tile: Option<TileOffset>,
tile_offset: Option<TileOffset>,
) {
let sub_rect_block = sub_rect.unwrap_or(TexelRect::invalid()).into();
// If the tile spacing is the same as the rect size,
// then it is effectively zero. We use this later on
// in prim_store to detect if an image can be considered
// opaque.
let tile_spacing = if *tile_spacing == info.rect.size {
LayerSize::zero()
} else {
*tile_spacing
};
if tile_spacing == info.rect.size {
tile_spacing = LayerSize::zero();
}
let prim_cpu = ImagePrimitiveCpu {
image_key,
image_rendering,
tile_offset: tile,
tile_spacing,
alpha_type,
gpu_blocks: [
[
stretch_size.width,
stretch_size.height,
tile_spacing.width,
tile_spacing.height,
].into(),
sub_rect_block,
],
stretch_size,
current_epoch: Epoch::invalid(),
source: ImageSource::Default,
key: ImageCacheKey {
image_key,
image_rendering,
tile_offset,
texel_rect: sub_rect.map(|texel_rect| {
DeviceIntRect::new(
DeviceIntPoint::new(
texel_rect.uv0.x as i32,
texel_rect.uv0.y as i32,
),
DeviceIntSize::new(
(texel_rect.uv1.x - texel_rect.uv0.x) as i32,
(texel_rect.uv1.y - texel_rect.uv0.y) as i32,
),
)
}),
},
};
self.add_primitive(
@ -1477,9 +1481,9 @@ impl FrameBuilder {
) {
let format = yuv_data.get_format();
let yuv_key = match yuv_data {
YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::dummy()],
YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
YuvData::PlanarYCbCr(plane_0, plane_1, plane_2) => [plane_0, plane_1, plane_2],
YuvData::InterleavedYCbCr(plane_0) => [plane_0, ImageKey::dummy(), ImageKey::dummy()],
YuvData::InterleavedYCbCr(plane_0) => [plane_0, ImageKey::DUMMY, ImageKey::DUMMY],
};
let prim_cpu = YuvImagePrimitiveCpu {

View File

@ -24,9 +24,9 @@
//! address in the GPU cache of a given resource slot
//! for this frame.
use api::{LayerRect, PremultipliedColorF};
use api::{PremultipliedColorF, TexelRect};
use device::FrameId;
use internal_types::UvRect;
use euclid::TypedRect;
use profiler::GpuCacheProfileCounters;
use renderer::MAX_VERTEX_TEXTURE_WIDTH;
use std::{mem, u16, u32};
@ -58,51 +58,50 @@ struct CacheLocation {
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))]
pub struct GpuBlockData {
pub data: [f32; 4],
data: [f32; 4],
}
impl GpuBlockData {
pub fn empty() -> Self {
GpuBlockData { data: [0.0; 4] }
}
pub const EMPTY: Self = GpuBlockData { data: [0.0; 4] };
}
/// Conversion helpers for GpuBlockData
impl Into<GpuBlockData> for PremultipliedColorF {
fn into(self) -> GpuBlockData {
impl From<PremultipliedColorF> for GpuBlockData {
fn from(c: PremultipliedColorF) -> Self {
GpuBlockData {
data: [self.r, self.g, self.b, self.a],
data: [c.r, c.g, c.b, c.a],
}
}
}
impl Into<GpuBlockData> for [f32; 4] {
fn into(self) -> GpuBlockData {
GpuBlockData { data: self }
impl From<[f32; 4]> for GpuBlockData {
fn from(data: [f32; 4]) -> Self {
GpuBlockData { data }
}
}
impl Into<GpuBlockData> for LayerRect {
fn into(self) -> GpuBlockData {
impl<P> From<TypedRect<f32, P>> for GpuBlockData {
fn from(r: TypedRect<f32, P>) -> Self {
GpuBlockData {
data: [
self.origin.x,
self.origin.y,
self.size.width,
self.size.height,
r.origin.x,
r.origin.y,
r.size.width,
r.size.height,
],
}
}
}
impl Into<GpuBlockData> for UvRect {
fn into(self) -> GpuBlockData {
impl From<TexelRect> for GpuBlockData {
fn from(tr: TexelRect) -> Self {
GpuBlockData {
data: [self.uv0.x, self.uv0.y, self.uv1.x, self.uv1.y],
data: [tr.uv0.x, tr.uv0.y, tr.uv1.x, tr.uv1.y],
}
}
}
// Any data type that can be stored in the GPU cache should
// implement this trait.
pub trait ToGpuBlocks {
@ -222,6 +221,7 @@ pub enum GpuCacheUpdate {
},
}
#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))]
pub struct GpuCacheUpdateList {
// The current height of the texture. The render thread
// should resize the texture if required.

View File

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ClipId, DevicePoint, DeviceUintRect, DocumentId, Epoch};
use api::{ClipId, DeviceUintRect, DocumentId, Epoch};
use api::{ExternalImageData, ExternalImageId};
use api::{ImageFormat, PipelineId};
use api::DebugCommand;
@ -17,7 +17,7 @@ use std::path::PathBuf;
use std::sync::Arc;
#[cfg(feature = "capture")]
use capture::{CaptureConfig, ExternalCaptureImage};
use capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
use tiling;
pub type FastHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>;
@ -154,7 +154,7 @@ pub enum DebugOutput {
#[cfg(feature = "capture")]
SaveCapture(CaptureConfig, Vec<ExternalCaptureImage>),
#[cfg(feature = "capture")]
LoadCapture(PathBuf),
LoadCapture(PathBuf, Vec<PlainExternalImage>),
}
pub enum ResultMsg {
@ -172,9 +172,3 @@ pub enum ResultMsg {
cancel_rendering: bool,
},
}
#[derive(Clone, Copy, Debug)]
pub struct UvRect {
pub uv0: DevicePoint,
pub uv1: DevicePoint,
}

View File

@ -158,6 +158,8 @@ extern crate ws;
extern crate image;
#[cfg(feature = "debugger")]
extern crate base64;
#[cfg(all(feature = "capture", feature = "png"))]
extern crate png;
pub extern crate webrender_api;

View File

@ -585,7 +585,7 @@ impl PicturePrimitive {
// the brush_picture shader is updated to draw
// segments, since the scale factor will not
// be used at all then during drawing.
(root_task_id, [scale_factor, 0.0, 0.0])
(root_task_id, [scale_factor, 0.0, 0.0], false)
}
);

View File

@ -3,12 +3,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipAndScrollInfo, ClipId, ClipMode};
use api::{ColorF, ColorU, DeviceIntRect, DevicePixelScale, DevicePoint};
use api::{ColorF, ColorU, DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch};
use api::{ComplexClipRegion, ExtendMode, FontRenderMode};
use api::{GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag};
use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D, LineOrientation};
use api::{LineStyle, PipelineId, PremultipliedColorF, TileOffset, WorldToLayerTransform};
use api::{YuvColorSpace, YuvFormat};
use api::{LineStyle, PipelineId, PremultipliedColorF, TileOffset};
use api::{WorldToLayerTransform, YuvColorSpace, YuvFormat};
use border::{BorderCornerInstance, BorderEdgeKind};
use clip_scroll_tree::{CoordinateSystemId, ClipScrollTree};
use clip_scroll_node::ClipScrollNode;
@ -21,10 +21,10 @@ use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuData
use gpu_types::{ClipChainRectIndex, ClipScrollNodeData};
use picture::{PictureKind, PicturePrimitive};
use profiler::FrameProfileCounters;
use render_task::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipWorkItem};
use render_task::{RenderTask, RenderTaskId, RenderTaskTree};
use renderer::{BLOCKS_PER_UV_RECT, MAX_VERTEX_TEXTURE_WIDTH};
use resource_cache::{ImageProperties, ResourceCache};
use render_task::{BlitSource, ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipWorkItem};
use render_task::{RenderTask, RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId, RenderTaskTree};
use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
use resource_cache::{CacheItem, ImageProperties, ResourceCache};
use scene::{ScenePipeline, SceneProperties};
use segment::SegmentBuilder;
use std::{mem, usize};
@ -88,41 +88,6 @@ pub struct PrimitiveRunLocalRect {
pub local_rect_in_original_parent_space: LayerRect,
}
/// Stores two coordinates in texel space. The coordinates
/// are stored in texel coordinates because the texture atlas
/// may grow. Storing them as texel coords and normalizing
/// the UVs in the vertex shader means nothing needs to be
/// updated on the CPU when the texture size changes.
#[derive(Copy, Clone, Debug)]
pub struct TexelRect {
pub uv0: DevicePoint,
pub uv1: DevicePoint,
}
impl TexelRect {
pub fn new(u0: f32, v0: f32, u1: f32, v1: f32) -> TexelRect {
TexelRect {
uv0: DevicePoint::new(u0, v0),
uv1: DevicePoint::new(u1, v1),
}
}
pub fn invalid() -> TexelRect {
TexelRect {
uv0: DevicePoint::new(-1.0, -1.0),
uv1: DevicePoint::new(-1.0, -1.0),
}
}
}
impl Into<GpuBlockData> for TexelRect {
fn into(self) -> GpuBlockData {
GpuBlockData {
data: [self.uv0.x, self.uv0.y, self.uv1.x, self.uv1.y],
}
}
}
/// For external images, it's not possible to know the
/// UV coords of the image (or the image data itself)
/// until the render thread receives the frame and issues
@ -131,6 +96,7 @@ impl Into<GpuBlockData> for TexelRect {
/// that is stored in the frame. This allows the render
/// thread to iterate this list and update any changed
/// texture data and update the UV rect.
#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))]
pub struct DeferredResolve {
pub address: GpuCacheAddress,
pub image_properties: ImageProperties,
@ -354,20 +320,49 @@ impl BrushPrimitive {
}
}
#[derive(Debug)]
pub struct ImagePrimitiveCpu {
// Key that identifies a unique (partial) image that is being
// stored in the render task cache.
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))]
pub struct ImageCacheKey {
// TODO(gw): Consider introducing a struct that collectively
// identifies an image in the resource cache
// uniquely. We pass this around to a few places.
pub image_key: ImageKey,
pub image_rendering: ImageRendering,
pub tile_offset: Option<TileOffset>,
pub texel_rect: Option<DeviceIntRect>,
}
// Where to find the texture data for an image primitive.
#[derive(Debug)]
pub enum ImageSource {
// A normal image - just reference the texture cache.
Default,
// An image that is pre-rendered into the texture cache
// via a render task.
Cache {
size: DeviceIntSize,
item: CacheItem,
},
}
#[derive(Debug)]
pub struct ImagePrimitiveCpu {
pub tile_spacing: LayerSize,
pub alpha_type: AlphaType,
// TODO(gw): Build on demand
pub gpu_blocks: [GpuBlockData; BLOCKS_PER_UV_RECT],
pub stretch_size: LayerSize,
pub current_epoch: Epoch,
pub source: ImageSource,
pub key: ImageCacheKey,
}
impl ToGpuBlocks for ImagePrimitiveCpu {
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
request.extend_from_slice(&self.gpu_blocks);
request.push([
self.stretch_size.width, self.stretch_size.height,
self.tile_spacing.width, self.tile_spacing.height,
]);
}
}
@ -708,7 +703,7 @@ impl TextRunPrimitiveCpu {
// TODO(gw): If we support chunks() on AuxIter
// in the future, this code below could
// be much simpler...
let mut gpu_block = GpuBlockData::empty();
let mut gpu_block = [0.0; 4];
for (i, src) in src_glyphs.enumerate() {
let key = GlyphKey::new(src.index, src.point, font.render_mode, subpx_dir);
self.glyph_keys.push(key);
@ -716,19 +711,19 @@ impl TextRunPrimitiveCpu {
// Two glyphs are packed per GPU block.
if (i & 1) == 0 {
gpu_block.data[0] = src.point.x;
gpu_block.data[1] = src.point.y;
gpu_block[0] = src.point.x;
gpu_block[1] = src.point.y;
} else {
gpu_block.data[2] = src.point.x;
gpu_block.data[3] = src.point.y;
self.glyph_gpu_blocks.push(gpu_block);
gpu_block[2] = src.point.x;
gpu_block[3] = src.point.y;
self.glyph_gpu_blocks.push(gpu_block.into());
}
}
// Ensure the last block is added in the case
// of an odd number of glyphs.
if (self.glyph_keys.len() & 1) != 0 {
self.glyph_gpu_blocks.push(gpu_block);
self.glyph_gpu_blocks.push(gpu_block.into());
}
}
@ -739,9 +734,7 @@ impl TextRunPrimitiveCpu {
request.push(self.get_color().premultiplied());
// this is the only case where we need to provide plain color to GPU
let bg_color = ColorF::from(self.font.bg_color);
request.extend_from_slice(&[
GpuBlockData { data: [bg_color.r, bg_color.g, bg_color.b, 1.0] }
]);
request.push([bg_color.r, bg_color.g, bg_color.b, 1.0]);
request.push([
self.offset.x,
self.offset.y,
@ -1197,24 +1190,97 @@ impl PrimitiveStore {
}
PrimitiveKind::Image => {
let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0];
let image_properties = resource_cache.get_image_properties(image_cpu.key.image_key);
resource_cache.request_image(
image_cpu.image_key,
image_cpu.image_rendering,
image_cpu.tile_offset,
gpu_cache,
);
// TODO(gw): Add image.rs and move this code out to a separate
// source file as it gets more complicated, and we
// start pre-rendering images for other reasons.
// TODO(gw): This doesn't actually need to be calculated each frame.
// It's cheap enough that it's not worth introducing a cache for images
// right now, but if we introduce a cache for images for some other
// reason then we might as well cache this with it.
if let Some(image_properties) =
resource_cache.get_image_properties(image_cpu.image_key)
{
if let Some(image_properties) = image_properties {
// See if this image has been updated since we last hit this code path.
// If so, we need to (at least) update the opacity, and also rebuild
// and render task cached portions of this image.
if image_properties.epoch != image_cpu.current_epoch {
image_cpu.current_epoch = image_properties.epoch;
// Update the opacity.
metadata.opacity.is_opaque = image_properties.descriptor.is_opaque &&
image_cpu.tile_spacing.width == 0.0 &&
image_cpu.tile_spacing.height == 0.0;
// Work out whether this image is a normal / simple type, or if
// we need to pre-render it to the render task cache.
image_cpu.source = match image_cpu.key.texel_rect {
Some(texel_rect) => {
ImageSource::Cache {
// Size in device-pixels we need to allocate in render task cache.
size: texel_rect.size,
item: CacheItem::invalid(),
}
}
None => {
// Simple image - just use a normal texture cache entry.
ImageSource::Default
}
};
}
// TODO(gw): Don't actually need this in cached source mode if
// the cache item is still valid...
resource_cache.request_image(
image_cpu.key.image_key,
image_cpu.key.image_rendering,
image_cpu.key.tile_offset,
gpu_cache,
);
// Every frame, for cached items, we need to request the render
// task cache item. The closure will be invoked on the first
// time through, and any time the render task output has been
// evicted from the texture cache.
if let ImageSource::Cache { size, ref mut item } = image_cpu.source {
let key = image_cpu.key;
// Request a pre-rendered image task.
*item = resource_cache.request_render_task(
RenderTaskCacheKey {
size,
kind: RenderTaskCacheKeyKind::Image(key),
},
gpu_cache,
render_tasks,
|render_tasks| {
// Create a task to blit from the texture cache to
// a normal transient render task surface. This will
// copy only the sub-rect, if specified.
let cache_to_target_task = RenderTask::new_blit(
size,
BlitSource::Image {
key,
},
);
let cache_to_target_task_id = render_tasks.add(cache_to_target_task);
// Create a task to blit the rect from the child render
// task above back into the right spot in the persistent
// render target cache.
let target_to_cache_task = RenderTask::new_blit(
size,
BlitSource::RenderTask {
task_id: cache_to_target_task_id,
},
);
let target_to_cache_task_id = render_tasks.add(target_to_cache_task);
// Hook this into the render task tree at the right spot.
parent_tasks.push(target_to_cache_task_id);
// Pass the image opacity, so that the cached render task
// item inherits the same opacity properties.
(target_to_cache_task_id, [0.0; 3], image_properties.descriptor.is_opaque)
}
);
}
}
}
PrimitiveKind::YuvImage => {

View File

@ -77,6 +77,11 @@ struct Document {
// the first frame would produce inconsistent rendering results, because
// scroll events are not necessarily received in deterministic order.
render_on_scroll: Option<bool>,
// A helper flag to prevent any hit-tests from happening between calls
// to build_scene and rendering the document. In between these two calls,
// hit-tests produce inconsistent results because the clip_scroll_tree
// is out of sync with the display list.
render_on_hittest: bool,
}
impl Document {
@ -105,8 +110,9 @@ impl Document {
},
frame_ctx: FrameContext::new(config),
frame_builder: Some(FrameBuilder::empty()),
render_on_scroll,
output_pipelines: FastHashSet::default(),
render_on_scroll,
render_on_hittest: false,
}
}
@ -403,6 +409,15 @@ impl RenderBackend {
}
DocumentMsg::HitTest(pipeline_id, point, flags, tx) => {
profile_scope!("HitTest");
if doc.render_on_hittest {
doc.render(
&mut self.resource_cache,
&mut self.gpu_cache,
&mut profile_counters.resources,
);
doc.render_on_hittest = false;
}
let cst = doc.frame_ctx.get_clip_scroll_tree();
let result = doc.frame_builder
.as_ref()
@ -580,16 +595,14 @@ impl RenderBackend {
DebugCommand::LoadCapture(root, tx) => {
NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed);
frame_counter += 1;
let msg = ResultMsg::DebugOutput(
DebugOutput::LoadCapture(root.clone())
);
self.result_tx.send(msg).unwrap();
self.load_capture(&root, &mut profile_counters);
for (id, doc) in &self.documents {
let captured = CapturedDocument {
document_id: *id,
root_pipeline_id: doc.scene.root_pipeline_id,
window_size: doc.view.window_size,
};
tx.send(captured).unwrap();
}
@ -658,6 +671,7 @@ impl RenderBackend {
if op.build {
profile_scope!("build scene");
doc.build_scene(&mut self.resource_cache);
doc.render_on_hittest = true;
}
if op.render {
@ -683,6 +697,7 @@ impl RenderBackend {
);
self.result_tx.send(msg).unwrap();
profile_counters.reset();
doc.render_on_hittest = false;
}
if op.render || op.scroll {
@ -862,6 +877,15 @@ impl RenderBackend {
config.serialize(&backend, "backend");
if config.bits.contains(CaptureBits::FRAME) {
// After we rendered the frames, there are pending updates.
// Instead of serializing them, we are going to make sure
// they are applied on the `Renderer` side.
let msg = ResultMsg::UpdateResources {
updates: self.resource_cache.pending_updates(),
cancel_rendering: false,
};
self.result_tx.send(msg).unwrap();
// Save the texture/glyph/image caches.
info!("\tresource cache");
let caches = self.resource_cache.save_caches(&config.root);
config.serialize(&caches, "resource_cache");
@ -884,11 +908,15 @@ impl RenderBackend {
.expect("Unable to open backend.ron");
let caches_maybe = CaptureConfig::deserialize::<PlainCacheOwn, _>(root, "resource_cache");
// Note: it would be great to have RenderBackend to be split
// Note: it would be great to have `RenderBackend` to be split
// rather explicitly on what's used before and after scene building
// so that, for example, we never miss anything in the code below:
self.resource_cache.load_capture(backend.resources, caches_maybe,root);
let plain_externals = self.resource_cache.load_capture(backend.resources, caches_maybe, root);
let msg_load = ResultMsg::DebugOutput(
DebugOutput::LoadCapture(root.clone(), plain_externals)
);
self.result_tx.send(msg_load).unwrap();
self.gpu_cache = match CaptureConfig::deserialize::<GpuCache, _>(root, "gpu_cache") {
Some(gpu_cache) => gpu_cache,
@ -913,6 +941,7 @@ impl RenderBackend {
frame_builder: Some(FrameBuilder::empty()),
output_pipelines: FastHashSet::default(),
render_on_scroll: None,
render_on_hittest: false,
};
let frame_name = format!("frame-{}-{}", (id.0).0, id.1);
@ -931,13 +960,13 @@ impl RenderBackend {
}
};
let msg = ResultMsg::PublishDocument(
let msg_publish = ResultMsg::PublishDocument(
id,
render_doc,
self.resource_cache.pending_updates(),
profile_counters.clone(),
);
self.result_tx.send(msg).unwrap();
self.result_tx.send(msg_publish).unwrap();
profile_counters.reset();
self.notifier.new_document_ready(id, false, true);

View File

@ -12,7 +12,7 @@ use gpu_cache::GpuCache;
use gpu_types::{ClipScrollNodeIndex, PictureType};
use internal_types::{FastHashMap, RenderPassIndex, SourceTexture};
use picture::ContentOrigin;
use prim_store::{PrimitiveIndex};
use prim_store::{PrimitiveIndex, ImageCacheKey};
#[cfg(feature = "debugger")]
use print_tree::{PrintTreePrinter};
use resource_cache::CacheItem;
@ -263,6 +263,24 @@ impl BlurTask {
}
}
// Where the source data for a blit task can be found.
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))]
pub enum BlitSource {
Image {
key: ImageCacheKey,
},
RenderTask {
task_id: RenderTaskId,
},
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))]
pub struct BlitTask {
pub source: BlitSource,
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))]
pub struct RenderTaskData {
@ -278,6 +296,7 @@ pub enum RenderTaskKind {
HorizontalBlur(BlurTask),
Readback(DeviceIntRect),
Scaling(RenderTargetKind),
Blit(BlitTask),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
@ -342,6 +361,32 @@ impl RenderTask {
}
}
pub fn new_blit(
size: DeviceIntSize,
source: BlitSource,
) -> Self {
let mut children = Vec::new();
// If this blit uses a render task as a source,
// ensure it's added as a child task. This will
// ensure it gets allocated in the correct pass
// and made available as an input when this task
// executes.
if let BlitSource::RenderTask { task_id } = source {
children.push(task_id);
}
RenderTask {
children,
location: RenderTaskLocation::Dynamic(None, size),
kind: RenderTaskKind::Blit(BlitTask {
source,
}),
clear_mode: ClearMode::Transparent,
pass_index: None,
}
}
pub fn new_mask(
outer_rect: DeviceIntRect,
clips: Vec<ClipWorkItem>,
@ -511,7 +556,8 @@ impl RenderTask {
)
}
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) => {
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) => {
(
[0.0; 3],
[0.0; 4],
@ -598,6 +644,10 @@ impl RenderTask {
RenderTaskKind::Picture(ref task_info) => {
task_info.target_kind
}
RenderTaskKind::Blit(..) => {
RenderTargetKind::Color
}
}
}
@ -613,7 +663,8 @@ impl RenderTask {
RenderTaskKind::VerticalBlur(..) |
RenderTaskKind::Readback(..) |
RenderTaskKind::HorizontalBlur(..) |
RenderTaskKind::Scaling(..) => false,
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) => false,
RenderTaskKind::CacheMask(..) => true,
}
@ -646,6 +697,10 @@ impl RenderTask {
pt.new_level("Scaling".to_owned());
pt.add_item(format!("kind: {:?}", kind));
}
RenderTaskKind::Blit(ref task) => {
pt.new_level("Blit".to_owned());
pt.add_item(format!("source: {:?}", task.source));
}
}
pt.add_item(format!("clear to: {:?}", self.clear_mode));
@ -664,6 +719,7 @@ impl RenderTask {
#[derive(Debug, Hash, PartialEq, Eq)]
pub enum RenderTaskCacheKeyKind {
BoxShadow(BoxShadowCacheKey),
Image(ImageCacheKey),
}
#[derive(Debug, Hash, PartialEq, Eq)]
@ -722,7 +778,7 @@ impl RenderTaskCache {
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
mut f: F,
) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, [f32; 3]) {
) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, [f32; 3], bool) {
// Get the texture cache handle for this cache key,
// or create one.
let cache_entry = self.entries
@ -735,9 +791,15 @@ impl RenderTaskCache {
if texture_cache.request(&mut cache_entry.handle, gpu_cache) {
// Invoke user closure to get render task chain
// to draw this into the texture cache.
let (render_task_id, user_data) = f(render_tasks);
let (render_task_id, user_data, is_opaque) = f(render_tasks);
let render_task = &mut render_tasks[render_task_id];
// Select the right texture page to allocate from.
let image_format = match render_task.target_kind() {
RenderTargetKind::Color => ImageFormat::BGRA8,
RenderTargetKind::Alpha => ImageFormat::R8,
};
// Find out what size to alloc in the texture cache.
let size = match render_task.location {
RenderTaskLocation::Fixed |
@ -753,8 +815,8 @@ impl RenderTaskCache {
let descriptor = ImageDescriptor::new(
size.width as u32,
size.height as u32,
ImageFormat::R8,
false,
image_format,
is_opaque,
);
// Allocate space in the texture cache, but don't supply

View File

@ -11,8 +11,9 @@
use api::{BlobImageRenderer, ColorF, ColorU, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, Epoch, ExternalImageId};
use api::{ExternalImageType, FontRenderMode, ImageFormat, PipelineId, RenderApiSender};
use api::{RenderNotifier, YUV_COLOR_SPACES, YUV_FORMATS, YuvColorSpace, YuvFormat, channel};
use api::{ExternalImageType, FontRenderMode, ImageFormat, PipelineId};
use api::{RenderApiSender, RenderNotifier, TexelRect, TextureTarget, YuvColorSpace, YuvFormat};
use api::{YUV_COLOR_SPACES, YUV_FORMATS, channel};
#[cfg(not(feature = "debugger"))]
use api::ApiMsg;
use api::DebugCommand;
@ -21,7 +22,7 @@ use api::channel::MsgSender;
use batch::{BatchKey, BatchKind, BatchTextures, BrushBatchKind};
use batch::{BrushImageSourceKind, TransformBatchKind};
#[cfg(feature = "capture")]
use capture::{CaptureConfig, ExternalCaptureImage};
use capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
use debug_colors;
use debug_render::DebugRenderer;
#[cfg(feature = "debugger")]
@ -29,7 +30,7 @@ use debug_server::{self, DebugServer};
use device::{DepthFunction, Device, FrameId, Program, UploadMethod, Texture,
VertexDescriptor, PBO};
use device::{ExternalTexture, FBOId, TextureSlot, VertexAttribute, VertexAttributeKind};
use device::{FileWatcherHandler, ShaderError, TextureFilter, TextureTarget,
use device::{FileWatcherHandler, ShaderError, TextureFilter,
VertexUsageHint, VAO, VBO, CustomVAO};
use device::{ProgramCache, ReadPixelsFormat};
use euclid::{rect, Transform3D};
@ -67,7 +68,7 @@ use std::thread;
use texture_cache::TextureCache;
use thread_profiler::{register_thread_with_profiler, write_profile};
use tiling::{AlphaRenderTarget, ColorRenderTarget};
use tiling::{RenderPass, RenderPassKind, RenderTargetList};
use tiling::{BlitJob, BlitJobSource, RenderPass, RenderPassKind, RenderTargetList};
use tiling::{Frame, RenderTarget, ScalingInfo, TextureCacheRenderTarget};
use time::precise_time_ns;
use util::TransformedRectKind;
@ -165,6 +166,10 @@ const GPU_TAG_BLUR: GpuProfileTag = GpuProfileTag {
label: "Blur",
color: debug_colors::VIOLET,
};
const GPU_TAG_BLIT: GpuProfileTag = GpuProfileTag {
label: "Blit",
color: debug_colors::LIME,
};
const GPU_SAMPLER_TAG_ALPHA: GpuProfileTag = GpuProfileTag {
label: "Alpha Targets",
@ -490,6 +495,18 @@ pub enum ImageBufferKind {
Texture2DArray = 3,
}
//TODO: those types are the same, so let's merge them
impl From<TextureTarget> for ImageBufferKind {
fn from(target: TextureTarget) -> Self {
match target {
TextureTarget::Default => ImageBufferKind::Texture2D,
TextureTarget::Rect => ImageBufferKind::TextureRect,
TextureTarget::Array => ImageBufferKind::Texture2DArray,
TextureTarget::External => ImageBufferKind::TextureExternal,
}
}
}
pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 4] = [
ImageBufferKind::Texture2D,
ImageBufferKind::TextureRect,
@ -960,7 +977,7 @@ impl CacheTexture {
rows.push(CacheRow::new());
// Add enough GPU blocks for this row.
cpu_blocks
.extend_from_slice(&[GpuBlockData::empty(); MAX_VERTEX_TEXTURE_WIDTH]);
.extend_from_slice(&[GpuBlockData::EMPTY; MAX_VERTEX_TEXTURE_WIDTH]);
}
// This row is dirty (needs to be updated in GPU texture).
@ -1526,16 +1543,6 @@ fn create_clip_shader(name: &'static str, device: &mut Device) -> Result<Program
program
}
fn get_external_image_target(ext_type: ExternalImageType) -> Option<TextureTarget> {
Some(match ext_type {
ExternalImageType::Texture2DHandle => TextureTarget::Default,
ExternalImageType::Texture2DArrayHandle => TextureTarget::Array,
ExternalImageType::TextureRectHandle => TextureTarget::Rect,
ExternalImageType::TextureExternalHandle => TextureTarget::External,
ExternalImageType::ExternalBuffer => return None,
})
}
struct FileWatcher {
notifier: Box<RenderNotifier>,
result_tx: Sender<ResultMsg>,
@ -2424,9 +2431,9 @@ impl Renderer {
self.save_capture(config, deferred);
}
#[cfg(feature = "capture")]
DebugOutput::LoadCapture(root) => {
DebugOutput::LoadCapture(root, plain_externals) => {
self.active_documents.clear();
self.load_capture(root);
self.load_capture(root, plain_externals);
}
},
ResultMsg::DebugCommand(command) => {
@ -3324,6 +3331,50 @@ impl Renderer {
);
}
fn handle_blits(
&mut self,
blits: &[BlitJob],
render_tasks: &RenderTaskTree,
) {
if blits.is_empty() {
return;
}
let _timer = self.gpu_profile.start_timer(GPU_TAG_BLIT);
// TODO(gw): For now, we don't bother batching these by source texture.
// If if ever shows up as an issue, we can easily batch them.
for blit in blits {
let source_rect = match blit.source {
BlitJobSource::Texture(texture_id, layer, source_rect) => {
// A blit from a texture into this target.
let src_texture = self.texture_resolver
.resolve(&texture_id)
.expect("BUG: invalid source texture");
self.device.bind_read_target(Some((src_texture, layer)));
source_rect
}
BlitJobSource::RenderTask(task_id) => {
// A blit from the child render task into this target.
// TODO(gw): Support R8 format here once we start
// creating mips for alpha masks.
let src_texture = self.texture_resolver
.resolve(&SourceTexture::CacheRGBA8)
.expect("BUG: invalid source texture");
let source = &render_tasks[task_id];
let (source_rect, layer) = source.get_target_rect();
self.device.bind_read_target(Some((src_texture, layer.0 as i32)));
source_rect
}
};
debug_assert_eq!(source_rect.size, blit.target_rect.size);
self.device.blit_render_target(
source_rect,
blit.target_rect,
);
}
}
fn handle_scaling(
&mut self,
render_tasks: &RenderTaskTree,
@ -3413,6 +3464,9 @@ impl Renderer {
}
}
// Handle any blits from the texture cache to this target.
self.handle_blits(&target.blits, render_tasks);
// Draw any blurs for this target.
// Blurs are rendered as a standard 2-pass
// separable implementation.
@ -3977,6 +4031,7 @@ impl Renderer {
texture: &SourceTexture,
layer: i32,
target: &TextureCacheRenderTarget,
render_tasks: &RenderTaskTree,
stats: &mut RendererStats,
) {
let projection = {
@ -4001,6 +4056,9 @@ impl Renderer {
)
};
// Handle any blits to this texture from child tasks.
self.handle_blits(&target.blits, render_tasks);
// Draw any blurs for this target.
if !target.horizontal_blurs.is_empty() {
let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR);
@ -4043,8 +4101,12 @@ impl Renderer {
.external_image
.expect("BUG: Deferred resolves must be external images!");
let image = handler.lock(ext_image.id, ext_image.channel_index);
let texture_target = get_external_image_target(ext_image.image_type)
.expect(&format!("{:?} is not a suitable image type in update_deferred_resolves()", ext_image.image_type));
let texture_target = match ext_image.image_type {
ExternalImageType::TextureHandle(target) => target,
ExternalImageType::Buffer => {
panic!("{:?} is not a suitable image type in update_deferred_resolves()", ext_image.image_type);
}
};
// In order to produce the handle, the external image handler may call into
// the GL context and change some states.
@ -4063,7 +4125,9 @@ impl Renderer {
// Just use 0 as the gl handle for this failed case.
ExternalTexture::new(0, texture_target)
}
_ => panic!("No native texture found."),
ExternalImageSource::RawData(_) => {
panic!("Raw external data is not expected for deferred resolves!");
}
};
self.texture_resolver
@ -4075,7 +4139,7 @@ impl Renderer {
block_count: BLOCKS_PER_UV_RECT,
address: deferred_resolve.address,
});
list.blocks.push([image.u0, image.v0, image.u1, image.v1].into());
list.blocks.push(image.uv.into());
list.blocks.push([0f32; 4].into());
}
@ -4209,9 +4273,9 @@ impl Renderer {
stats: &mut RendererStats,
) {
let _gm = self.gpu_profile.start_marker("tile frame draw");
frame.has_been_rendered = true;
if frame.passes.is_empty() {
frame.has_been_rendered = true;
return;
}
@ -4270,14 +4334,21 @@ impl Renderer {
RenderPassKind::OffScreen { ref mut alpha, ref mut color, ref mut texture_cache } => {
alpha.check_ready();
color.check_ready();
// If this frame has already been drawn, then any texture
// cache targets have already been updated and can be
// skipped this time.
if !frame.has_been_rendered {
for (&(texture_id, target_index), target) in texture_cache {
self.draw_texture_cache_target(
&texture_id,
target_index,
target,
&frame.render_tasks,
stats,
);
}
}
for (target_index, target) in alpha.targets.iter().enumerate() {
stats.alpha_target_count += 1;
@ -4365,6 +4436,8 @@ impl Renderer {
} else {
true
});
frame.has_been_rendered = true;
}
pub fn debug_renderer<'b>(&'b mut self) -> &'b mut DebugRenderer {
@ -4636,10 +4709,7 @@ pub enum ExternalImageSource<'a> {
/// will know to re-upload the image data to the GPU.
/// Note that the UV coords are supplied in texel-space!
pub struct ExternalImage<'a> {
pub u0: f32,
pub v0: f32,
pub u1: f32,
pub v1: f32,
pub uv: TexelRect,
pub source: ExternalImageSource<'a>,
}
@ -4786,21 +4856,27 @@ struct PlainRenderer {
external_images: Vec<ExternalCaptureImage>
}
#[cfg(feature = "capture")]
enum CapturedExternalImageData {
NativeTexture(gl::GLuint),
Buffer(Arc<Vec<u8>>),
}
#[cfg(feature = "capture")]
struct DummyExternalImageHandler {
data: FastHashMap<(ExternalImageId, u8), Vec<u8>>,
data: FastHashMap<(ExternalImageId, u8), (CapturedExternalImageData, TexelRect)>,
}
#[cfg(feature = "capture")]
impl ExternalImageHandler for DummyExternalImageHandler {
fn lock(&mut self, key: ExternalImageId, channel_index: u8) -> ExternalImage {
let slice = &self.data[&(key, channel_index)];
let (ref captured_data, ref uv) = self.data[&(key, channel_index)];
ExternalImage {
u0: 0.0,
v0: 0.0,
u1: 1.0,
v1: 1.0,
source: ExternalImageSource::RawData(slice),
uv: *uv,
source: match *captured_data {
CapturedExternalImageData::NativeTexture(tid) => ExternalImageSource::NativeTexture(tid),
CapturedExternalImageData::Buffer(ref arc) => ExternalImageSource::RawData(&*arc),
}
}
}
fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {}
@ -4838,8 +4914,28 @@ impl Renderer {
let bytes_per_layer = (rect.size.width * rect.size.height * bytes_per_pixel) as usize;
let mut data = vec![0; bytes_per_layer];
//TODO: instead of reading from an FBO with `read_pixels*`, we could
// read from textures directly with `get_tex_image*`.
for layer_id in 0 .. texture.get_layer_count() {
device.attach_read_texture(texture, layer_id);
#[cfg(feature = "png")]
{
let mut png_data;
let (data_ref, format) = match texture.get_format() {
ImageFormat::RGBAF32 => {
png_data = vec![0; (rect.size.width * rect.size.height * 4) as usize];
device.read_pixels_into(rect, ReadPixelsFormat::Rgba8, &mut png_data);
(&png_data, ReadPixelsFormat::Rgba8)
}
fm => (&data, ReadPixelsFormat::Standard(fm)),
};
CaptureConfig::save_png(
root.join(format!("textures/{}-{}.png", name, layer_id)),
(rect.size.width, rect.size.height), format,
data_ref,
);
}
device.read_pixels_into(rect, read_format, &mut data);
file.write_all(&data)
.unwrap();
@ -4861,7 +4957,7 @@ impl Renderer {
let mut texels = Vec::new();
assert_eq!(plain.format, texture.get_format());
File::open(root.join(&plain.data))
.unwrap()
.expect(&format!("Unable to open texture at {}", plain.data))
.read_to_end(&mut texels)
.unwrap();
@ -4874,42 +4970,85 @@ impl Renderer {
texels
}
fn save_capture(&mut self, config: CaptureConfig, deferred_images: Vec<ExternalCaptureImage>) {
fn save_capture(
&mut self,
config: CaptureConfig,
deferred_images: Vec<ExternalCaptureImage>,
) {
use std::fs;
use std::io::Write;
use api::{CaptureBits, ExternalImageData};
self.device.begin_frame();
let _gm = self.gpu_profile.start_marker("read GPU data");
self.device.bind_read_target_impl(self.capture.read_fbo);
if !deferred_images.is_empty() {
info!("saving external images");
let mut arc_map = FastHashMap::<*const u8, String>::default();
let mut tex_map = FastHashMap::<u32, String>::default();
let handler = self.external_image_handler
.as_mut()
.expect("Unable to lock the external image handler!");
for def in &deferred_images {
info!("\t{}", def.short_path);
let ExternalImageData { id, channel_index, image_type } = def.external;
let data = match handler.lock(id, channel_index).source {
ExternalImageSource::RawData(data) => data.to_vec(),
let ext_image = handler.lock(id, channel_index);
let (data, short_path) = match ext_image.source {
ExternalImageSource::RawData(data) => {
let arc_id = arc_map.len() + 1;
match arc_map.entry(data.as_ptr()) {
Entry::Occupied(e) => {
(None, e.get().clone())
}
Entry::Vacant(e) => {
let short_path = format!("externals/d{}.raw", arc_id);
(Some(data.to_vec()), e.insert(short_path).clone())
}
}
}
ExternalImageSource::NativeTexture(gl_id) => {
let target = get_external_image_target(image_type).unwrap();
self.device.attach_read_texture_external(gl_id, target, 0);
self.device.read_pixels(&def.descriptor)
let tex_id = tex_map.len() + 1;
match tex_map.entry(gl_id) {
Entry::Occupied(e) => {
(None, e.get().clone())
}
Entry::Vacant(e) => {
let target = match image_type {
ExternalImageType::TextureHandle(target) => target,
ExternalImageType::Buffer => unreachable!(),
};
info!("\t\tnative texture of target {:?}", target);
let layer_index = 0; //TODO: what about layered textures?
self.device.attach_read_texture_external(gl_id, target, layer_index);
let data = self.device.read_pixels(&def.descriptor);
let short_path = format!("externals/t{}.raw", tex_id);
(Some(data), e.insert(short_path).clone())
}
}
}
ExternalImageSource::Invalid => {
// Create a dummy buffer...
let stride = def.descriptor.compute_stride();
let total_size = def.descriptor.height * stride;
vec![0xFF; total_size as usize]
info!("\t\tinvalid source!");
(None, String::new())
}
};
handler.unlock(id, channel_index);
fs::File::create(config.root.join(&def.short_path))
.expect(&format!("Unable to create {}", def.short_path))
.write_all(&data)
if let Some(bytes) = data {
fs::File::create(config.root.join(&short_path))
.expect(&format!("Unable to create {}", short_path))
.write_all(&bytes)
.unwrap();
}
let plain = PlainExternalImage {
data: short_path,
id: def.external.id,
channel_index: def.external.channel_index,
uv: ext_image.uv,
};
config.serialize(&plain, &def.short_path);
}
for def in &deferred_images {
handler.unlock(def.external.id, def.external.channel_index);
}
}
if config.bits.contains(CaptureBits::FRAME) {
@ -4944,14 +5083,43 @@ impl Renderer {
info!("done.");
}
fn load_capture(&mut self, root: PathBuf) {
let renderer = match CaptureConfig::deserialize::<PlainRenderer, _>(&root, "renderer") {
Some(r) => r,
None => return,
};
fn load_capture(
&mut self, root: PathBuf, plain_externals: Vec<PlainExternalImage>
) {
use std::fs::File;
use std::io::Read;
use std::slice;
self.device.begin_frame();
info!("loading external buffer-backed images");
assert!(self.texture_resolver.external_images.is_empty());
let mut raw_map = FastHashMap::<String, Arc<Vec<u8>>>::default();
let mut image_handler = DummyExternalImageHandler {
data: FastHashMap::default(),
};
// Note: this is a `SCENE` level population of the external image handlers
// It would put both external buffers and texture into the map.
// But latter are going to be overwritten later in this function
// if we are in the `FRAME` level.
for plain_ext in plain_externals {
let data = match raw_map.entry(plain_ext.data) {
Entry::Occupied(e) => e.get().clone(),
Entry::Vacant(e) => {
let mut buffer = Vec::new();
File::open(root.join(e.key()))
.expect(&format!("Unable to open {}", e.key()))
.read_to_end(&mut buffer)
.unwrap();
e.insert(Arc::new(buffer)).clone()
}
};
let key = (plain_ext.id, plain_ext.channel_index);
let value = (CapturedExternalImageData::Buffer(data), plain_ext.uv);
image_handler.data.insert(key, value);
}
if let Some(renderer) = CaptureConfig::deserialize::<PlainRenderer, _>(&root, "renderer") {
info!("loading cached textures");
self.device.begin_frame();
for texture in self.texture_resolver.cache_texture_map.drain(..) {
self.device.delete_texture(texture);
@ -4964,7 +5132,7 @@ impl Renderer {
}
info!("loading gpu cache");
Self::load_texture(
let gpu_cache_data = Self::load_texture(
&mut self.gpu_cache_texture.texture,
&renderer.gpu_cache,
&root,
@ -4972,43 +5140,62 @@ impl Renderer {
);
match self.gpu_cache_texture.bus {
CacheBus::PixelBuffer { ref mut rows, ref mut cpu_blocks, .. } => {
let dim = self.gpu_cache_texture.texture.get_dimensions();
let blocks = unsafe {
slice::from_raw_parts(
gpu_cache_data.as_ptr() as *const GpuBlockData,
gpu_cache_data.len() / mem::size_of::<GpuBlockData>(),
)
};
// fill up the CPU cache from the contents we just loaded
rows.clear();
cpu_blocks.clear();
rows.extend((0 .. dim.height).map(|_| CacheRow::new()));
cpu_blocks.extend_from_slice(blocks);
}
CacheBus::Scatter { .. } => {}
}
info!("loading external images");
assert!(self.texture_resolver.external_images.is_empty());
let mut image_handler = DummyExternalImageHandler {
data: FastHashMap::default(),
};
info!("loading external texture-backed images");
let mut native_map = FastHashMap::<String, gl::GLuint>::default();
for ExternalCaptureImage { short_path, external, descriptor } in renderer.external_images {
let target = match get_external_image_target(external.image_type) {
Some(target) => target,
None => continue,
let target = match external.image_type {
ExternalImageType::TextureHandle(target) => target,
ExternalImageType::Buffer => continue,
};
let plain_ext = CaptureConfig::deserialize::<PlainExternalImage, _>(&root, &short_path)
.expect(&format!("Unable to read {}.ron", short_path));
let key = (external.id, external.channel_index);
let tid = match native_map.entry(plain_ext.data) {
Entry::Occupied(e) => e.get().clone(),
Entry::Vacant(e) => {
//TODO: provide a way to query both the layer count and the filter from external images
let (layer_count, filter) = (1, TextureFilter::Linear);
let plain = PlainTexture {
data: short_path,
let plain_tex = PlainTexture {
data: e.key().clone(),
size: (descriptor.width, descriptor.height, layer_count),
format: descriptor.format,
filter,
render_target: None,
};
let mut t = self.device.create_texture(target, plain_tex.format);
Self::load_texture(&mut t, &plain_tex, &root, &mut self.device);
let extex = t.into_external();
self.capture.owned_external_images.insert(key, extex.clone());
e.insert(extex.internal_id()).clone()
}
};
let mut t = self.device.create_texture(target, plain.format);
let data = Self::load_texture(&mut t, &plain, &root, &mut self.device);
let key = (external.id, external.channel_index);
self.capture.owned_external_images.insert(key, t.into_external());
image_handler.data.insert(key, data);
let value = (CapturedExternalImageData::NativeTexture(tid), plain_ext.uv);
image_handler.data.insert(key, value);
}
self.device.end_frame();
self.external_image_handler = Some(Box::new(image_handler) as Box<_>);
}
self.output_image_handler = Some(Box::new(()) as Box<_>);
self.external_image_handler = Some(Box::new(image_handler) as Box<_>);
info!("done.");
}
}

View File

@ -15,7 +15,7 @@ use api::{TileOffset, TileSize};
use api::{NativeFontHandle};
use app_units::Au;
#[cfg(feature = "capture")]
use capture::{ExternalCaptureImage};
use capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
use device::TextureFilter;
use frame::FrameId;
use glyph_cache::GlyphCache;
@ -55,17 +55,32 @@ pub struct GlyphFetchResult {
// storing the coordinates as texel values
// we don't need to go through and update
// various CPU-side structures.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct CacheItem {
pub texture_id: SourceTexture,
pub uv_rect_handle: GpuCacheHandle,
pub uv_rect: DeviceUintRect,
pub texture_layer: i32,
}
impl CacheItem {
pub fn invalid() -> Self {
CacheItem {
texture_id: SourceTexture::Invalid,
uv_rect_handle: GpuCacheHandle::new(),
uv_rect: DeviceUintRect::zero(),
texture_layer: 0,
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))]
pub struct ImageProperties {
pub descriptor: ImageDescriptor,
pub external_image: Option<ExternalImageData>,
pub tiling: Option<TileSize>,
pub epoch: Epoch,
}
#[derive(Debug, Copy, Clone, PartialEq)]
@ -285,7 +300,7 @@ impl ResourceCache {
ImageData::External(info) => {
// External handles already represent existing textures so it does
// not make sense to tile them into smaller ones.
info.image_type == ExternalImageType::ExternalBuffer && size_check
info.image_type == ExternalImageType::Buffer && size_check
}
}
}
@ -301,7 +316,7 @@ impl ResourceCache {
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
f: F,
) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, [f32; 3]) {
) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, [f32; 3], bool) {
self.cached_render_tasks.request_render_task(
key,
&mut self.texture_cache,
@ -747,16 +762,11 @@ impl ResourceCache {
image_template.map(|image_template| {
let external_image = match image_template.data {
ImageData::External(ext_image) => {
match ext_image.image_type {
ExternalImageType::Texture2DHandle |
ExternalImageType::Texture2DArrayHandle |
ExternalImageType::TextureRectHandle |
ExternalImageType::TextureExternalHandle => Some(ext_image),
ImageData::External(ext_image) => match ext_image.image_type {
ExternalImageType::TextureHandle(_) => Some(ext_image),
// external buffer uses resource_cache.
ExternalImageType::ExternalBuffer => None,
}
}
ExternalImageType::Buffer => None,
},
// raw and blob image are all using resource_cache.
ImageData::Raw(..) | ImageData::Blob(..) => None,
};
@ -765,6 +775,7 @@ impl ResourceCache {
descriptor: image_template.descriptor,
external_image,
tiling: image_template.tiling,
epoch: image_template.epoch,
}
})
}
@ -990,6 +1001,7 @@ enum PlainFontTemplate {
struct PlainImageTemplate {
data: String,
descriptor: ImageDescriptor,
epoch: Epoch,
tiling: Option<TileSize>,
}
@ -1026,6 +1038,8 @@ impl ResourceCache {
pub fn save_capture(
&mut self, root: &PathBuf
) -> (PlainResources, Vec<ExternalCaptureImage>) {
#[cfg(feature = "png")]
use device::ReadPixelsFormat;
use std::fs;
use std::io::Write;
@ -1046,6 +1060,10 @@ impl ResourceCache {
if !path_blobs.is_dir() {
fs::create_dir(&path_blobs).unwrap();
}
let path_externals = root.join("externals");
if !path_externals.is_dir() {
fs::create_dir(&path_externals).unwrap();
}
info!("\tfont templates");
let mut font_paths = FastHashMap::default();
@ -1071,6 +1089,7 @@ impl ResourceCache {
info!("\timage templates");
let mut image_paths = FastHashMap::default();
let mut other_paths = FastHashMap::default();
let mut num_blobs = 0;
let mut external_images = Vec::new();
for (&key, template) in res.image_templates.images.iter() {
let desc = &template.descriptor;
@ -1082,8 +1101,13 @@ impl ResourceCache {
Entry::Vacant(e) => e,
};
//TODO: option to save as PNG:
// https://github.com/servo/webrender/issues/2234
#[cfg(feature = "png")]
CaptureConfig::save_png(
root.join(format!("images/{}.png", image_id)),
(desc.width, desc.height),
ReadPixelsFormat::Standard(desc.format),
&arc,
);
let file_name = format!("{}.raw", image_id);
let short_path = format!("images/{}", file_name);
fs::File::create(path_images.join(file_name))
@ -1100,7 +1124,6 @@ impl ResourceCache {
// https://github.com/servo/webrender/issues/2236
tile: None,
};
let renderer = self.blob_image_renderer.as_mut().unwrap();
renderer.request(
&self.resources,
@ -1116,8 +1139,17 @@ impl ResourceCache {
let result = renderer.resolve(request)
.expect("Blob resolve failed");
assert_eq!((result.width, result.height), (desc.width, desc.height));
assert_eq!(result.data.len(), desc.compute_total_size() as usize);
let file_name = format!("{}.raw", other_paths.len() + 1);
num_blobs += 1;
#[cfg(feature = "png")]
CaptureConfig::save_png(
root.join(format!("blobs/{}.png", num_blobs)),
(desc.width, desc.height),
ReadPixelsFormat::Standard(desc.format),
&result.data,
);
let file_name = format!("{}.raw", num_blobs);
let short_path = format!("blobs/{}", file_name);
let full_path = path_blobs.clone().join(&file_name);
fs::File::create(full_path)
@ -1127,7 +1159,7 @@ impl ResourceCache {
other_paths.insert(key, short_path);
}
ImageData::External(ref ext) => {
let short_path = format!("blobs/{}.raw", other_paths.len() + 1);
let short_path = format!("externals/{}", external_images.len() + 1);
other_paths.insert(key, short_path.clone());
external_images.push(ExternalCaptureImage {
short_path,
@ -1166,6 +1198,7 @@ impl ResourceCache {
},
descriptor: template.descriptor.clone(),
tiling: template.tiling,
epoch: template.epoch,
})
})
.collect(),
@ -1243,7 +1276,7 @@ impl ResourceCache {
resources: PlainResources,
caches: Option<PlainCacheOwn>,
root: &PathBuf,
) {
) -> Vec<PlainExternalImage> {
use std::fs::File;
use std::io::Read;
@ -1348,7 +1381,19 @@ impl ResourceCache {
}
info!("\timage templates...");
let mut external_images = Vec::new();
for (key, template) in resources.image_templates {
let data = match CaptureConfig::deserialize::<PlainExternalImage, _>(root, &template.data) {
Some(plain) => {
let ext_data = ExternalImageData {
id: plain.id,
channel_index: plain.channel_index,
image_type: ExternalImageType::Buffer,
};
external_images.push(plain);
ImageData::External(ext_data)
}
None => {
let arc = match raw_map.entry(template.data) {
Entry::Occupied(e) => {
e.get().clone()
@ -1363,14 +1408,19 @@ impl ResourceCache {
.clone()
}
};
ImageData::Raw(arc)
}
};
res.image_templates.images.insert(key, ImageResource {
data: ImageData::Raw(arc),
data,
descriptor: template.descriptor,
tiling: template.tiling,
epoch: Epoch(0),
epoch: template.epoch,
dirty_rect: None,
});
}
external_images
}
}

View File

@ -405,9 +405,21 @@ impl TextureCache {
.get_opt(handle)
.expect("BUG: was dropped from cache or not updated!");
debug_assert_eq!(entry.last_access, self.frame_id);
let (layer_index, origin) = match entry.kind {
EntryKind::Standalone { .. } => {
(0, DeviceUintPoint::zero())
}
EntryKind::Cache {
layer_index,
origin,
..
} => (layer_index, origin),
};
CacheItem {
uv_rect_handle: entry.uv_rect_handle,
texture_id: SourceTexture::TextureCache(entry.texture_id),
uv_rect: DeviceUintRect::new(origin, entry.size),
texture_layer: layer_index as i32,
}
}
None => panic!("BUG: handle not requested earlier in frame"),
@ -1051,13 +1063,10 @@ impl TextureUpdate {
panic!("The vector image should have been rasterized.");
}
ImageData::External(ext_image) => match ext_image.image_type {
ExternalImageType::Texture2DHandle |
ExternalImageType::Texture2DArrayHandle |
ExternalImageType::TextureRectHandle |
ExternalImageType::TextureExternalHandle => {
ExternalImageType::TextureHandle(_) => {
panic!("External texture handle should not go through texture_cache.");
}
ExternalImageType::ExternalBuffer => TextureUpdateSource::External {
ExternalImageType::Buffer => TextureUpdateSource::External {
id: ext_image.id,
channel_index: ext_image.channel_index,
},

View File

@ -6,7 +6,7 @@ use api::{ClipId, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{DevicePixelScale, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
use api::{DocumentLayer, FilterOp, ImageFormat};
use api::{LayerRect, MixBlendMode, PipelineId};
use batch::{AlphaBatcher, ClipBatcher};
use batch::{AlphaBatcher, ClipBatcher, resolve_image};
use clip::{ClipStore};
use clip_scroll_tree::{ClipScrollTree};
use device::Texture;
@ -19,7 +19,7 @@ use picture::{PictureKind};
use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveStore};
use prim_store::{BrushMaskKind, BrushKind, DeferredResolve, EdgeAaSegmentMask};
use profiler::FrameProfileCounters;
use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind};
use render_task::{BlitSource, RenderTaskAddress, RenderTaskId, RenderTaskKind};
use render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskTree};
use resource_cache::{ResourceCache};
use std::{cmp, usize, f32, i32};
@ -100,13 +100,21 @@ pub trait RenderTarget {
_deferred_resolves: &mut Vec<DeferredResolve>,
) {
}
// TODO(gw): It's a bit odd that we need the deferred resolves and mutable
// GPU cache here. They are typically used by the build step
// above. They are used for the blit jobs to allow resolve_image
// to be called. It's a bit of extra overhead to store the image
// key here and the resolve them in the build step separately.
// BUT: if/when we add more texture cache target jobs, we might
// want to tidy this up.
fn add_task(
&mut self,
task_id: RenderTaskId,
ctx: &RenderTargetContext,
gpu_cache: &GpuCache,
gpu_cache: &mut GpuCache,
render_tasks: &RenderTaskTree,
clip_store: &ClipStore,
deferred_resolves: &mut Vec<DeferredResolve>,
);
fn used_rect(&self) -> DeviceIntRect;
fn needs_depth(&self) -> bool;
@ -159,9 +167,10 @@ impl<T: RenderTarget> RenderTargetList<T> {
&mut self,
task_id: RenderTaskId,
ctx: &RenderTargetContext,
gpu_cache: &GpuCache,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
clip_store: &ClipStore,
deferred_resolves: &mut Vec<DeferredResolve>,
) {
self.targets.last_mut().unwrap().add_task(
task_id,
@ -169,6 +178,7 @@ impl<T: RenderTarget> RenderTargetList<T> {
gpu_cache,
render_tasks,
clip_store,
deferred_resolves,
);
}
@ -233,6 +243,20 @@ pub struct ScalingInfo {
pub dest_task_id: RenderTaskId,
}
// Defines where the source data for a blit job can be found.
#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))]
pub enum BlitJobSource {
Texture(SourceTexture, i32, DeviceIntRect),
RenderTask(RenderTaskId),
}
// Information required to do a blit from a source to a target.
#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))]
pub struct BlitJob {
pub source: BlitJobSource,
pub target_rect: DeviceIntRect,
}
/// A render target represents a number of rendering operations on a surface.
#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))]
pub struct ColorRenderTarget {
@ -242,6 +266,7 @@ pub struct ColorRenderTarget {
pub horizontal_blurs: Vec<BlurInstance>,
pub readbacks: Vec<DeviceIntRect>,
pub scalings: Vec<ScalingInfo>,
pub blits: Vec<BlitJob>,
// List of frame buffer outputs for this render target.
pub outputs: Vec<FrameOutput>,
allocator: Option<TextureAllocator>,
@ -266,6 +291,7 @@ impl RenderTarget for ColorRenderTarget {
horizontal_blurs: Vec::new(),
readbacks: Vec::new(),
scalings: Vec::new(),
blits: Vec::new(),
allocator: size.map(TextureAllocator::new),
outputs: Vec::new(),
alpha_tasks: Vec::new(),
@ -292,9 +318,10 @@ impl RenderTarget for ColorRenderTarget {
&mut self,
task_id: RenderTaskId,
ctx: &RenderTargetContext,
_: &GpuCache,
gpu_cache: &mut GpuCache,
render_tasks: &RenderTaskTree,
_: &ClipStore,
deferred_resolves: &mut Vec<DeferredResolve>,
) {
let task = &render_tasks[task_id];
@ -354,6 +381,51 @@ impl RenderTarget for ColorRenderTarget {
dest_task_id: task_id,
});
}
RenderTaskKind::Blit(ref task_info) => {
match task_info.source {
BlitSource::Image { key } => {
// Get the cache item for the source texture.
let cache_item = resolve_image(
key.image_key,
key.image_rendering,
key.tile_offset,
ctx.resource_cache,
gpu_cache,
deferred_resolves,
);
// Work out a source rect to copy from the texture, depending on whether
// a sub-rect is present or not.
// TODO(gw): We have much type confusion below - f32, i32 and u32 for
// various representations of the texel rects. We should make
// this consistent!
let source_rect = key.texel_rect.map_or(cache_item.uv_rect.to_i32(), |sub_rect| {
DeviceIntRect::new(
DeviceIntPoint::new(
cache_item.uv_rect.origin.x as i32 + sub_rect.origin.x,
cache_item.uv_rect.origin.y as i32 + sub_rect.origin.y,
),
sub_rect.size,
)
});
// Store the blit job for the renderer to execute, including
// the allocated destination rect within this target.
let (target_rect, _) = task.get_target_rect();
self.blits.push(BlitJob {
source: BlitJobSource::Texture(
cache_item.texture_id,
cache_item.texture_layer,
source_rect,
),
target_rect,
});
}
BlitSource::RenderTask { .. } => {
panic!("BUG: render task blit jobs to render tasks not supported");
}
}
}
}
}
@ -407,9 +479,10 @@ impl RenderTarget for AlphaRenderTarget {
&mut self,
task_id: RenderTaskId,
ctx: &RenderTargetContext,
gpu_cache: &GpuCache,
gpu_cache: &mut GpuCache,
render_tasks: &RenderTaskTree,
clip_store: &ClipStore,
_: &mut Vec<DeferredResolve>,
) {
let task = &render_tasks[task_id];
@ -424,8 +497,9 @@ impl RenderTarget for AlphaRenderTarget {
}
match task.kind {
RenderTaskKind::Readback(..) => {
panic!("Should not be added to alpha target!");
RenderTaskKind::Readback(..) |
RenderTaskKind::Blit(..) => {
panic!("BUG: should not be added to alpha target!");
}
RenderTaskKind::VerticalBlur(ref info) => {
info.add_instances(
@ -543,6 +617,7 @@ impl RenderTarget for AlphaRenderTarget {
#[cfg_attr(feature = "capture", derive(Deserialize, Serialize))]
pub struct TextureCacheRenderTarget {
pub horizontal_blurs: Vec<BlurInstance>,
pub blits: Vec<BlitJob>,
}
impl TextureCacheRenderTarget {
@ -552,6 +627,7 @@ impl TextureCacheRenderTarget {
) -> Self {
TextureCacheRenderTarget {
horizontal_blurs: Vec::new(),
blits: Vec::new(),
}
}
@ -572,6 +648,24 @@ impl TextureCacheRenderTarget {
render_tasks,
);
}
RenderTaskKind::Blit(ref task_info) => {
match task_info.source {
BlitSource::Image { .. } => {
// reading/writing from the texture cache at the same time
// is undefined behavior.
panic!("bug: a single blit cannot be to/from texture cache");
}
BlitSource::RenderTask { task_id } => {
// Add a blit job to copy from an existing render
// task to this target.
let (target_rect, _) = task.get_target_rect();
self.blits.push(BlitJob {
source: BlitJobSource::RenderTask(task_id),
target_rect,
});
}
}
}
RenderTaskKind::VerticalBlur(..) |
RenderTaskKind::Picture(..) |
RenderTaskKind::CacheMask(..) |
@ -658,7 +752,14 @@ impl RenderPass {
for &task_id in &self.tasks {
assert_eq!(render_tasks[task_id].target_kind(), RenderTargetKind::Color);
render_tasks[task_id].pass_index = Some(pass_index);
target.add_task(task_id, ctx, gpu_cache, render_tasks, clip_store);
target.add_task(
task_id,
ctx,
gpu_cache,
render_tasks,
clip_store,
deferred_resolves,
);
}
target.build(ctx, gpu_cache, render_tasks, deferred_resolves);
}
@ -706,8 +807,22 @@ impl RenderPass {
}
None => {
match target_kind {
RenderTargetKind::Color => color.add_task(task_id, ctx, gpu_cache, render_tasks, clip_store),
RenderTargetKind::Alpha => alpha.add_task(task_id, ctx, gpu_cache, render_tasks, clip_store),
RenderTargetKind::Color => color.add_task(
task_id,
ctx,
gpu_cache,
render_tasks,
clip_store,
deferred_resolves,
),
RenderTargetKind::Alpha => alpha.add_task(
task_id,
ctx,
gpu_cache,
render_tasks,
clip_store,
deferred_resolves,
),
}
}
}
@ -761,14 +876,12 @@ pub struct Frame {
// List of updates that need to be pushed to the
// gpu resource cache.
#[cfg_attr(feature = "capture", serde(skip))]
pub gpu_cache_updates: Option<GpuCacheUpdateList>,
// List of textures that we don't know about yet
// from the backend thread. The render thread
// will use a callback to resolve these and
// patch the data structures.
#[cfg_attr(feature = "capture", serde(skip))]
pub deferred_resolves: Vec<DeferredResolve>,
// True if this frame contains any render tasks

View File

@ -13,6 +13,7 @@ use std::cell::Cell;
use std::fmt;
use std::marker::PhantomData;
use std::path::PathBuf;
use std::u32;
pub type TileSize = u16;
/// Documents are rendered in the ascending order of their associated layer values.
@ -430,6 +431,7 @@ bitflags!{
pub struct CapturedDocument {
pub document_id: DocumentId,
pub root_pipeline_id: Option<PipelineId>,
pub window_size: DeviceUintSize,
}
#[derive(Clone, Deserialize, Serialize)]
@ -520,6 +522,12 @@ impl fmt::Debug for ApiMsg {
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Epoch(pub u32);
impl Epoch {
pub fn invalid() -> Epoch {
Epoch(u32::MAX)
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
pub struct IdNamespace(pub u32);

View File

@ -47,12 +47,12 @@ pub struct ColorF {
impl ColorF {
/// Constructs a new `ColorF` from its components.
pub fn new(r: f32, g: f32, b: f32, a: f32) -> ColorF {
pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
ColorF { r, g, b, a }
}
/// Multiply the RGB channels (but not alpha) with a given factor.
pub fn scale_rgb(&self, scale: f32) -> ColorF {
pub fn scale_rgb(&self, scale: f32) -> Self {
ColorF {
r: self.r * scale,
g: self.g * scale,

View File

@ -13,12 +13,10 @@ use std::sync::Arc;
pub struct ImageKey(pub IdNamespace, pub u32);
impl ImageKey {
pub fn new(namespace: IdNamespace, key: u32) -> ImageKey {
ImageKey(namespace, key)
}
pub const DUMMY: Self = ImageKey(IdNamespace(0), 0);
pub fn dummy() -> ImageKey {
ImageKey(IdNamespace(0), 0)
pub fn new(namespace: IdNamespace, key: u32) -> Self {
ImageKey(namespace, key)
}
}
@ -29,14 +27,19 @@ impl ImageKey {
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct ExternalImageId(pub u64);
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum TextureTarget {
Default = 0,
Array = 1,
Rect = 2,
External = 3,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum ExternalImageType {
Texture2DHandle, // gl TEXTURE_2D handle
Texture2DArrayHandle, // gl TEXTURE_2D_ARRAY handle
TextureRectHandle, // gl TEXTURE_RECT handle
TextureExternalHandle, // gl TEXTURE_EXTERNAL handle
ExternalBuffer,
TextureHandle(TextureTarget),
Buffer,
}
#[repr(C)]
@ -93,6 +96,10 @@ impl ImageDescriptor {
self.stride
.unwrap_or(self.width * self.format.bytes_per_pixel())
}
pub fn compute_total_size(&self) -> u32 {
self.compute_stride() * self.height
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
@ -103,15 +110,15 @@ pub enum ImageData {
}
impl ImageData {
pub fn new(bytes: Vec<u8>) -> ImageData {
pub fn new(bytes: Vec<u8>) -> Self {
ImageData::Raw(Arc::new(bytes))
}
pub fn new_shared(bytes: Arc<Vec<u8>>) -> ImageData {
pub fn new_shared(bytes: Arc<Vec<u8>>) -> Self {
ImageData::Raw(bytes)
}
pub fn new_blob_image(commands: Vec<u8>) -> ImageData {
pub fn new_blob_image(commands: Vec<u8>) -> Self {
ImageData::Blob(commands)
}
@ -125,16 +132,13 @@ impl ImageData {
#[inline]
pub fn uses_texture_cache(&self) -> bool {
match self {
&ImageData::External(ext_data) => match ext_data.image_type {
ExternalImageType::Texture2DHandle => false,
ExternalImageType::Texture2DArrayHandle => false,
ExternalImageType::TextureRectHandle => false,
ExternalImageType::TextureExternalHandle => false,
ExternalImageType::ExternalBuffer => true,
match *self {
ImageData::External(ref ext_data) => match ext_data.image_type {
ExternalImageType::TextureHandle(_) => false,
ExternalImageType::Buffer => true,
},
&ImageData::Blob(_) => true,
&ImageData::Raw(_) => true,
ImageData::Blob(_) => true,
ImageData::Raw(_) => true,
}
}
}

View File

@ -114,3 +114,30 @@ pub fn as_scroll_parent_rect(rect: &LayerRect) -> ScrollLayerRect {
pub fn as_scroll_parent_vector(vector: &LayerVector2D) -> ScrollLayerVector2D {
ScrollLayerVector2D::from_untyped(&vector.to_untyped())
}
/// Stores two coordinates in texel space. The coordinates
/// are stored in texel coordinates because the texture atlas
/// may grow. Storing them as texel coords and normalizing
/// the UVs in the vertex shader means nothing needs to be
/// updated on the CPU when the texture size changes.
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct TexelRect {
pub uv0: DevicePoint,
pub uv1: DevicePoint,
}
impl TexelRect {
pub fn new(u0: f32, v0: f32, u1: f32, v1: f32) -> Self {
TexelRect {
uv0: DevicePoint::new(u0, v0),
uv1: DevicePoint::new(u1, v1),
}
}
pub fn invalid() -> Self {
TexelRect {
uv0: DevicePoint::new(-1.0, -1.0),
uv1: DevicePoint::new(-1.0, -1.0),
}
}
}

View File

@ -9,7 +9,7 @@ rayon = "0.8"
thread_profiler = "0.1.1"
euclid = "0.16"
app_units = "0.6"
gleam = "0.4.19"
gleam = "0.4.20"
log = "0.3"
[dependencies.webrender]

View File

@ -28,8 +28,31 @@ use core_foundation::string::CFString;
#[cfg(target_os = "macos")]
use core_graphics::font::CGFont;
/// cbindgen:field-names=[mNamespace, mHandle]
type WrExternalImageBufferType = ExternalImageType;
#[repr(C)]
pub enum WrExternalImageBufferType {
TextureHandle = 0,
TextureRectHandle = 1,
TextureArrayHandle = 2,
TextureExternalHandle = 3,
ExternalBuffer = 4,
}
impl WrExternalImageBufferType {
fn to_wr(self) -> ExternalImageType {
match self {
WrExternalImageBufferType::TextureHandle =>
ExternalImageType::TextureHandle(TextureTarget::Default),
WrExternalImageBufferType::TextureRectHandle =>
ExternalImageType::TextureHandle(TextureTarget::Rect),
WrExternalImageBufferType::TextureArrayHandle =>
ExternalImageType::TextureHandle(TextureTarget::Array),
WrExternalImageBufferType::TextureExternalHandle =>
ExternalImageType::TextureHandle(TextureTarget::External),
WrExternalImageBufferType::ExternalBuffer =>
ExternalImageType::Buffer,
}
}
}
/// cbindgen:field-names=[mHandle]
/// cbindgen:derive-lt=true
@ -306,34 +329,19 @@ impl ExternalImageHandler for WrExternalImageHandler {
channel_index: u8)
-> ExternalImage {
let image = (self.lock_func)(self.external_image_obj, id.into(), channel_index);
match image.image_type {
WrExternalImageType::NativeTexture => {
ExternalImage {
uv: TexelRect::new(image.u0, image.v0, image.u1, image.v1),
//uv: UvRect::new(UvPoint::new(image.u0, image.v0), UvSize::new(image.u1, image.v1)),
/*
u0: image.u0,
v0: image.v0,
u1: image.u1,
v1: image.v1,
source: ExternalImageSource::NativeTexture(image.handle),
}
},
WrExternalImageType::RawData => {
ExternalImage {
u0: image.u0,
v0: image.v0,
u1: image.u1,
v1: image.v1,
source: ExternalImageSource::RawData(make_slice(image.buff, image.size)),
}
},
WrExternalImageType::Invalid => {
ExternalImage {
u0: image.u0,
v0: image.v0,
u1: image.u1,
v1: image.v1,
source: ExternalImageSource::Invalid,
}
*/
source: match image.image_type {
WrExternalImageType::NativeTexture => ExternalImageSource::NativeTexture(image.handle),
WrExternalImageType::RawData => ExternalImageSource::RawData(make_slice(image.buff, image.size)),
WrExternalImageType::Invalid => ExternalImageSource::Invalid,
},
}
}
@ -1010,7 +1018,7 @@ pub extern "C" fn wr_resource_updates_add_external_image(
ExternalImageData {
id: external_image_id.into(),
channel_index: channel_index,
image_type: buffer_type,
image_type: buffer_type.to_wr(),
}
),
None
@ -1048,7 +1056,7 @@ pub extern "C" fn wr_resource_updates_update_external_image(
ExternalImageData {
id: external_image_id.into(),
channel_index,
image_type,
image_type: image_type.to_wr(),
}
),
None

View File

@ -53,16 +53,6 @@ enum class ExtendMode : uint32_t {
Sentinel /* this must be last for serialization purposes. */
};
enum class ExternalImageType : uint32_t {
Texture2DHandle = 0,
Texture2DArrayHandle = 1,
TextureRectHandle = 2,
TextureExternalHandle = 3,
ExternalBuffer = 4,
Sentinel /* this must be last for serialization purposes. */
};
#if !(defined(XP_MACOSX) || defined(XP_WIN))
enum class FontHinting : uint8_t {
None = 0,
@ -204,6 +194,16 @@ enum class WrAnimationType : uint32_t {
Sentinel /* this must be last for serialization purposes. */
};
enum class WrExternalImageBufferType {
TextureHandle = 0,
TextureRectHandle = 1,
TextureArrayHandle = 2,
TextureExternalHandle = 3,
ExternalBuffer = 4,
Sentinel /* this must be last for serialization purposes. */
};
enum class WrExternalImageType : uint32_t {
RawData = 0,
NativeTexture = 1,
@ -843,8 +843,6 @@ struct WrImageDescriptor {
}
};
using WrExternalImageBufferType = ExternalImageType;
// Represents RGBA screen colors with one byte per channel.
//
// If the alpha value `a` is 255 the color is opaque.

View File

@ -93,14 +93,11 @@ bool HasRTLChars(const char16_t* aText, uint32_t aLength)
const char16_t* cp = aText;
const char16_t* end = cp + aLength;
while (cp < end) {
uint32_t ch = *cp++;
char16_t ch = *cp++;
if (ch < mozilla::kMinRTLChar) {
continue;
}
if (NS_IS_HIGH_SURROGATE(ch) && cp < end && NS_IS_LOW_SURROGATE(*cp)) {
ch = SURROGATE_TO_UCS4(ch, *cp++);
}
if (UTF32_CHAR_IS_BIDI(ch) || IsBidiControlRTL(ch)) {
if (UTF16_CODE_UNIT_IS_BIDI(ch) || IsBidiControlRTL(ch)) {
return true;
}
}

View File

@ -873,7 +873,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
}
}
# if defined(XP_WIN) && defined(MOZ_SANDBOX)
# if defined(MOZ_SANDBOX)
bool shouldSandboxCurrentProcess = false;
// XXX: Bug 1124167: We should get rid of the process specific logic for
@ -954,7 +954,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
mSandboxBroker.AllowReadFile(it->c_str());
}
}
# endif // defined(XP_WIN) && defined(MOZ_SANDBOX)
# endif // defined(MOZ_SANDBOX)
// Add the application directory path (-appdir path)
AddAppDirToCommandLine(cmdLine);
@ -988,7 +988,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
// Process type
cmdLine.AppendLooseValue(UTF8ToWide(childProcessType));
# if defined(XP_WIN) && defined(MOZ_SANDBOX)
# if defined(MOZ_SANDBOX)
if (shouldSandboxCurrentProcess) {
if (mSandboxBroker.LaunchApp(cmdLine.program().c_str(),
cmdLine.command_line_string().c_str(),
@ -1002,7 +1002,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
cmdLine.command_line_string().c_str());
}
} else
# endif // defined(XP_WIN) && defined(MOZ_SANDBOX)
# endif // defined(MOZ_SANDBOX)
{
base::LaunchApp(cmdLine, *mLaunchOptions, &process);

View File

@ -1028,8 +1028,12 @@ AccessibleCaretManager::ClearMaintainedSelection() const
void
AccessibleCaretManager::FlushLayout() const
{
if (mPresShell) {
mPresShell->FlushPendingNotifications(FlushType::Layout);
if (!mPresShell) {
return;
}
if (nsIDocument* doc = mPresShell->GetDocument()) {
doc->FlushPendingNotifications(FlushType::Layout);
}
}

View File

@ -4102,6 +4102,8 @@ PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush)
FlushType flushType = aFlush.mFlushType;
MOZ_ASSERT(NeedFlush(flushType), "Why did we get called?");
MOZ_DIAGNOSTIC_ASSERT(mDocument->HasShellOrBFCacheEntry());
MOZ_DIAGNOSTIC_ASSERT(mDocument->GetShell() == this);
#ifdef MOZ_GECKO_PROFILER
static const EnumeratedArray<FlushType,
@ -4123,10 +4125,10 @@ PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush)
GRAPHICS, flushTypeNames[flushType]);
#endif
#ifdef ACCESSIBILITY
#ifdef DEBUG
nsAccessibilityService* accService = GetAccService();
if (accService) {
if (nsAccessibilityService* accService = GetAccService()) {
NS_ASSERTION(!accService->IsProcessingRefreshDriverNotification(),
"Flush during accessible tree update!");
}

View File

@ -2438,6 +2438,12 @@ nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument)
}
if (aDocument->LoadsFullXULStyleSheetUpFront()) {
// This is the only place components.css gets loaded, unlike xul.css
sheet = cache->XULComponentsSheet();
if (sheet) {
styleSet->PrependStyleSheet(SheetType::Agent, sheet);
}
// nsXULElement::BindToTree loads xul.css on-demand if we don't load it
// up-front here.
sheet = cache->XULSheet();

View File

@ -156,6 +156,17 @@ nsLayoutStylesheetCache::XULSheet()
return mXULSheet;
}
StyleSheet*
nsLayoutStylesheetCache::XULComponentsSheet()
{
if (!mXULComponentsSheet) {
LoadSheetURL("chrome://global/content/components.css",
&mXULComponentsSheet, eAgentSheetFeatures, eCrash);
}
return mXULComponentsSheet;
}
StyleSheet*
nsLayoutStylesheetCache::QuirkSheet()
{
@ -313,6 +324,7 @@ nsLayoutStylesheetCache::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf
MEASURE(mUserChromeSheet);
MEASURE(mUserContentSheet);
MEASURE(mXULSheet);
MEASURE(mXULComponentsSheet);
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
@ -353,6 +365,7 @@ nsLayoutStylesheetCache::nsLayoutStylesheetCache(StyleBackendType aType)
if (XRE_IsParentProcess()) {
// We know we need xul.css for the UI, so load that now too:
XULSheet();
XULComponentsSheet();
}
auto& userContentSheetURL = aType == StyleBackendType::Gecko ?

View File

@ -63,6 +63,7 @@ class nsLayoutStylesheetCache final
mozilla::StyleSheet* HTMLSheet();
mozilla::StyleSheet* MinimalXULSheet();
mozilla::StyleSheet* XULSheet();
mozilla::StyleSheet* XULComponentsSheet();
mozilla::StyleSheet* QuirkSheet();
mozilla::StyleSheet* SVGSheet();
mozilla::StyleSheet* MathMLSheet();
@ -131,6 +132,7 @@ private:
RefPtr<mozilla::StyleSheet> mUserChromeSheet;
RefPtr<mozilla::StyleSheet> mUserContentSheet;
RefPtr<mozilla::StyleSheet> mXULSheet;
RefPtr<mozilla::StyleSheet> mXULComponentsSheet;
};
#endif

View File

@ -9,4 +9,4 @@ https://github.com/kinetiknz/cubeb/pull/398/commits/2ed979bc891cf1a7822799947a35
The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
The git commit ID used was bda37c26b0ed46c568e35b10728fc498262778f3 (2017-12-28 09:34:08 +1000)
The git commit ID used was 2b98e3d3279322ce0764ae2b9f28a9ea9000418b (2018-01-19 10:17:28 +1300)

View File

@ -15,6 +15,17 @@
#include "cubeb/cubeb.h"
#include "common.h"
long data_cb_duplex(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
{
// noop, unused
return 0;
}
void state_cb_duplex(cubeb_stream * stream, void * /*user*/, cubeb_state state)
{
// noop, unused
}
static void
print_device_info(cubeb_device_info * info, FILE * f)
{
@ -164,4 +175,32 @@ TEST(cubeb, enumerate_devices)
fprintf(stdout, "Found %zu output devices\n", collection.count);
print_device_collection(&collection, stdout);
cubeb_device_collection_destroy(ctx, &collection);
uint32_t count_before_creating_duplex_stream;
r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection);
ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
count_before_creating_duplex_stream = collection.count;
cubeb_device_collection_destroy(ctx, &collection);
cubeb_stream * stream;
cubeb_stream_params input_params;
cubeb_stream_params output_params;
input_params.format = output_params.format = CUBEB_SAMPLE_FLOAT32NE;
input_params.rate = output_params.rate = 48000;
input_params.channels = output_params.channels = 1;
input_params.layout = output_params.layout = CUBEB_LAYOUT_MONO;
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
NULL, &input_params, NULL, &output_params,
1024, data_cb_duplex, state_cb_duplex, nullptr);
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection);
ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
ASSERT_EQ(count_before_creating_duplex_stream, collection.count);
cubeb_device_collection_destroy(ctx, &collection);
cubeb_stream_destroy(stream);
}

View File

@ -43,6 +43,7 @@ typedef UInt32 AudioFormatFlags;
#define AU_IN_BUS 1
const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb";
const char * PRIVATE_AGGREGATE_DEVICE_NAME = "CubebAggregateDevice";
#ifdef ALOGV
#undef ALOGV
@ -1497,11 +1498,11 @@ audiounit_create_blank_aggregate_device(AudioObjectID * plugin_id, AudioDeviceID
struct timeval timestamp;
gettimeofday(&timestamp, NULL);
long long int time_id = timestamp.tv_sec * 1000000LL + timestamp.tv_usec;
CFStringRef aggregate_device_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("CubebAggregateDevice_%llx"), time_id);
CFStringRef aggregate_device_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id);
CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceNameKey), aggregate_device_name);
CFRelease(aggregate_device_name);
CFStringRef aggregate_device_UID = CFStringCreateWithFormat(NULL, NULL, CFSTR("org.mozilla.CubebAggregateDevice_%llx"), time_id);
CFStringRef aggregate_device_UID = CFStringCreateWithFormat(NULL, NULL, CFSTR("org.mozilla.%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id);
CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceUIDKey), aggregate_device_UID);
CFRelease(aggregate_device_UID);
@ -3181,6 +3182,13 @@ audiounit_create_device_from_hwdev(cubeb_device_info * ret, AudioObjectID devid,
return CUBEB_OK;
}
bool
is_aggregate_device(cubeb_device_info * device_info)
{
return !strncmp(device_info->friendly_name, PRIVATE_AGGREGATE_DEVICE_NAME,
strlen(PRIVATE_AGGREGATE_DEVICE_NAME));
}
static int
audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type,
cubeb_device_collection * collection)
@ -3209,7 +3217,7 @@ audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type,
for (auto dev: output_devs) {
auto device = &devices[collection->count];
auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_OUTPUT);
if (err != CUBEB_OK) {
if (err != CUBEB_OK || is_aggregate_device(device)) {
continue;
}
collection->count += 1;
@ -3220,7 +3228,7 @@ audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type,
for (auto dev: input_devs) {
auto device = &devices[collection->count];
auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_INPUT);
if (err != CUBEB_OK) {
if (err != CUBEB_OK || is_aggregate_device(device)) {
continue;
}
collection->count += 1;

View File

@ -231,9 +231,10 @@ load_jack_lib(cubeb * context)
return CUBEB_OK;
}
static void
static int
cbjack_connect_ports (cubeb_stream * stream)
{
int r = CUBEB_ERROR;
const char ** phys_in_ports = api_jack_get_ports (stream->context->jack_client,
NULL, NULL,
JackPortIsInput
@ -243,7 +244,7 @@ cbjack_connect_ports (cubeb_stream * stream)
JackPortIsOutput
| JackPortIsPhysical);
if (*phys_in_ports == NULL) {
if (phys_in_ports == NULL || *phys_in_ports == NULL) {
goto skipplayback;
}
@ -253,9 +254,10 @@ cbjack_connect_ports (cubeb_stream * stream)
api_jack_connect (stream->context->jack_client, src_port, phys_in_ports[c]);
}
r = CUBEB_OK;
skipplayback:
if (*phys_out_ports == NULL) {
if (phys_out_ports == NULL || *phys_out_ports == NULL) {
goto end;
}
// Connect inputs to capture
@ -264,9 +266,15 @@ skipplayback:
api_jack_connect (stream->context->jack_client, phys_out_ports[c], src_port);
}
r = CUBEB_OK;
end:
if (phys_out_ports) {
api_jack_free(phys_out_ports);
}
if (phys_in_ports) {
api_jack_free(phys_in_ports);
}
return r;
}
static int
@ -866,7 +874,11 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
}
}
cbjack_connect_ports(stm);
if (cbjack_connect_ports(stm) != CUBEB_OK) {
pthread_mutex_unlock(&stm->mutex);
cbjack_stream_destroy(stm);
return CUBEB_ERROR;
}
*stream = stm;

View File

@ -460,7 +460,7 @@ cubeb_upmix(long inframes,
/* Put silence in remaining channels. */
for (long i = 0, o = 0; i < inframes; ++i, o += out_channels) {
for (unsigned int j = 2; j < out_channels; ++j) {
for (unsigned int j = in_channels; j < out_channels; ++j) {
assert((unsigned long)o + j < out_len);
out[o + j] = 0.0;
}

View File

@ -1565,6 +1565,7 @@ pulse_register_device_collection_changed(cubeb * context,
pa_operation * o;
o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success, context);
if (o == NULL) {
WRAP(pa_threaded_mainloop_unlock)(context->mainloop);
LOG("Context subscribe failed");
return CUBEB_ERROR;
}

View File

@ -1190,7 +1190,7 @@ int wasapi_init(cubeb ** context, char const * context_name)
ctx->ops = &wasapi_ops;
if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) {
free(ctx);
delete ctx;
return CUBEB_ERROR;
}
@ -1671,6 +1671,10 @@ int setup_wasapi_stream(cubeb_stream * stm)
stm->input_available_event,
stm->capture_client,
&stm->input_mix_params);
if (rv != CUBEB_OK) {
LOG("Failure to open the input side.");
return rv;
}
// We initializing an input stream, buffer ahead two buffers worth of silence.
// This delays the input side slightly, but allow to not glitch when no input
@ -1680,16 +1684,11 @@ int setup_wasapi_stream(cubeb_stream * stm)
#if !defined(DEBUG)
const int silent_buffer_count = 2;
#else
const int silent_buffer_count = 4;
const int silent_buffer_count = 6;
#endif
stm->linear_input_buffer->push_silence(stm->input_buffer_frame_count *
stm->input_stream_params.channels *
silent_buffer_count);
if (rv != CUBEB_OK) {
LOG("Failure to open the input side.");
return rv;
}
}
if (has_output(stm)) {

View File

@ -402,6 +402,7 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
XASSERT(context);
XASSERT(stream);
XASSERT(output_stream_params);
if (input_stream_params) {
/* Capture support not yet implemented. */

View File

@ -94,21 +94,14 @@ nsJARURI::CreateEntryURL(const nsACString& entryFilename,
nsIURL** url)
{
*url = nullptr;
nsCOMPtr<nsIStandardURL> stdURL(do_CreateInstance(NS_STANDARDURL_CONTRACTID));
if (!stdURL) {
return NS_ERROR_OUT_OF_MEMORY;
}
// Flatten the concatenation, just in case. See bug 128288
nsAutoCString spec(NS_BOGUS_ENTRY_SCHEME + entryFilename);
nsresult rv = stdURL->Init(nsIStandardURL::URLTYPE_NO_AUTHORITY, -1,
spec, charset, nullptr);
if (NS_FAILED(rv)) {
return rv;
}
return CallQueryInterface(stdURL, url);
return NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
.Apply<nsIStandardURLMutator>(&nsIStandardURLMutator::Init,
nsIStandardURL::URLTYPE_NO_AUTHORITY, -1,
spec, charset, nullptr,
nullptr)
.Finalize(url);
}
////////////////////////////////////////////////////////////////////////////////
@ -299,14 +292,18 @@ nsJARURI::SetSpecWithBase(const nsACString &aSpec, nsIURI* aBaseURL)
mJARFile = otherJAR->mJARFile;
nsCOMPtr<nsIStandardURL> entry(do_CreateInstance(NS_STANDARDURL_CONTRACTID));
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsIURI> entry;
rv = entry->Init(nsIStandardURL::URLTYPE_NO_AUTHORITY, -1,
aSpec, mCharsetHint.get(), otherJAR->mJAREntry);
if (NS_FAILED(rv))
rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
.Apply<nsIStandardURLMutator>(&nsIStandardURLMutator::Init,
nsIStandardURL::URLTYPE_NO_AUTHORITY, -1,
nsCString(aSpec), mCharsetHint.get(),
otherJAR->mJAREntry,
nullptr)
.Finalize(entry);
if (NS_FAILED(rv)) {
return rv;
}
mJAREntry = do_QueryInterface(entry);
if (!mJAREntry)

View File

@ -6,6 +6,7 @@
#include "nsIMutable.idl"
interface nsIURI;
interface nsIURIMutator;
/**
* nsIStandardURL defines the interface to an URL with the standard
@ -40,6 +41,18 @@ interface nsIStandardURL : nsIMutable
*/
const unsigned long URLTYPE_NO_AUTHORITY = 3;
void init(in unsigned long aUrlType,
in long aDefaultPort,
in AUTF8String aSpec,
in string aOriginCharset,
in nsIURI aBaseURI);
void setDefaultPort(in long aNewDefaultPort);
};
[scriptable, builtinclass, uuid(fc894e98-23a1-43cd-a7fe-72876f8ea2ee)]
interface nsIStandardURLMutator : nsISupports
{
/**
* Initialize a standard URL.
*
@ -60,7 +73,7 @@ interface nsIStandardURL : nsIMutable
* otherwise, aSpec will be resolved relative
* to aBaseURI.
*/
void init(in unsigned long aUrlType,
nsIURIMutator init(in unsigned long aUrlType,
in long aDefaultPort,
in AUTF8String aSpec,
in string aOriginCharset,
@ -76,5 +89,5 @@ interface nsIStandardURL : nsIMutable
* matches this default, then we won't include a
* port number in the canonical form of the URL.
*/
void setDefaultPort(in long aNewDefaultPort);
nsIURIMutator setDefaultPort(in long aNewDefaultPort);
};

View File

@ -2861,24 +2861,20 @@ NS_ShouldSecureUpgrade(nsIURI* aURI,
nsresult
NS_GetSecureUpgradedURI(nsIURI* aURI, nsIURI** aUpgradedURI)
{
nsCOMPtr<nsIURI> upgradedURI;
nsresult rv = aURI->Clone(getter_AddRefs(upgradedURI));
NS_ENSURE_SUCCESS(rv,rv);
// Change the scheme to HTTPS:
upgradedURI->SetScheme(NS_LITERAL_CSTRING("https"));
NS_MutateURI mutator(aURI);
mutator.SetScheme(NS_LITERAL_CSTRING("https")); // Change the scheme to HTTPS:
// Change the default port to 443:
nsCOMPtr<nsIStandardURL> upgradedStandardURL = do_QueryInterface(upgradedURI);
if (upgradedStandardURL) {
upgradedStandardURL->SetDefaultPort(443);
nsCOMPtr<nsIStandardURL> stdURL = do_QueryInterface(aURI);
if (stdURL) {
mutator.Apply<nsIStandardURLMutator>(&nsIStandardURLMutator::SetDefaultPort, 443, nullptr);
} else {
// If we don't have a nsStandardURL, fall back to using GetPort/SetPort.
// XXXdholbert Is this function even called with a non-nsStandardURL arg,
// in practice?
NS_WARNING("Calling NS_GetSecureUpgradedURI for non nsStandardURL");
int32_t oldPort = -1;
rv = aURI->GetPort(&oldPort);
nsresult rv = aURI->GetPort(&oldPort);
if (NS_FAILED(rv)) return rv;
// Keep any nonstandard ports so only the scheme is changed.
@ -2887,14 +2883,13 @@ NS_GetSecureUpgradedURI(nsIURI* aURI, nsIURI** aUpgradedURI)
// http://foo.com:81 -> https://foo.com:81
if (oldPort == 80 || oldPort == -1) {
upgradedURI->SetPort(-1);
mutator.SetPort(-1);
} else {
upgradedURI->SetPort(oldPort);
mutator.SetPort(oldPort);
}
}
upgradedURI.forget(aUpgradedURI);
return NS_OK;
return mutator.Finalize(aUpgradedURI);
}
nsresult

View File

@ -2226,7 +2226,7 @@ nsStandardURL::SetPathQueryRef(const nsACString &input)
return NS_OK;
}
NS_IMPL_ISUPPORTS(nsStandardURL::Mutator, nsIURISetters, nsIURIMutator)
NS_IMPL_ISUPPORTS(nsStandardURL::Mutator, nsIURISetters, nsIURIMutator, nsIStandardURLMutator)
NS_IMETHODIMP
nsStandardURL::Mutate(nsIURIMutator** aMutator)

View File

@ -313,19 +313,98 @@ public:
#endif
public:
class Mutator
// We make this implementation a template so that we can avoid writing
// the same code for SubstitutingURL (which extends nsStandardURL)
template<class T>
class TemplatedMutator
: public nsIURIMutator
, public BaseURIMutator<nsStandardURL>
, public BaseURIMutator<T>
, public nsIStandardURLMutator
{
NS_FORWARD_SAFE_NSIURISETTERS_RET(BaseURIMutator<T>::mURI)
MOZ_MUST_USE NS_IMETHOD
Deserialize(const mozilla::ipc::URIParams& aParams) override
{
return BaseURIMutator<T>::InitFromIPCParams(aParams);
}
MOZ_MUST_USE NS_IMETHOD
Read(nsIObjectInputStream* aStream) override
{
return BaseURIMutator<T>::InitFromInputStream(aStream);
}
MOZ_MUST_USE NS_IMETHOD
Finalize(nsIURI** aURI) override
{
BaseURIMutator<T>::mURI.forget(aURI);
return NS_OK;
}
MOZ_MUST_USE NS_IMETHOD
SetSpec(const nsACString& aSpec, nsIURIMutator** aMutator) override
{
if (aMutator) {
nsCOMPtr<nsIURIMutator> mutator = this;
mutator.forget(aMutator);
}
return BaseURIMutator<T>::InitFromSpec(aSpec);
}
MOZ_MUST_USE NS_IMETHOD
Init(uint32_t aURLType, int32_t aDefaultPort,
const nsACString& aSpec, const char* aCharset, nsIURI* aBaseURI,
nsIURIMutator** aMutator) override
{
if (aMutator) {
nsCOMPtr<nsIURIMutator> mutator = this;
mutator.forget(aMutator);
}
RefPtr<T> uri;
if (BaseURIMutator<T>::mURI) {
// We don't need a new URI object if we already have one
BaseURIMutator<T>::mURI.swap(uri);
} else {
uri = new T();
}
nsresult rv = uri->Init(aURLType, aDefaultPort, aSpec, aCharset, aBaseURI);
if (NS_FAILED(rv)) {
return rv;
}
BaseURIMutator<T>::mURI = uri;
return NS_OK;
}
MOZ_MUST_USE NS_IMETHODIMP
SetDefaultPort(int32_t aNewDefaultPort, nsIURIMutator** aMutator) override
{
if (!BaseURIMutator<T>::mURI) {
return NS_ERROR_NULL_POINTER;
}
if (aMutator) {
nsCOMPtr<nsIURIMutator> mutator = this;
mutator.forget(aMutator);
}
return BaseURIMutator<T>::mURI->SetDefaultPort(aNewDefaultPort);
}
explicit TemplatedMutator() { }
private:
virtual ~TemplatedMutator() { }
friend T;
};
class Mutator
: public TemplatedMutator<nsStandardURL>
{
NS_DECL_ISUPPORTS
NS_FORWARD_SAFE_NSIURISETTERS_RET(mURI)
NS_DEFINE_NSIMUTATOR_COMMON
explicit Mutator() { }
public:
explicit Mutator() = default;
private:
virtual ~Mutator() { }
friend class nsStandardURL;
virtual ~Mutator() = default;
};
};

View File

@ -9,6 +9,7 @@
#include "nsFileChannel.h"
#include "nsStandardURL.h"
#include "nsURLHelper.h"
#include "nsIURIMutator.h"
#include "nsNetUtil.h"
@ -166,23 +167,22 @@ nsFileProtocolHandler::NewURI(const nsACString &spec,
nsIURI *baseURI,
nsIURI **result)
{
nsCOMPtr<nsIStandardURL> url = new nsStandardURL(true);
if (!url)
return NS_ERROR_OUT_OF_MEMORY;
const nsACString *specPtr = &spec;
nsCOMPtr<nsIURI> url = new nsStandardURL(true);
nsAutoCString buf(spec);
#if defined(XP_WIN)
nsAutoCString buf;
if (net_NormalizeFileURL(spec, buf))
specPtr = &buf;
buf.Truncate();
if (!net_NormalizeFileURL(spec, buf)) {
buf = spec;
}
#endif
nsresult rv = url->Init(nsIStandardURL::URLTYPE_NO_AUTHORITY, -1,
*specPtr, charset, baseURI);
if (NS_FAILED(rv)) return rv;
return CallQueryInterface(url, result);
return NS_MutateURI(url)
.Apply<nsIStandardURLMutator>(&nsIStandardURLMutator::Init,
nsIStandardURL::URLTYPE_NO_AUTHORITY, -1,
buf, charset, baseURI,
nullptr)
.Finalize(result);
}
NS_IMETHODIMP

View File

@ -171,14 +171,13 @@ nsFtpProtocolHandler::NewURI(const nsACString &aSpec,
if (spec.FindCharInSet(CRLF) >= 0 || spec.FindChar('\0') >= 0)
return NS_ERROR_MALFORMED_URI;
nsresult rv;
nsCOMPtr<nsIStandardURL> url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY, 21, aSpec, aCharset, aBaseURI);
if (NS_FAILED(rv)) return rv;
return CallQueryInterface(url, result);
nsCOMPtr<nsIURI> url;
return NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
.Apply<nsIStandardURLMutator>(&nsIStandardURLMutator::Init,
nsIStandardURL::URLTYPE_AUTHORITY, 21,
nsCString(aSpec), aCharset, aBaseURI,
nullptr)
.Finalize(result);
}
NS_IMETHODIMP

View File

@ -20,6 +20,7 @@
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsIURI.h"
#include "nsIURIMutator.h"
#include "nsIAuthPrompt.h"
#include "nsIChannel.h"
#include "nsIInputStream.h"
@ -1026,18 +1027,12 @@ nsGIOProtocolHandler::NewURI(const nsACString &aSpec,
}
}
nsresult rv;
nsCOMPtr<nsIStandardURL> url =
do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
if (NS_FAILED(rv))
return rv;
rv = url->Init(nsIStandardURL::URLTYPE_STANDARD, -1, flatSpec,
aOriginCharset, aBaseURI);
if (NS_SUCCEEDED(rv))
rv = CallQueryInterface(url, aResult);
return rv;
return NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
.Apply<nsIStandardURLMutator>(&nsIStandardURLMutator::Init,
nsIStandardURL::URLTYPE_STANDARD, -1,
flatSpec, aOriginCharset, aBaseURI,
nullptr)
.Finalize(aResult);
}
NS_IMETHODIMP

View File

@ -1888,7 +1888,7 @@ Http2Session::RecvPushPromise(Http2Session *self)
// does the pushed origin belong on this connection?
LOG3(("Http2Session::RecvPushPromise %p origin check %s", self,
pushedStream->Origin().get()));
RefPtr<nsStandardURL> pushedOrigin;
nsCOMPtr<nsIURI> pushedOrigin;
rv = Http2Stream::MakeOriginURL(pushedStream->Origin(), pushedOrigin);
nsAutoCString pushedHostName;
int32_t pushedPort = -1;
@ -1943,7 +1943,7 @@ Http2Session::RecvPushPromise(Http2Session *self)
nsAutoCString spec;
spec.Assign(pushedStream->Origin());
spec.Append(pushedStream->Path());
RefPtr<nsStandardURL> pushedURL;
nsCOMPtr<nsIURI> pushedURL;
// Nifty trick: this doesn't actually do anything origin-specific, it's just
// named that way. So by passing it the full spec here, we get a URL with
// the full path.
@ -2651,7 +2651,7 @@ Http2Session::RecvOrigin(Http2Session *self)
}
nsAutoCString originString;
RefPtr<nsStandardURL> originURL;
nsCOMPtr<nsIURI> originURL;
originString.Assign(self->mInputFrameBuffer.get() + kFrameHeaderBytes + offset + 2, originLen);
offset += originLen + 2;
if (NS_FAILED(Http2Stream::MakeOriginURL(originString, originURL))){

View File

@ -333,7 +333,7 @@ Http2Stream::WriteSegments(nsAHttpSegmentWriter *writer,
}
nsresult
Http2Stream::MakeOriginURL(const nsACString &origin, RefPtr<nsStandardURL> &url)
Http2Stream::MakeOriginURL(const nsACString &origin, nsCOMPtr<nsIURI> &url)
{
nsAutoCString scheme;
nsresult rv = net_ExtractURLScheme(origin, scheme);
@ -343,15 +343,17 @@ Http2Stream::MakeOriginURL(const nsACString &origin, RefPtr<nsStandardURL> &url)
nsresult
Http2Stream::MakeOriginURL(const nsACString &scheme, const nsACString &origin,
RefPtr<nsStandardURL> &url)
nsCOMPtr<nsIURI> &url)
{
url = new nsStandardURL();
nsresult rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY,
return NS_MutateURI(new nsStandardURL::Mutator())
.Apply<nsIStandardURLMutator>(&nsIStandardURLMutator::Init,
nsIStandardURL::URLTYPE_AUTHORITY,
scheme.EqualsLiteral("http") ?
NS_HTTP_DEFAULT_PORT :
NS_HTTPS_DEFAULT_PORT,
origin, nullptr, nullptr);
return rv;
nsCString(origin), nullptr, nullptr,
nullptr)
.Finalize(url);
}
void
@ -367,7 +369,7 @@ Http2Stream::CreatePushHashKey(const nsCString &scheme,
fullOrigin.AppendLiteral("://");
fullOrigin.Append(hostHeader);
RefPtr<nsStandardURL> origin;
nsCOMPtr<nsIURI> origin;
nsresult rv = Http2Stream::MakeOriginURL(scheme, fullOrigin, origin);
if (NS_SUCCEEDED(rv)) {

View File

@ -161,11 +161,11 @@ public:
Http2Session *Session() { return mSession; }
static MOZ_MUST_USE nsresult MakeOriginURL(const nsACString &origin,
RefPtr<nsStandardURL> &url);
nsCOMPtr<nsIURI> &url);
static MOZ_MUST_USE nsresult MakeOriginURL(const nsACString &scheme,
const nsACString &origin,
RefPtr<nsStandardURL> &url);
nsCOMPtr<nsIURI> &url);
// Mirrors nsAHttpTransaction
bool Do0RTT();

View File

@ -136,16 +136,12 @@ NewURI(const nsACString &aSpec,
int32_t aDefaultPort,
nsIURI **aURI)
{
RefPtr<nsStandardURL> url = new nsStandardURL();
nsresult rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY,
aDefaultPort, aSpec, aCharset, aBaseURI);
if (NS_FAILED(rv)) {
return rv;
}
url.forget(aURI);
return NS_OK;
return NS_MutateURI(new nsStandardURL::Mutator())
.Apply<nsIStandardURLMutator>(&nsIStandardURLMutator::Init,
nsIStandardURL::URLTYPE_AUTHORITY,
aDefaultPort, nsCString(aSpec), aCharset, aBaseURI,
nullptr)
.Finalize(aURI);
}
#ifdef ANDROID

View File

@ -32,6 +32,8 @@ static NS_DEFINE_CID(kSubstitutingURLCID, NS_SUBSTITUTINGURL_CID);
// SubstitutingURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
//---------------------------------------------------------------------------------
NS_IMPL_ISUPPORTS(SubstitutingURL::Mutator, nsIURISetters, nsIURIMutator, nsIStandardURLMutator)
nsresult
SubstitutingURL::EnsureFile()
{
@ -196,12 +198,6 @@ SubstitutingProtocolHandler::NewURI(const nsACString &aSpec,
nsIURI *aBaseURI,
nsIURI **result)
{
nsresult rv;
RefPtr<SubstitutingURL> url = new SubstitutingURL();
if (!url)
return NS_ERROR_OUT_OF_MEMORY;
// unescape any %2f and %2e to make sure nsStandardURL coalesces them.
// Later net_GetFileFromURLSpec() will do a full unescape and we want to
// treat them the same way the file system will. (bugs 380994, 394075)
@ -233,11 +229,12 @@ SubstitutingProtocolHandler::NewURI(const nsACString &aSpec,
if (last < src)
spec.Append(last, src-last);
rv = url->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI);
if (NS_SUCCEEDED(rv)) {
url.forget(result);
}
return rv;
return NS_MutateURI(new SubstitutingURL::Mutator())
.Apply<nsIStandardURLMutator>(&nsIStandardURLMutator::Init,
nsIStandardURL::URLTYPE_STANDARD, -1,
spec, aCharset, aBaseURI,
nullptr)
.Finalize(result);
}
nsresult

View File

@ -126,6 +126,27 @@ public:
virtual nsStandardURL* StartClone() override;
virtual MOZ_MUST_USE nsresult EnsureFile() override;
NS_IMETHOD GetClassIDNoAlloc(nsCID *aCID) override;
class Mutator
: public TemplatedMutator<SubstitutingURL>
{
NS_DECL_ISUPPORTS
public:
explicit Mutator() = default;
private:
virtual ~Mutator() = default;
};
NS_IMETHOD Mutate(nsIURIMutator** aMutator) override
{
RefPtr<SubstitutingURL::Mutator> mutator = new SubstitutingURL::Mutator();
nsresult rv = mutator->InitFromURI(this);
if (NS_FAILED(rv)) {
return rv;
}
mutator.forget(aMutator);
return NS_OK;
}
};
} // namespace net

View File

@ -306,13 +306,12 @@ BaseWebSocketChannel::NewURI(const nsACString & aSpec, const char *aOriginCharse
if (NS_FAILED(rv))
return rv;
RefPtr<nsStandardURL> url = new nsStandardURL();
rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY, port, aSpec,
aOriginCharset, aBaseURI);
if (NS_FAILED(rv))
return rv;
url.forget(_retval);
return NS_OK;
return NS_MutateURI(new nsStandardURL::Mutator())
.Apply<nsIStandardURLMutator>(&nsIStandardURLMutator::Init,
nsIStandardURL::URLTYPE_AUTHORITY, port,
nsCString(aSpec), aOriginCharset, aBaseURI,
nullptr)
.Finalize(_retval);
}
NS_IMETHODIMP

View File

@ -1,8 +1,3 @@
const StandardURL = Components.Constructor("@mozilla.org/network/standard-url;1",
"nsIStandardURL",
"init");
// 1.percent-encoded IDN that contains blacklisted character should be converted
// to punycode, not UTF-8 string
// 2.only hostname-valid percent encoded ASCII characters should be decoded
@ -37,8 +32,10 @@ let prefData =
};
function stringToURL(str) {
return (new StandardURL(Ci.nsIStandardURL.URLTYPE_AUTHORITY, 80,
str, "UTF-8", null))
return Cc["@mozilla.org/network/standard-url-mutator;1"]
.createInstance(Ci.nsIStandardURLMutator)
.init(Ci.nsIStandardURL.URLTYPE_AUTHORITY, 80, str, "UTF-8", null)
.finalize()
.QueryInterface(Ci.nsIURL);
}

View File

@ -9,28 +9,40 @@
var Cc = Components.classes;
var Ci = Components.interfaces;
const StandardURL = Components.Constructor("@mozilla.org/network/standard-url;1",
"nsIStandardURL",
"init");
function run_test()
{
let mutator = Cc["@mozilla.org/network/standard-url-mutator;1"]
.createInstance(Ci.nsIURIMutator);
Assert.ok(mutator, "Mutator constructor works");
let url = Cc["@mozilla.org/network/standard-url-mutator;1"]
.createInstance(Ci.nsIStandardURLMutator)
.init(Ci.nsIStandardURL.URLTYPE_AUTHORITY, 65535,
"http://localhost", "UTF-8", null)
.finalize();
// Bug 1301621 makes invalid ports throw
Assert.throws(() => {
new StandardURL(Ci.nsIStandardURL.URLTYPE_AUTHORITY, 65536,
url = Cc["@mozilla.org/network/standard-url-mutator;1"]
.createInstance(Ci.nsIStandardURLMutator)
.init(Ci.nsIStandardURL.URLTYPE_AUTHORITY, 65536,
"http://localhost", "UTF-8", null)
.finalize();
}, "invalid port during creation");
let url = new StandardURL(Ci.nsIStandardURL.URLTYPE_AUTHORITY, 65535,
"http://localhost", "UTF-8", null)
.QueryInterface(Ci.nsIStandardURL)
Assert.throws(() => {
url.setDefaultPort(65536);
url = url.mutate()
.QueryInterface(Ci.nsIStandardURLMutator)
.setDefaultPort(65536)
.finalize();
}, "invalid port in setDefaultPort");
Assert.throws(() => {
url.port = 65536;
url = url.mutate()
.setPort(65536)
.finalize();
}, "invalid port in port setter");
Assert.equal(url.QueryInterface(Ci.nsIURI).port, -1);
Assert.equal(url.port, -1);
do_test_finished();
}

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