mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Merge autoland to mozilla-central. a=merge
This commit is contained in:
commit
f8bb2d707a
@ -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);
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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]]});
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
[DEFAULT]
|
||||
firefox-appdir = browser
|
||||
headless = true
|
||||
head = head.js
|
||||
support-files =
|
||||
../fixtures/**
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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%;
|
||||
}
|
||||
|
||||
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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 }),
|
||||
),
|
||||
|
@ -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',
|
||||
|
@ -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]
|
||||
|
@ -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`);
|
||||
}
|
||||
});
|
||||
|
@ -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`);
|
||||
}
|
||||
});
|
||||
|
49
devtools/client/netmonitor/test/browser_net_columns_time.js
Normal file
49
devtools/client/netmonitor/test/browser_net_columns_time.js
Normal 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);
|
||||
});
|
@ -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.
|
||||
*
|
||||
|
@ -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 () {
|
||||
|
@ -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 () {
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -175,4 +175,4 @@ Troubleshooting tips:
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
The version of WebRender currently in the tree is:
|
||||
e9269c7e06e20363be0b2a2a1be98d292ff7acca
|
||||
c0943271eb8c6440a61db37e2f1e84201dcac2e3
|
||||
|
@ -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" }
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -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 => {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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!");
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 ?
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(×tamp, 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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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. */
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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))){
|
||||
|
@ -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)) {
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user