зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland
This commit is contained in:
Коммит
cb5ce9d807
|
@ -49,6 +49,7 @@ RCINCLUDE = 'AccessibleHandler.rc'
|
||||||
# we add the prefix "Proxy" to all of the generated counterparts.
|
# we add the prefix "Proxy" to all of the generated counterparts.
|
||||||
DEFINES['ENTRY_PREFIX'] = 'Proxy'
|
DEFINES['ENTRY_PREFIX'] = 'Proxy'
|
||||||
DEFINES['REGISTER_PROXY_DLL'] = True
|
DEFINES['REGISTER_PROXY_DLL'] = True
|
||||||
|
LIBRARY_DEFINES['MOZ_MSCOM_REMARSHAL_NO_HANDLER'] = True
|
||||||
|
|
||||||
# We want to generate distinct UUIDs on a per-channel basis, so we need
|
# We want to generate distinct UUIDs on a per-channel basis, so we need
|
||||||
# finer granularity than the standard preprocessor definitions offer.
|
# finer granularity than the standard preprocessor definitions offer.
|
||||||
|
|
|
@ -540,7 +540,8 @@ BrowserGlue.prototype = {
|
||||||
this._flashHangCount = 0;
|
this._flashHangCount = 0;
|
||||||
this._firstWindowReady = new Promise(resolve => this._firstWindowLoaded = resolve);
|
this._firstWindowReady = new Promise(resolve => this._firstWindowLoaded = resolve);
|
||||||
|
|
||||||
if (AppConstants.platform == "macosx") {
|
if (AppConstants.platform == "macosx" ||
|
||||||
|
(AppConstants.platform == "win" && AppConstants.RELEASE_OR_BETA)) {
|
||||||
// Handles prompting to inform about incompatibilites when accessibility
|
// Handles prompting to inform about incompatibilites when accessibility
|
||||||
// and e10s are active together.
|
// and e10s are active together.
|
||||||
E10SAccessibilityCheck.init();
|
E10SAccessibilityCheck.init();
|
||||||
|
|
|
@ -12,13 +12,14 @@ const {
|
||||||
} = require("devtools/client/shared/vendor/react");
|
} = require("devtools/client/shared/vendor/react");
|
||||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||||
const Actions = require("../actions/index");
|
const Actions = require("../actions/index");
|
||||||
const { FILTER_SEARCH_DELAY, FILTER_FLAGS } = require("../constants");
|
const { FILTER_SEARCH_DELAY } = require("../constants");
|
||||||
const {
|
const {
|
||||||
getDisplayedRequestsSummary,
|
getDisplayedRequestsSummary,
|
||||||
getRequestFilterTypes,
|
getRequestFilterTypes,
|
||||||
isNetworkDetailsToggleButtonDisabled,
|
isNetworkDetailsToggleButtonDisabled,
|
||||||
} = require("../selectors/index");
|
} = require("../selectors/index");
|
||||||
|
|
||||||
|
const { autocompleteProvider } = require("../utils/filter-text-utils");
|
||||||
const { L10N } = require("../utils/l10n");
|
const { L10N } = require("../utils/l10n");
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
|
@ -92,11 +93,6 @@ const Toolbar = createClass({
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Setup autocomplete list
|
|
||||||
let negativeAutocompleteList = FILTER_FLAGS.map((item) => `-${item}`);
|
|
||||||
let autocompleteList = [...FILTER_FLAGS, ...negativeAutocompleteList]
|
|
||||||
.map((item) => `${item}:`);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
span({ className: "devtools-toolbar devtools-toolbar-container" },
|
span({ className: "devtools-toolbar devtools-toolbar-container" },
|
||||||
span({ className: "devtools-toolbar-group" },
|
span({ className: "devtools-toolbar-group" },
|
||||||
|
@ -114,7 +110,7 @@ const Toolbar = createClass({
|
||||||
placeholder: SEARCH_PLACE_HOLDER,
|
placeholder: SEARCH_PLACE_HOLDER,
|
||||||
type: "filter",
|
type: "filter",
|
||||||
onChange: setRequestFilterText,
|
onChange: setRequestFilterText,
|
||||||
autocompleteList,
|
autocompleteProvider,
|
||||||
}),
|
}),
|
||||||
button({
|
button({
|
||||||
className: toggleButtonClassName.join(" "),
|
className: toggleButtonClassName.join(" "),
|
||||||
|
|
|
@ -242,6 +242,50 @@ function isFreetextMatch(item, text) {
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an autocomplete list for the search-box for network monitor
|
||||||
|
*
|
||||||
|
* It expects an entire string of the searchbox ie "is:cached pr".
|
||||||
|
* The string is then tokenized into "is:cached" and "pr"
|
||||||
|
*
|
||||||
|
* @param {string} filter - The entire search string of the search box
|
||||||
|
* @return {Array} - The output is an array of objects as below
|
||||||
|
* [{value: "is:cached protocol", displayValue: "protocol"}[, ...]]
|
||||||
|
* `value` is used to update the search-box input box for given item
|
||||||
|
* `displayValue` is used to render the autocomplete list
|
||||||
|
*/
|
||||||
|
function autocompleteProvider(filter) {
|
||||||
|
if (!filter) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let negativeAutocompleteList = FILTER_FLAGS.map((item) => `-${item}`);
|
||||||
|
let baseList = [...FILTER_FLAGS, ...negativeAutocompleteList]
|
||||||
|
.map((item) => `${item}:`);
|
||||||
|
|
||||||
|
// The last token is used to filter the base autocomplete list
|
||||||
|
let tokens = filter.split(/\s+/g);
|
||||||
|
let lastToken = tokens[tokens.length - 1];
|
||||||
|
let previousTokens = tokens.slice(0, tokens.length - 1);
|
||||||
|
|
||||||
|
// Autocomplete list is not generated for empty lastToken
|
||||||
|
if (!lastToken) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseList
|
||||||
|
.filter((item) => {
|
||||||
|
return item.toLowerCase().startsWith(lastToken.toLowerCase())
|
||||||
|
&& item.toLowerCase() !== lastToken.toLowerCase();
|
||||||
|
})
|
||||||
|
.sort()
|
||||||
|
.map(item => ({
|
||||||
|
value: [...previousTokens, item].join(" "),
|
||||||
|
displayValue: item,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
isFreetextMatch,
|
isFreetextMatch,
|
||||||
|
autocompleteProvider,
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,15 @@ module.exports = createClass({
|
||||||
displayName: "AutocompletePopup",
|
displayName: "AutocompletePopup",
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
list: PropTypes.array.isRequired,
|
/**
|
||||||
|
* autocompleteProvider takes search-box's entire input text as `filter` argument
|
||||||
|
* ie. "is:cached pr"
|
||||||
|
* returned value is array of objects like below
|
||||||
|
* [{value: "is:cached protocol", displayValue: "protocol"}[, ...]]
|
||||||
|
* `value` is used to update the search-box input box for given item
|
||||||
|
* `displayValue` is used to render the autocomplete list
|
||||||
|
*/
|
||||||
|
autocompleteProvider: PropTypes.func.isRequired,
|
||||||
filter: PropTypes.string.isRequired,
|
filter: PropTypes.string.isRequired,
|
||||||
onItemSelected: PropTypes.func.isRequired,
|
onItemSelected: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
@ -32,14 +40,11 @@ module.exports = createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computeState({ filter, list }) {
|
computeState({ autocompleteProvider, filter }) {
|
||||||
let filteredList = list.filter((item) => {
|
let list = autocompleteProvider(filter);
|
||||||
return item.toLowerCase().startsWith(filter.toLowerCase())
|
let selectedIndex = list.length == 1 ? 0 : -1;
|
||||||
&& item.toLowerCase() !== filter.toLowerCase();
|
|
||||||
}).sort();
|
|
||||||
let selectedIndex = filteredList.length == 1 ? 0 : -1;
|
|
||||||
|
|
||||||
return { filteredList, selectedIndex };
|
return { list, selectedIndex };
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,8 +60,7 @@ module.exports = createClass({
|
||||||
* This method is public.
|
* This method is public.
|
||||||
*/
|
*/
|
||||||
jumpToBottom() {
|
jumpToBottom() {
|
||||||
let selectedIndex = this.state.filteredList.length - 1;
|
this.setState({ selectedIndex: this.state.list.length - 1 });
|
||||||
this.setState({ selectedIndex });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,14 +71,14 @@ module.exports = createClass({
|
||||||
* @param {number} increment - No. of hops in the direction
|
* @param {number} increment - No. of hops in the direction
|
||||||
*/
|
*/
|
||||||
jumpBy(increment = 1) {
|
jumpBy(increment = 1) {
|
||||||
let { filteredList, selectedIndex } = this.state;
|
let { list, selectedIndex } = this.state;
|
||||||
let nextIndex = selectedIndex + increment;
|
let nextIndex = selectedIndex + increment;
|
||||||
if (increment > 0) {
|
if (increment > 0) {
|
||||||
// Positive cycling
|
// Positive cycling
|
||||||
nextIndex = nextIndex > filteredList.length - 1 ? 0 : nextIndex;
|
nextIndex = nextIndex > list.length - 1 ? 0 : nextIndex;
|
||||||
} else if (increment < 0) {
|
} else if (increment < 0) {
|
||||||
// Inverse cycling
|
// Inverse cycling
|
||||||
nextIndex = nextIndex < 0 ? filteredList.length - 1 : nextIndex;
|
nextIndex = nextIndex < 0 ? list.length - 1 : nextIndex;
|
||||||
}
|
}
|
||||||
this.setState({selectedIndex: nextIndex});
|
this.setState({selectedIndex: nextIndex});
|
||||||
},
|
},
|
||||||
|
@ -85,7 +89,7 @@ module.exports = createClass({
|
||||||
*/
|
*/
|
||||||
select() {
|
select() {
|
||||||
if (this.refs.selected) {
|
if (this.refs.selected) {
|
||||||
this.props.onItemSelected(this.refs.selected.textContent);
|
this.props.onItemSelected(this.refs.selected.dataset.value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -95,13 +99,13 @@ module.exports = createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { filteredList } = this.state;
|
let { list } = this.state;
|
||||||
|
|
||||||
return filteredList.length > 0 && dom.div(
|
return list.length > 0 && dom.div(
|
||||||
{ className: "devtools-autocomplete-popup devtools-monospace" },
|
{ className: "devtools-autocomplete-popup devtools-monospace" },
|
||||||
dom.ul(
|
dom.ul(
|
||||||
{ className: "devtools-autocomplete-listbox" },
|
{ className: "devtools-autocomplete-listbox" },
|
||||||
filteredList.map((item, i) => {
|
list.map((item, i) => {
|
||||||
let isSelected = this.state.selectedIndex == i;
|
let isSelected = this.state.selectedIndex == i;
|
||||||
let itemClassList = ["autocomplete-item"];
|
let itemClassList = ["autocomplete-item"];
|
||||||
|
|
||||||
|
@ -111,10 +115,11 @@ module.exports = createClass({
|
||||||
return dom.li({
|
return dom.li({
|
||||||
key: i,
|
key: i,
|
||||||
"data-index": i,
|
"data-index": i,
|
||||||
|
"data-value": item.value,
|
||||||
className: itemClassList.join(" "),
|
className: itemClassList.join(" "),
|
||||||
ref: isSelected ? "selected" : null,
|
ref: isSelected ? "selected" : null,
|
||||||
onMouseDown: this.onMouseDown,
|
onMouseDown: this.onMouseDown,
|
||||||
}, item);
|
}, item.displayValue);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,13 +22,7 @@ module.exports = createClass({
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
placeholder: PropTypes.string,
|
placeholder: PropTypes.string,
|
||||||
type: PropTypes.string,
|
type: PropTypes.string,
|
||||||
autocompleteList: PropTypes.array,
|
autocompleteProvider: PropTypes.func,
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps() {
|
|
||||||
return {
|
|
||||||
autocompleteList: [],
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
|
@ -66,6 +60,7 @@ module.exports = createClass({
|
||||||
onChange() {
|
onChange() {
|
||||||
if (this.state.value !== this.refs.input.value) {
|
if (this.state.value !== this.refs.input.value) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
focused: true,
|
||||||
value: this.refs.input.value,
|
value: this.refs.input.value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -102,10 +97,8 @@ module.exports = createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onKeyDown(e) {
|
onKeyDown(e) {
|
||||||
let { autocompleteList } = this.props;
|
|
||||||
let { autocomplete } = this.refs;
|
let { autocomplete } = this.refs;
|
||||||
|
if (!autocomplete || autocomplete.state.list.length <= 0) {
|
||||||
if (autocompleteList.length == 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,13 +137,12 @@ module.exports = createClass({
|
||||||
let {
|
let {
|
||||||
type = "search",
|
type = "search",
|
||||||
placeholder,
|
placeholder,
|
||||||
autocompleteList
|
autocompleteProvider,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
let { value } = this.state;
|
let { value } = this.state;
|
||||||
let divClassList = ["devtools-searchbox", "has-clear-btn"];
|
let divClassList = ["devtools-searchbox", "has-clear-btn"];
|
||||||
let inputClassList = [`devtools-${type}input`];
|
let inputClassList = [`devtools-${type}input`];
|
||||||
let showAutocomplete =
|
let showAutocomplete = autocompleteProvider && this.state.focused && value !== "";
|
||||||
autocompleteList.length > 0 && this.state.focused && value !== "";
|
|
||||||
|
|
||||||
if (value !== "") {
|
if (value !== "") {
|
||||||
inputClassList.push("filled");
|
inputClassList.push("filled");
|
||||||
|
@ -173,7 +165,7 @@ module.exports = createClass({
|
||||||
onClick: this.onClearButtonClick
|
onClick: this.onClearButtonClick
|
||||||
}),
|
}),
|
||||||
showAutocomplete && AutocompletePopup({
|
showAutocomplete && AutocompletePopup({
|
||||||
list: autocompleteList,
|
autocompleteProvider,
|
||||||
filter: value,
|
filter: value,
|
||||||
ref: "autocomplete",
|
ref: "autocomplete",
|
||||||
onItemSelected: (itemValue) => {
|
onItemSelected: (itemValue) => {
|
||||||
|
|
|
@ -43,7 +43,8 @@ window.onload = async function () {
|
||||||
);
|
);
|
||||||
const { component, $ } = await createComponentTest(SearchBox, {
|
const { component, $ } = await createComponentTest(SearchBox, {
|
||||||
type: "search",
|
type: "search",
|
||||||
autocompleteList: [
|
autocompleteProvider: (filter) => {
|
||||||
|
let baseList = [
|
||||||
"foo",
|
"foo",
|
||||||
"BAR",
|
"BAR",
|
||||||
"baZ",
|
"baZ",
|
||||||
|
@ -56,7 +57,30 @@ window.onload = async function () {
|
||||||
"a3",
|
"a3",
|
||||||
"a4",
|
"a4",
|
||||||
"a5",
|
"a5",
|
||||||
],
|
];
|
||||||
|
if (!filter) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens = filter.split(/\s+/g);
|
||||||
|
let lastToken = tokens[tokens.length - 1];
|
||||||
|
let previousTokens = tokens.slice(0, tokens.length - 1);
|
||||||
|
|
||||||
|
if (!lastToken) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseList
|
||||||
|
.filter((item) => {
|
||||||
|
return item.toLowerCase().startsWith(lastToken.toLowerCase())
|
||||||
|
&& item.toLowerCase() !== lastToken.toLowerCase();
|
||||||
|
})
|
||||||
|
.sort()
|
||||||
|
.map(item => ({
|
||||||
|
value: [...previousTokens, item].join(" "),
|
||||||
|
displayValue: item,
|
||||||
|
}));
|
||||||
|
},
|
||||||
onChange: () => null,
|
onChange: () => null,
|
||||||
});
|
});
|
||||||
const { refs } = component;
|
const { refs } = component;
|
||||||
|
@ -150,6 +174,8 @@ window.onload = async function () {
|
||||||
ok(!$(".devtools-autocomplete-popup"), "Enter/Return hides the popup");
|
ok(!$(".devtools-autocomplete-popup"), "Enter/Return hides the popup");
|
||||||
|
|
||||||
// Escape should remove the autocomplete component
|
// Escape should remove the autocomplete component
|
||||||
|
synthesizeKey("VK_BACK_SPACE", {});
|
||||||
|
await forceRender(component);
|
||||||
synthesizeKey("VK_ESCAPE", {});
|
synthesizeKey("VK_ESCAPE", {});
|
||||||
await forceRender(component);
|
await forceRender(component);
|
||||||
ok(!$(".devtools-autocomplete-popup"),
|
ok(!$(".devtools-autocomplete-popup"),
|
||||||
|
@ -182,10 +208,25 @@ window.onload = async function () {
|
||||||
ok(!$(".devtools-autocomplete-popup"), "Mouse click on item hides the popup");
|
ok(!$(".devtools-autocomplete-popup"), "Mouse click on item hides the popup");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function testTokenizedAutocomplete() {
|
||||||
|
// Test for string "pqr ab" which should show list of ABC, abc
|
||||||
|
sendString(" ab");
|
||||||
|
await forceRender(component);
|
||||||
|
compareAutocompleteList($(".devtools-autocomplete-listbox"), ["ABC", "abc"]);
|
||||||
|
|
||||||
|
// Select the first element, value now should be "pqr ABC"
|
||||||
|
synthesizeMouseAtCenter(
|
||||||
|
$(".devtools-autocomplete-listbox .autocomplete-item:nth-child(1)"),
|
||||||
|
{}, window
|
||||||
|
);
|
||||||
|
is(component.state.value, "pqr ABC", "Post Tokenization value selection");
|
||||||
|
}
|
||||||
|
|
||||||
add_task(async function () {
|
add_task(async function () {
|
||||||
await testSearchBoxWithAutocomplete();
|
await testSearchBoxWithAutocomplete();
|
||||||
await testKeyEventsWithAutocomplete();
|
await testKeyEventsWithAutocomplete();
|
||||||
await testMouseEventsWithAutocomplete();
|
await testMouseEventsWithAutocomplete();
|
||||||
|
await testTokenizedAutocomplete();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -81,6 +81,7 @@ const ErrorDocs = {
|
||||||
JSMSG_BAD_URI: "Malformed_URI",
|
JSMSG_BAD_URI: "Malformed_URI",
|
||||||
JSMSG_DEPRECATED_DELETE_OPERAND: "Delete_in_strict_mode",
|
JSMSG_DEPRECATED_DELETE_OPERAND: "Delete_in_strict_mode",
|
||||||
JSMSG_MISSING_FORMAL: "Missing_formal_parameter",
|
JSMSG_MISSING_FORMAL: "Missing_formal_parameter",
|
||||||
|
JSMSG_CANT_TRUNCATE_ARRAY: "Non_configurable_array_element",
|
||||||
};
|
};
|
||||||
|
|
||||||
const MIXED_CONTENT_LEARN_MORE = "https://developer.mozilla.org/docs/Web/Security/Mixed_content";
|
const MIXED_CONTENT_LEARN_MORE = "https://developer.mozilla.org/docs/Web/Security/Mixed_content";
|
||||||
|
|
|
@ -414,8 +414,10 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time
|
||||||
if (intersectionRect.isSome() && !isSameDoc) {
|
if (intersectionRect.isSome() && !isSameDoc) {
|
||||||
nsRect rect = intersectionRect.value();
|
nsRect rect = intersectionRect.value();
|
||||||
nsPresContext* presContext = targetFrame->PresContext();
|
nsPresContext* presContext = targetFrame->PresContext();
|
||||||
nsLayoutUtils::TransformRect(rootFrame,
|
nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame();
|
||||||
presContext->PresShell()->GetRootScrollFrame(), rect);
|
if (rootScrollFrame) {
|
||||||
|
nsLayoutUtils::TransformRect(rootFrame, rootScrollFrame, rect);
|
||||||
|
}
|
||||||
intersectionRect = Some(rect);
|
intersectionRect = Some(rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||||
|
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
|
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
||||||
|
<xhtml:div id="target"></xhtml:div>
|
||||||
|
</window>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<iframe id="iframe" src="1370968-inner.xul"></iframe>
|
||||||
|
<script>
|
||||||
|
var io = new IntersectionObserver(function () {
|
||||||
|
}, { });
|
||||||
|
var iframe = document.getElementById('iframe');
|
||||||
|
iframe.onload = function () {
|
||||||
|
io.observe(iframe.contentDocument.getElementById('target'));
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -201,10 +201,6 @@ load 1154598.xhtml
|
||||||
load 1157995.html
|
load 1157995.html
|
||||||
load 1158412.html
|
load 1158412.html
|
||||||
load 1181619.html
|
load 1181619.html
|
||||||
load structured_clone_container_throws.html
|
|
||||||
HTTP(..) load xhr_abortinprogress.html
|
|
||||||
load xhr_empty_datauri.html
|
|
||||||
load xhr_html_nullresponse.html
|
|
||||||
load 1230422.html
|
load 1230422.html
|
||||||
load 1251361.html
|
load 1251361.html
|
||||||
load 1304437.html
|
load 1304437.html
|
||||||
|
@ -212,7 +208,12 @@ pref(dom.IntersectionObserver.enabled,true) load 1324209.html
|
||||||
pref(dom.IntersectionObserver.enabled,true) load 1326194-1.html
|
pref(dom.IntersectionObserver.enabled,true) load 1326194-1.html
|
||||||
pref(dom.IntersectionObserver.enabled,true) load 1326194-2.html
|
pref(dom.IntersectionObserver.enabled,true) load 1326194-2.html
|
||||||
pref(dom.IntersectionObserver.enabled,true) load 1332939.html
|
pref(dom.IntersectionObserver.enabled,true) load 1332939.html
|
||||||
pref(dom.IntersectionObserver.enabled,true) load 1353529.xul
|
|
||||||
pref(dom.webcomponents.enabled,true) load 1341693.html
|
pref(dom.webcomponents.enabled,true) load 1341693.html
|
||||||
|
pref(dom.IntersectionObserver.enabled,true) load 1353529.xul
|
||||||
pref(dom.IntersectionObserver.enabled,true) load 1369363.xul
|
pref(dom.IntersectionObserver.enabled,true) load 1369363.xul
|
||||||
load 1370072.html
|
load 1370072.html
|
||||||
|
pref(dom.IntersectionObserver.enabled,true) load 1370968.html
|
||||||
|
load structured_clone_container_throws.html
|
||||||
|
HTTP(..) load xhr_abortinprogress.html
|
||||||
|
load xhr_empty_datauri.html
|
||||||
|
load xhr_html_nullresponse.html
|
||||||
|
|
|
@ -56,6 +56,8 @@
|
||||||
#include "nsGlobalWindow.h"
|
#include "nsGlobalWindow.h"
|
||||||
#include "nsScriptNameSpaceManager.h"
|
#include "nsScriptNameSpaceManager.h"
|
||||||
#include "mozilla/AutoRestore.h"
|
#include "mozilla/AutoRestore.h"
|
||||||
|
#include "mozilla/MainThreadIdlePeriod.h"
|
||||||
|
#include "mozilla/StaticPtr.h"
|
||||||
#include "mozilla/dom/DOMException.h"
|
#include "mozilla/dom/DOMException.h"
|
||||||
#include "mozilla/dom/DOMExceptionBinding.h"
|
#include "mozilla/dom/DOMExceptionBinding.h"
|
||||||
#include "mozilla/dom/ErrorEvent.h"
|
#include "mozilla/dom/ErrorEvent.h"
|
||||||
|
@ -63,7 +65,7 @@
|
||||||
#include "nsAXPCNativeCallContext.h"
|
#include "nsAXPCNativeCallContext.h"
|
||||||
#include "mozilla/CycleCollectedJSRuntime.h"
|
#include "mozilla/CycleCollectedJSRuntime.h"
|
||||||
#include "mozilla/SystemGroup.h"
|
#include "mozilla/SystemGroup.h"
|
||||||
|
#include "nsRefreshDriver.h"
|
||||||
#include "nsJSPrincipals.h"
|
#include "nsJSPrincipals.h"
|
||||||
|
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
|
@ -111,21 +113,24 @@ const size_t gStackSize = 8192;
|
||||||
// Maximum amount of time that should elapse between incremental GC slices
|
// Maximum amount of time that should elapse between incremental GC slices
|
||||||
#define NS_INTERSLICE_GC_DELAY 100 // ms
|
#define NS_INTERSLICE_GC_DELAY 100 // ms
|
||||||
|
|
||||||
// If we haven't painted in 100ms, or we're in e10s parent process and
|
|
||||||
// user isn't active, we allow for a longer GC budget.
|
|
||||||
#define NS_INTERSLICE_GC_BUDGET 40 // ms
|
|
||||||
|
|
||||||
// The amount of time we wait between a request to CC (after GC ran)
|
// The amount of time we wait between a request to CC (after GC ran)
|
||||||
// and doing the actual CC.
|
// and doing the actual CC.
|
||||||
#define NS_CC_DELAY 6000 // ms
|
#define NS_CC_DELAY 6000 // ms
|
||||||
|
|
||||||
#define NS_CC_SKIPPABLE_DELAY 250 // ms
|
#define NS_CC_SKIPPABLE_DELAY 250 // ms
|
||||||
|
|
||||||
|
// ForgetSkippable is usually fast, so we can use small budgets.
|
||||||
|
// This isn't a real budget but a hint to CollectorRunner whether there
|
||||||
|
// is enough time to call ForgetSkippable.
|
||||||
|
static const int64_t kForgetSkippableSliceDuration = 2;
|
||||||
|
|
||||||
// Maximum amount of time that should elapse between incremental CC slices
|
// Maximum amount of time that should elapse between incremental CC slices
|
||||||
static const int64_t kICCIntersliceDelay = 32; // ms
|
static const int64_t kICCIntersliceDelay = 32; // ms
|
||||||
|
|
||||||
// Time budget for an incremental CC slice
|
// Time budget for an incremental CC slice when using timer to run it.
|
||||||
static const int64_t kICCSliceBudget = 5; // ms
|
static const int64_t kICCSliceBudget = 5; // ms
|
||||||
|
// Minimum budget for an incremental CC slice when using idle time to run it.
|
||||||
|
static const int64_t kIdleICCSliceBudget = 3; // ms
|
||||||
|
|
||||||
// Maximum total duration for an ICC
|
// Maximum total duration for an ICC
|
||||||
static const uint32_t kMaxICCDuration = 2000; // ms
|
static const uint32_t kMaxICCDuration = 2000; // ms
|
||||||
|
@ -144,14 +149,16 @@ static const uint32_t kMaxICCDuration = 2000; // ms
|
||||||
// Large value used to specify that a script should run essentially forever
|
// Large value used to specify that a script should run essentially forever
|
||||||
#define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
|
#define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
|
||||||
|
|
||||||
|
class CollectorRunner;
|
||||||
|
|
||||||
// if you add statics here, add them to the list in StartupJSEnvironment
|
// if you add statics here, add them to the list in StartupJSEnvironment
|
||||||
|
|
||||||
static nsITimer *sGCTimer;
|
static nsITimer *sGCTimer;
|
||||||
static nsITimer *sShrinkingGCTimer;
|
static nsITimer *sShrinkingGCTimer;
|
||||||
static nsITimer *sCCTimer;
|
static StaticRefPtr<CollectorRunner> sCCRunner;
|
||||||
static nsITimer *sICCTimer;
|
static StaticRefPtr<CollectorRunner> sICCRunner;
|
||||||
static nsITimer *sFullGCTimer;
|
static nsITimer *sFullGCTimer;
|
||||||
static nsITimer *sInterSliceGCTimer;
|
static StaticRefPtr<CollectorRunner> sInterSliceGCRunner;
|
||||||
|
|
||||||
static TimeStamp sLastCCEndTime;
|
static TimeStamp sLastCCEndTime;
|
||||||
|
|
||||||
|
@ -176,7 +183,7 @@ static uint32_t sCCollectedZonesWaitingForGC;
|
||||||
static uint32_t sLikelyShortLivingObjectsNeedingGC;
|
static uint32_t sLikelyShortLivingObjectsNeedingGC;
|
||||||
static bool sPostGCEventsToConsole;
|
static bool sPostGCEventsToConsole;
|
||||||
static bool sPostGCEventsToObserver;
|
static bool sPostGCEventsToObserver;
|
||||||
static int32_t sCCTimerFireCount = 0;
|
static int32_t sCCRunnerFireCount = 0;
|
||||||
static uint32_t sMinForgetSkippableTime = UINT32_MAX;
|
static uint32_t sMinForgetSkippableTime = UINT32_MAX;
|
||||||
static uint32_t sMaxForgetSkippableTime = 0;
|
static uint32_t sMaxForgetSkippableTime = 0;
|
||||||
static uint32_t sTotalForgetSkippableTime = 0;
|
static uint32_t sTotalForgetSkippableTime = 0;
|
||||||
|
@ -188,7 +195,6 @@ static bool sNeedsFullCC = false;
|
||||||
static bool sNeedsFullGC = false;
|
static bool sNeedsFullGC = false;
|
||||||
static bool sNeedsGCAfterCC = false;
|
static bool sNeedsGCAfterCC = false;
|
||||||
static bool sIncrementalCC = false;
|
static bool sIncrementalCC = false;
|
||||||
static bool sDidPaintAfterPreviousICCSlice = false;
|
|
||||||
static int32_t sActiveIntersliceGCBudget = 0; // ms;
|
static int32_t sActiveIntersliceGCBudget = 0; // ms;
|
||||||
static nsScriptNameSpaceManager *gNameSpaceManager;
|
static nsScriptNameSpaceManager *gNameSpaceManager;
|
||||||
|
|
||||||
|
@ -223,6 +229,178 @@ static bool sIsCompactingOnUserInactive = false;
|
||||||
static int32_t sExpensiveCollectorPokes = 0;
|
static int32_t sExpensiveCollectorPokes = 0;
|
||||||
static const int32_t kPokesBetweenExpensiveCollectorTriggers = 5;
|
static const int32_t kPokesBetweenExpensiveCollectorTriggers = 5;
|
||||||
|
|
||||||
|
// Return true if some meaningful work was done.
|
||||||
|
typedef bool (*CollectorRunnerCallback) (TimeStamp aDeadline, void* aData);
|
||||||
|
|
||||||
|
// Repeating callback runner for CC and GC.
|
||||||
|
class CollectorRunner final : public IdleRunnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static already_AddRefed<CollectorRunner>
|
||||||
|
Create(CollectorRunnerCallback aCallback, uint32_t aDelay,
|
||||||
|
int64_t aBudget, bool aRepeating, void* aData = nullptr)
|
||||||
|
{
|
||||||
|
RefPtr<CollectorRunner> runner =
|
||||||
|
new CollectorRunner(aCallback, aDelay, aBudget, aRepeating, aData);
|
||||||
|
runner->Schedule(false); // Initial scheduling shouldn't use idle dispatch.
|
||||||
|
return runner.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHOD Run() override
|
||||||
|
{
|
||||||
|
if (!mCallback) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deadline is null when called from timer.
|
||||||
|
bool deadLineWasNull = mDeadline.IsNull();
|
||||||
|
bool didRun = false;
|
||||||
|
if (deadLineWasNull || ((TimeStamp::Now() + mBudget) < mDeadline)) {
|
||||||
|
CancelTimer();
|
||||||
|
didRun = mCallback(mDeadline, mData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCallback && (mRepeating || !didRun)) {
|
||||||
|
// If we didn't do meaningful work, don't schedule using immediate
|
||||||
|
// idle dispatch, since that could lead to a loop until the idle
|
||||||
|
// period ends.
|
||||||
|
Schedule(didRun);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
TimedOut(nsITimer* aTimer, void* aClosure)
|
||||||
|
{
|
||||||
|
RefPtr<CollectorRunner> runnable = static_cast<CollectorRunner*>(aClosure);
|
||||||
|
runnable->Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetDeadline(mozilla::TimeStamp aDeadline) override
|
||||||
|
{
|
||||||
|
mDeadline = aDeadline;
|
||||||
|
};
|
||||||
|
|
||||||
|
void SetTimer(uint32_t aDelay, nsIEventTarget* aTarget) override
|
||||||
|
{
|
||||||
|
if (mTimerActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mTarget = aTarget;
|
||||||
|
if (!mTimer) {
|
||||||
|
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||||
|
} else {
|
||||||
|
mTimer->Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mTimer) {
|
||||||
|
mTimer->SetTarget(mTarget);
|
||||||
|
mTimer->InitWithFuncCallback(TimedOut, this, aDelay,
|
||||||
|
nsITimer::TYPE_ONE_SHOT);
|
||||||
|
mTimerActive = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult Cancel() override
|
||||||
|
{
|
||||||
|
CancelTimer();
|
||||||
|
mTimer = nullptr;
|
||||||
|
mScheduleTimer = nullptr;
|
||||||
|
mCallback = nullptr;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ScheduleTimedOut(nsITimer* aTimer, void* aClosure)
|
||||||
|
{
|
||||||
|
RefPtr<CollectorRunner> runnable = static_cast<CollectorRunner*>(aClosure);
|
||||||
|
runnable->Schedule(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Schedule(bool aAllowIdleDispatch)
|
||||||
|
{
|
||||||
|
if (!mCallback) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sShuttingDown) {
|
||||||
|
Cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDeadline = TimeStamp();
|
||||||
|
TimeStamp now = TimeStamp::Now();
|
||||||
|
TimeStamp hint = nsRefreshDriver::GetIdleDeadlineHint(now);
|
||||||
|
if (hint != now) {
|
||||||
|
// RefreshDriver is ticking, let it schedule the idle dispatch.
|
||||||
|
nsRefreshDriver::DispatchIdleRunnableAfterTick(this, mDelay);
|
||||||
|
// Ensure we get called at some point, even if RefreshDriver is stopped.
|
||||||
|
SetTimer(mDelay, mTarget);
|
||||||
|
} else {
|
||||||
|
// RefreshDriver doesn't seem to be running.
|
||||||
|
if (aAllowIdleDispatch) {
|
||||||
|
nsCOMPtr<nsIRunnable> runnable = this;
|
||||||
|
NS_IdleDispatchToCurrentThread(runnable.forget(), mDelay);
|
||||||
|
SetTimer(mDelay, mTarget);
|
||||||
|
} else {
|
||||||
|
if (!mScheduleTimer) {
|
||||||
|
mScheduleTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||||
|
if (!mScheduleTimer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mScheduleTimer->Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We weren't allowed to do idle dispatch immediately, do it after a
|
||||||
|
// short timeout.
|
||||||
|
mScheduleTimer->InitWithFuncCallback(ScheduleTimedOut, this, 16,
|
||||||
|
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit CollectorRunner(CollectorRunnerCallback aCallback,
|
||||||
|
uint32_t aDelay, int64_t aBudget,
|
||||||
|
bool aRepeating, void* aData)
|
||||||
|
: mCallback(aCallback), mDelay(aDelay)
|
||||||
|
, mBudget(TimeDuration::FromMilliseconds(aBudget))
|
||||||
|
, mRepeating(aRepeating), mTimerActive(false), mData(aData)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~CollectorRunner()
|
||||||
|
{
|
||||||
|
CancelTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CancelTimer()
|
||||||
|
{
|
||||||
|
nsRefreshDriver::CancelIdleRunnable(this);
|
||||||
|
if (mTimer) {
|
||||||
|
mTimer->Cancel();
|
||||||
|
}
|
||||||
|
if (mScheduleTimer) {
|
||||||
|
mScheduleTimer->Cancel();
|
||||||
|
}
|
||||||
|
mTimerActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsITimer> mTimer;
|
||||||
|
nsCOMPtr<nsITimer> mScheduleTimer;
|
||||||
|
nsCOMPtr<nsIEventTarget> mTarget;
|
||||||
|
CollectorRunnerCallback mCallback;
|
||||||
|
uint32_t mDelay;
|
||||||
|
TimeStamp mDeadline;
|
||||||
|
TimeDuration mBudget;
|
||||||
|
bool mRepeating;
|
||||||
|
bool mTimerActive;
|
||||||
|
void* mData;
|
||||||
|
};
|
||||||
|
|
||||||
static const char*
|
static const char*
|
||||||
ProcessNameForCollectorLog()
|
ProcessNameForCollectorLog()
|
||||||
{
|
{
|
||||||
|
@ -299,10 +477,10 @@ KillTimers()
|
||||||
{
|
{
|
||||||
nsJSContext::KillGCTimer();
|
nsJSContext::KillGCTimer();
|
||||||
nsJSContext::KillShrinkingGCTimer();
|
nsJSContext::KillShrinkingGCTimer();
|
||||||
nsJSContext::KillCCTimer();
|
nsJSContext::KillCCRunner();
|
||||||
nsJSContext::KillICCTimer();
|
nsJSContext::KillICCRunner();
|
||||||
nsJSContext::KillFullGCTimer();
|
nsJSContext::KillFullGCTimer();
|
||||||
nsJSContext::KillInterSliceGCTimer();
|
nsJSContext::KillInterSliceGCRunner();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we collected a substantial amount of cycles, poke the GC since more objects
|
// If we collected a substantial amount of cycles, poke the GC since more objects
|
||||||
|
@ -1451,7 +1629,7 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
|
||||||
|
|
||||||
//static
|
//static
|
||||||
void
|
void
|
||||||
nsJSContext::RunCycleCollectorSlice()
|
nsJSContext::RunCycleCollectorSlice(TimeStamp aDeadline)
|
||||||
{
|
{
|
||||||
if (!NS_IsMainThread()) {
|
if (!NS_IsMainThread()) {
|
||||||
return;
|
return;
|
||||||
|
@ -1467,33 +1645,40 @@ nsJSContext::RunCycleCollectorSlice()
|
||||||
js::SliceBudget budget = js::SliceBudget::unlimited();
|
js::SliceBudget budget = js::SliceBudget::unlimited();
|
||||||
|
|
||||||
if (sIncrementalCC) {
|
if (sIncrementalCC) {
|
||||||
|
int64_t baseBudget = kICCSliceBudget;
|
||||||
|
if (!aDeadline.IsNull()) {
|
||||||
|
baseBudget = int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds());
|
||||||
|
}
|
||||||
|
|
||||||
if (gCCStats.mBeginTime.IsNull()) {
|
if (gCCStats.mBeginTime.IsNull()) {
|
||||||
// If no CC is in progress, use the standard slice time.
|
// If no CC is in progress, use the standard slice time.
|
||||||
budget = js::SliceBudget(js::TimeBudget(kICCSliceBudget));
|
budget = js::SliceBudget(js::TimeBudget(baseBudget));
|
||||||
} else {
|
} else {
|
||||||
TimeStamp now = TimeStamp::Now();
|
TimeStamp now = TimeStamp::Now();
|
||||||
|
|
||||||
// Only run a limited slice if we're within the max running time.
|
// Only run a limited slice if we're within the max running time.
|
||||||
uint32_t runningTime = TimeBetween(gCCStats.mBeginTime, now);
|
uint32_t runningTime = TimeBetween(gCCStats.mBeginTime, now);
|
||||||
if (runningTime < kMaxICCDuration) {
|
if (runningTime < kMaxICCDuration) {
|
||||||
// Try to make up for a delay in running this slice.
|
const float maxSlice = MainThreadIdlePeriod::GetLongIdlePeriod();
|
||||||
float sliceDelayMultiplier = TimeBetween(gCCStats.mEndSliceTime, now) / (float)kICCIntersliceDelay;
|
|
||||||
float delaySliceBudget = kICCSliceBudget * sliceDelayMultiplier;
|
|
||||||
|
|
||||||
// Increase slice budgets up to |maxLaterSlice| as we approach
|
// Try to make up for a delay in running this slice.
|
||||||
|
float sliceDelayMultiplier =
|
||||||
|
TimeBetween(gCCStats.mEndSliceTime, now) / (float)kICCIntersliceDelay;
|
||||||
|
float delaySliceBudget =
|
||||||
|
std::min(baseBudget * sliceDelayMultiplier, maxSlice);
|
||||||
|
|
||||||
|
// Increase slice budgets up to |maxSlice| as we approach
|
||||||
// half way through the ICC, to avoid large sync CCs.
|
// half way through the ICC, to avoid large sync CCs.
|
||||||
float percentToHalfDone = std::min(2.0f * runningTime / kMaxICCDuration, 1.0f);
|
float percentToHalfDone = std::min(2.0f * runningTime / kMaxICCDuration, 1.0f);
|
||||||
const float maxLaterSlice = 40.0f;
|
float laterSliceBudget = maxSlice * percentToHalfDone;
|
||||||
float laterSliceBudget = maxLaterSlice * percentToHalfDone;
|
|
||||||
|
|
||||||
budget = js::SliceBudget(js::TimeBudget(std::max({delaySliceBudget,
|
budget = js::SliceBudget(js::TimeBudget(std::max({delaySliceBudget,
|
||||||
laterSliceBudget, (float)kICCSliceBudget})));
|
laterSliceBudget, (float)baseBudget})));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCycleCollector_collectSlice(budget, sDidPaintAfterPreviousICCSlice);
|
nsCycleCollector_collectSlice(budget);
|
||||||
sDidPaintAfterPreviousICCSlice = false;
|
|
||||||
|
|
||||||
gCCStats.FinishCycleCollectionSlice();
|
gCCStats.FinishCycleCollectionSlice();
|
||||||
}
|
}
|
||||||
|
@ -1529,11 +1714,11 @@ nsJSContext::GetMaxCCSliceTimeSinceClear()
|
||||||
return gCCStats.mMaxSliceTimeSinceClear;
|
return gCCStats.mMaxSliceTimeSinceClear;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
ICCTimerFired(nsITimer* aTimer, void* aClosure)
|
ICCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||||
{
|
{
|
||||||
if (sDidShutdown) {
|
if (sDidShutdown) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore ICC timer fires during IGC. Running ICC during an IGC will cause us
|
// Ignore ICC timer fires during IGC. Running ICC during an IGC will cause us
|
||||||
|
@ -1543,14 +1728,15 @@ ICCTimerFired(nsITimer* aTimer, void* aClosure)
|
||||||
PRTime now = PR_Now();
|
PRTime now = PR_Now();
|
||||||
if (sCCLockedOutTime == 0) {
|
if (sCCLockedOutTime == 0) {
|
||||||
sCCLockedOutTime = now;
|
sCCLockedOutTime = now;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
|
if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nsJSContext::RunCycleCollectorSlice();
|
nsJSContext::RunCycleCollectorSlice(aDeadline);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//static
|
//static
|
||||||
|
@ -1562,22 +1748,16 @@ nsJSContext::BeginCycleCollectionCallback()
|
||||||
gCCStats.mBeginTime = gCCStats.mBeginSliceTime.IsNull() ? TimeStamp::Now() : gCCStats.mBeginSliceTime;
|
gCCStats.mBeginTime = gCCStats.mBeginSliceTime.IsNull() ? TimeStamp::Now() : gCCStats.mBeginSliceTime;
|
||||||
gCCStats.mSuspected = nsCycleCollector_suspectedCount();
|
gCCStats.mSuspected = nsCycleCollector_suspectedCount();
|
||||||
|
|
||||||
KillCCTimer();
|
KillCCRunner();
|
||||||
|
|
||||||
gCCStats.RunForgetSkippable();
|
gCCStats.RunForgetSkippable();
|
||||||
|
|
||||||
MOZ_ASSERT(!sICCTimer, "Tried to create a new ICC timer when one already existed.");
|
MOZ_ASSERT(!sICCRunner, "Tried to create a new ICC timer when one already existed.");
|
||||||
|
|
||||||
// Create an ICC timer even if ICC is globally disabled, because we could be manually triggering
|
// Create an ICC timer even if ICC is globally disabled, because we could be manually triggering
|
||||||
// an incremental collection, and we want to be sure to finish it.
|
// an incremental collection, and we want to be sure to finish it.
|
||||||
CallCreateInstance("@mozilla.org/timer;1", &sICCTimer);
|
sICCRunner = CollectorRunner::Create(ICCRunnerFired, kICCIntersliceDelay,
|
||||||
if (sICCTimer) {
|
kIdleICCSliceBudget, true);
|
||||||
sICCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
|
|
||||||
sICCTimer->InitWithNamedFuncCallback(ICCTimerFired, nullptr,
|
|
||||||
kICCIntersliceDelay,
|
|
||||||
nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY,
|
|
||||||
"ICCTimerFired");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(NS_GC_DELAY > kMaxICCDuration, "A max duration ICC shouldn't reduce GC delay to 0");
|
static_assert(NS_GC_DELAY > kMaxICCDuration, "A max duration ICC shouldn't reduce GC delay to 0");
|
||||||
|
@ -1588,7 +1768,7 @@ nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
nsJSContext::KillICCTimer();
|
nsJSContext::KillICCRunner();
|
||||||
|
|
||||||
// Update timing information for the current slice before we log it, if
|
// Update timing information for the current slice before we log it, if
|
||||||
// we previously called PrepareForCycleCollectionSlice(). During shutdown
|
// we previously called PrepareForCycleCollectionSlice(). During shutdown
|
||||||
|
@ -1736,16 +1916,24 @@ nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void
|
bool
|
||||||
InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure)
|
InterSliceGCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||||
{
|
{
|
||||||
nsJSContext::KillInterSliceGCTimer();
|
nsJSContext::KillInterSliceGCRunner();
|
||||||
int64_t budget = XRE_IsE10sParentProcess() && nsContentUtils::GetUserIsInteracting() && sActiveIntersliceGCBudget ?
|
MOZ_ASSERT(sActiveIntersliceGCBudget > 0);
|
||||||
sActiveIntersliceGCBudget : NS_INTERSLICE_GC_BUDGET;
|
int64_t budget = sActiveIntersliceGCBudget;
|
||||||
nsJSContext::GarbageCollectNow(JS::gcreason::INTER_SLICE_GC,
|
if (!aDeadline.IsNull()) {
|
||||||
|
budget = int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds());
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t reason = reinterpret_cast<uintptr_t>(aData);
|
||||||
|
nsJSContext::GarbageCollectNow(aData ?
|
||||||
|
static_cast<JS::gcreason::Reason>(reason) :
|
||||||
|
JS::gcreason::INTER_SLICE_GC,
|
||||||
nsJSContext::IncrementalGC,
|
nsJSContext::IncrementalGC,
|
||||||
nsJSContext::NonShrinkingGC,
|
nsJSContext::NonShrinkingGC,
|
||||||
budget);
|
budget);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -1753,9 +1941,12 @@ void
|
||||||
GCTimerFired(nsITimer *aTimer, void *aClosure)
|
GCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||||
{
|
{
|
||||||
nsJSContext::KillGCTimer();
|
nsJSContext::KillGCTimer();
|
||||||
uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
|
// Now start the actual GC after initial timer has fired.
|
||||||
nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
|
sInterSliceGCRunner = CollectorRunner::Create(InterSliceGCRunnerFired,
|
||||||
nsJSContext::IncrementalGC);
|
NS_INTERSLICE_GC_DELAY,
|
||||||
|
sActiveIntersliceGCBudget,
|
||||||
|
false,
|
||||||
|
aClosure);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -1778,11 +1969,11 @@ ShouldTriggerCC(uint32_t aSuspected)
|
||||||
TimeUntilNow(sLastCCEndTime) > NS_CC_FORCED);
|
TimeUntilNow(sLastCCEndTime) > NS_CC_FORCED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
CCTimerFired(nsITimer *aTimer, void *aClosure)
|
CCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||||
{
|
{
|
||||||
if (sDidShutdown) {
|
if (sDidShutdown) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t ccDelay = NS_CC_DELAY;
|
static uint32_t ccDelay = NS_CC_DELAY;
|
||||||
|
@ -1791,48 +1982,53 @@ CCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||||
|
|
||||||
PRTime now = PR_Now();
|
PRTime now = PR_Now();
|
||||||
if (sCCLockedOutTime == 0) {
|
if (sCCLockedOutTime == 0) {
|
||||||
// Reset sCCTimerFireCount so that we run forgetSkippable
|
// Reset sCCRunnerFireCount so that we run forgetSkippable
|
||||||
// often enough before CC. Because of reduced ccDelay
|
// often enough before CC. Because of reduced ccDelay
|
||||||
// forgetSkippable will be called just a few times.
|
// forgetSkippable will be called just a few times.
|
||||||
// NS_MAX_CC_LOCKEDOUT_TIME limit guarantees that we end up calling
|
// NS_MAX_CC_LOCKEDOUT_TIME limit guarantees that we end up calling
|
||||||
// forgetSkippable and CycleCollectNow eventually.
|
// forgetSkippable and CycleCollectNow eventually.
|
||||||
sCCTimerFireCount = 0;
|
sCCRunnerFireCount = 0;
|
||||||
sCCLockedOutTime = now;
|
sCCLockedOutTime = now;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
|
if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
++sCCTimerFireCount;
|
++sCCRunnerFireCount;
|
||||||
|
|
||||||
|
bool didDoWork = false;
|
||||||
|
|
||||||
// During early timer fires, we only run forgetSkippable. During the first
|
// During early timer fires, we only run forgetSkippable. During the first
|
||||||
// late timer fire, we decide if we are going to have a second and final
|
// late timer fire, we decide if we are going to have a second and final
|
||||||
// late timer fire, where we may begin to run the CC. Should run at least one
|
// late timer fire, where we may begin to run the CC. Should run at least one
|
||||||
// early timer fire to allow cleanup before the CC.
|
// early timer fire to allow cleanup before the CC.
|
||||||
int32_t numEarlyTimerFires = std::max((int32_t)ccDelay / NS_CC_SKIPPABLE_DELAY - 2, 1);
|
int32_t numEarlyTimerFires = std::max((int32_t)ccDelay / NS_CC_SKIPPABLE_DELAY - 2, 1);
|
||||||
bool isLateTimerFire = sCCTimerFireCount > numEarlyTimerFires;
|
bool isLateTimerFire = sCCRunnerFireCount > numEarlyTimerFires;
|
||||||
uint32_t suspected = nsCycleCollector_suspectedCount();
|
uint32_t suspected = nsCycleCollector_suspectedCount();
|
||||||
if (isLateTimerFire && ShouldTriggerCC(suspected)) {
|
if (isLateTimerFire && ShouldTriggerCC(suspected)) {
|
||||||
if (sCCTimerFireCount == numEarlyTimerFires + 1) {
|
if (sCCRunnerFireCount == numEarlyTimerFires + 1) {
|
||||||
FireForgetSkippable(suspected, true);
|
FireForgetSkippable(suspected, true);
|
||||||
|
didDoWork = true;
|
||||||
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||||
// Our efforts to avoid a CC have failed, so we return to let the
|
// Our efforts to avoid a CC have failed, so we return to let the
|
||||||
// timer fire once more to trigger a CC.
|
// timer fire once more to trigger a CC.
|
||||||
return;
|
return didDoWork;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// We are in the final timer fire and still meet the conditions for
|
// We are in the final timer fire and still meet the conditions for
|
||||||
// triggering a CC. Let RunCycleCollectorSlice finish the current IGC, if
|
// triggering a CC. Let RunCycleCollectorSlice finish the current IGC, if
|
||||||
// any because that will allow us to include the GC time in the CC pause.
|
// any because that will allow us to include the GC time in the CC pause.
|
||||||
nsJSContext::RunCycleCollectorSlice();
|
nsJSContext::RunCycleCollectorSlice(aDeadline);
|
||||||
|
didDoWork = true;
|
||||||
}
|
}
|
||||||
} else if (((sPreviousSuspectedCount + 100) <= suspected) ||
|
} else if (((sPreviousSuspectedCount + 100) <= suspected) ||
|
||||||
(sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS)) {
|
(sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS)) {
|
||||||
// Only do a forget skippable if there are more than a few new objects
|
// Only do a forget skippable if there are more than a few new objects
|
||||||
// or we're doing the initial forget skippables.
|
// or we're doing the initial forget skippables.
|
||||||
FireForgetSkippable(suspected, false);
|
FireForgetSkippable(suspected, false);
|
||||||
|
didDoWork = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLateTimerFire) {
|
if (isLateTimerFire) {
|
||||||
|
@ -1841,8 +2037,10 @@ CCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||||
// We have either just run the CC or decided we don't want to run the CC
|
// We have either just run the CC or decided we don't want to run the CC
|
||||||
// next time, so kill the timer.
|
// next time, so kill the timer.
|
||||||
sPreviousSuspectedCount = 0;
|
sPreviousSuspectedCount = 0;
|
||||||
nsJSContext::KillCCTimer();
|
nsJSContext::KillCCRunner();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return didDoWork;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -1889,13 +2087,13 @@ ReadyToTriggerExpensiveCollectorTimer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Check all of the various collector timers and see if they are waiting to fire.
|
// Check all of the various collector timers/runners and see if they are waiting to fire.
|
||||||
// For the synchronous collector timers, sGCTimer and sCCTimer, we only want to trigger
|
// For the synchronous collector timers/runners, sGCTimer and sCCRunner, we only want to
|
||||||
// the collection occasionally, because they are expensive. The incremental collector
|
// trigger the collection occasionally, because they are expensive. The incremental collector
|
||||||
// timers, sInterSliceGCTimer and sICCTimer, are fast and need to be run many times, so
|
// timers, sInterSliceGCRunner and sICCRunner, are fast and need to be run many times, so
|
||||||
// always run their corresponding timer.
|
// always run their corresponding timer.
|
||||||
|
|
||||||
// This does not check sFullGCTimer, as that's an even more expensive collection we run
|
// This does not check sFullGCTimer, as that's a more expensive collection we run
|
||||||
// on a long timer.
|
// on a long timer.
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -1913,8 +2111,8 @@ nsJSContext::RunNextCollectorTimer()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sInterSliceGCTimer) {
|
if (sInterSliceGCRunner) {
|
||||||
InterSliceGCTimerFired(nullptr, nullptr);
|
InterSliceGCRunnerFired(TimeStamp(), nullptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1922,15 +2120,15 @@ nsJSContext::RunNextCollectorTimer()
|
||||||
// anything if a GC is in progress.
|
// anything if a GC is in progress.
|
||||||
MOZ_ASSERT(!sCCLockedOut, "Don't check the CC timers if the CC is locked out.");
|
MOZ_ASSERT(!sCCLockedOut, "Don't check the CC timers if the CC is locked out.");
|
||||||
|
|
||||||
if (sCCTimer) {
|
if (sCCRunner) {
|
||||||
if (ReadyToTriggerExpensiveCollectorTimer()) {
|
if (ReadyToTriggerExpensiveCollectorTimer()) {
|
||||||
CCTimerFired(nullptr, nullptr);
|
CCRunnerFired(TimeStamp(), nullptr);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sICCTimer) {
|
if (sICCRunner) {
|
||||||
ICCTimerFired(nullptr, nullptr);
|
ICCRunnerFired(TimeStamp(), nullptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1952,12 +2150,12 @@ nsJSContext::PokeGC(JS::gcreason::Reason aReason,
|
||||||
sNeedsFullGC = true;
|
sNeedsFullGC = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sGCTimer || sInterSliceGCTimer) {
|
if (sGCTimer || sInterSliceGCRunner) {
|
||||||
// There's already a timer for GC'ing, just return
|
// There's already a timer for GC'ing, just return
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sCCTimer) {
|
if (sCCRunner) {
|
||||||
// Make sure CC is called...
|
// Make sure CC is called...
|
||||||
sNeedsFullCC = true;
|
sNeedsFullCC = true;
|
||||||
// and GC after it.
|
// and GC after it.
|
||||||
|
@ -1965,7 +2163,7 @@ nsJSContext::PokeGC(JS::gcreason::Reason aReason,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sICCTimer) {
|
if (sICCRunner) {
|
||||||
// Make sure GC is called after the current CC completes.
|
// Make sure GC is called after the current CC completes.
|
||||||
// No need to set sNeedsFullCC because we are currently running a CC.
|
// No need to set sNeedsFullCC because we are currently running a CC.
|
||||||
sNeedsGCAfterCC = true;
|
sNeedsGCAfterCC = true;
|
||||||
|
@ -1991,6 +2189,7 @@ nsJSContext::PokeGC(JS::gcreason::Reason aReason,
|
||||||
: NS_GC_DELAY),
|
: NS_GC_DELAY),
|
||||||
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
|
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
|
||||||
"GCTimerFired");
|
"GCTimerFired");
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2020,24 +2219,19 @@ nsJSContext::PokeShrinkingGC()
|
||||||
void
|
void
|
||||||
nsJSContext::MaybePokeCC()
|
nsJSContext::MaybePokeCC()
|
||||||
{
|
{
|
||||||
if (sCCTimer || sICCTimer || sShuttingDown || !sHasRunGC) {
|
if (sCCRunner || sICCRunner || sShuttingDown || !sHasRunGC) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||||
sCCTimerFireCount = 0;
|
sCCRunnerFireCount = 0;
|
||||||
CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
|
|
||||||
if (!sCCTimer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// We can kill some objects before running forgetSkippable.
|
// We can kill some objects before running forgetSkippable.
|
||||||
nsCycleCollector_dispatchDeferredDeletion();
|
nsCycleCollector_dispatchDeferredDeletion();
|
||||||
|
|
||||||
sCCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
|
sCCRunner =
|
||||||
sCCTimer->InitWithNamedFuncCallback(CCTimerFired, nullptr,
|
CollectorRunner::Create(CCRunnerFired, NS_CC_SKIPPABLE_DELAY,
|
||||||
NS_CC_SKIPPABLE_DELAY,
|
kForgetSkippableSliceDuration, true);
|
||||||
nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY,
|
|
||||||
"CCTimerFired");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2061,11 +2255,11 @@ nsJSContext::KillFullGCTimer()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsJSContext::KillInterSliceGCTimer()
|
nsJSContext::KillInterSliceGCRunner()
|
||||||
{
|
{
|
||||||
if (sInterSliceGCTimer) {
|
if (sInterSliceGCRunner) {
|
||||||
sInterSliceGCTimer->Cancel();
|
sInterSliceGCRunner->Cancel();
|
||||||
NS_RELEASE(sInterSliceGCTimer);
|
sInterSliceGCRunner = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2081,24 +2275,24 @@ nsJSContext::KillShrinkingGCTimer()
|
||||||
|
|
||||||
//static
|
//static
|
||||||
void
|
void
|
||||||
nsJSContext::KillCCTimer()
|
nsJSContext::KillCCRunner()
|
||||||
{
|
{
|
||||||
sCCLockedOutTime = 0;
|
sCCLockedOutTime = 0;
|
||||||
if (sCCTimer) {
|
if (sCCRunner) {
|
||||||
sCCTimer->Cancel();
|
sCCRunner->Cancel();
|
||||||
NS_RELEASE(sCCTimer);
|
sCCRunner = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//static
|
//static
|
||||||
void
|
void
|
||||||
nsJSContext::KillICCTimer()
|
nsJSContext::KillICCRunner()
|
||||||
{
|
{
|
||||||
sCCLockedOutTime = 0;
|
sCCLockedOutTime = 0;
|
||||||
|
|
||||||
if (sICCTimer) {
|
if (sICCRunner) {
|
||||||
sICCTimer->Cancel();
|
sICCRunner->Cancel();
|
||||||
NS_RELEASE(sICCTimer);
|
sICCRunner = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2170,8 +2364,8 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
|
||||||
sCCLockedOut = false;
|
sCCLockedOut = false;
|
||||||
sIsCompactingOnUserInactive = false;
|
sIsCompactingOnUserInactive = false;
|
||||||
|
|
||||||
// May need to kill the inter-slice GC timer
|
// May need to kill the inter-slice GC runner
|
||||||
nsJSContext::KillInterSliceGCTimer();
|
nsJSContext::KillInterSliceGCRunner();
|
||||||
|
|
||||||
sCCollectedWaitingForGC = 0;
|
sCCollectedWaitingForGC = 0;
|
||||||
sCCollectedZonesWaitingForGC = 0;
|
sCCollectedZonesWaitingForGC = 0;
|
||||||
|
@ -2212,15 +2406,11 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
|
||||||
case JS::GC_SLICE_END:
|
case JS::GC_SLICE_END:
|
||||||
|
|
||||||
// Schedule another GC slice if the GC has more work to do.
|
// Schedule another GC slice if the GC has more work to do.
|
||||||
nsJSContext::KillInterSliceGCTimer();
|
nsJSContext::KillInterSliceGCRunner();
|
||||||
if (!sShuttingDown && !aDesc.isComplete_) {
|
if (!sShuttingDown && !aDesc.isComplete_) {
|
||||||
CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer);
|
sInterSliceGCRunner =
|
||||||
sInterSliceGCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
|
CollectorRunner::Create(InterSliceGCRunnerFired, NS_INTERSLICE_GC_DELAY,
|
||||||
sInterSliceGCTimer->InitWithNamedFuncCallback(InterSliceGCTimerFired,
|
sActiveIntersliceGCBudget, false);
|
||||||
nullptr,
|
|
||||||
NS_INTERSLICE_GC_DELAY,
|
|
||||||
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
|
|
||||||
"InterSliceGCTimerFired");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||||
|
@ -2274,7 +2464,7 @@ void
|
||||||
mozilla::dom::StartupJSEnvironment()
|
mozilla::dom::StartupJSEnvironment()
|
||||||
{
|
{
|
||||||
// initialize all our statics, so that we can restart XPCOM
|
// initialize all our statics, so that we can restart XPCOM
|
||||||
sGCTimer = sShrinkingGCTimer = sFullGCTimer = sCCTimer = sICCTimer = nullptr;
|
sGCTimer = sShrinkingGCTimer = sFullGCTimer = nullptr;
|
||||||
sCCLockedOut = false;
|
sCCLockedOut = false;
|
||||||
sCCLockedOutTime = 0;
|
sCCLockedOutTime = 0;
|
||||||
sLastCCEndTime = TimeStamp();
|
sLastCCEndTime = TimeStamp();
|
||||||
|
@ -2609,52 +2799,6 @@ nsJSContext::EnsureStatics()
|
||||||
sIsInitialized = true;
|
sIsInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
nsJSContext::NotifyDidPaint()
|
|
||||||
{
|
|
||||||
sDidPaintAfterPreviousICCSlice = true;
|
|
||||||
if (sICCTimer) {
|
|
||||||
static uint32_t sCount = 0;
|
|
||||||
// 16 here is the common value for refresh driver tick frequency.
|
|
||||||
static const uint32_t kTicksPerSliceDelay = kICCIntersliceDelay / 16;
|
|
||||||
if (++sCount % kTicksPerSliceDelay != 0) {
|
|
||||||
// Don't trigger CC slice all the time after paint, but often still.
|
|
||||||
// The key point is to trigger it right after paint, especially when
|
|
||||||
// we're running RefreshDriver constantly.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sICCTimer->Cancel();
|
|
||||||
ICCTimerFired(nullptr, nullptr);
|
|
||||||
if (sICCTimer) {
|
|
||||||
sICCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
|
|
||||||
sICCTimer->InitWithNamedFuncCallback(ICCTimerFired, nullptr,
|
|
||||||
kICCIntersliceDelay,
|
|
||||||
nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY,
|
|
||||||
"ICCTimerFired");
|
|
||||||
}
|
|
||||||
} else if (sCCTimer) {
|
|
||||||
static uint32_t sCount = 0;
|
|
||||||
static const uint32_t kTicksPerForgetSkippableDelay =
|
|
||||||
NS_CC_SKIPPABLE_DELAY / 16;
|
|
||||||
if (++sCount % kTicksPerForgetSkippableDelay != 0) {
|
|
||||||
// The comment above about triggering CC slice applies to forget skippable
|
|
||||||
// too.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sCCTimer->Cancel();
|
|
||||||
CCTimerFired(nullptr, nullptr);
|
|
||||||
if (sCCTimer) {
|
|
||||||
sCCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
|
|
||||||
sCCTimer->InitWithNamedFuncCallback(CCTimerFired, nullptr,
|
|
||||||
NS_CC_SKIPPABLE_DELAY,
|
|
||||||
nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY,
|
|
||||||
"CCTimerFired");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsScriptNameSpaceManager*
|
nsScriptNameSpaceManager*
|
||||||
mozilla::dom::GetNameSpaceManager()
|
mozilla::dom::GetNameSpaceManager()
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "nsIXPConnect.h"
|
#include "nsIXPConnect.h"
|
||||||
#include "nsIArray.h"
|
#include "nsIArray.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
|
#include "mozilla/TimeStamp.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "xpcpublic.h"
|
#include "xpcpublic.h"
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@ public:
|
||||||
int32_t aExtraForgetSkippableCalls = 0);
|
int32_t aExtraForgetSkippableCalls = 0);
|
||||||
|
|
||||||
// Run a cycle collector slice, using a heuristic to decide how long to run it.
|
// Run a cycle collector slice, using a heuristic to decide how long to run it.
|
||||||
static void RunCycleCollectorSlice();
|
static void RunCycleCollectorSlice(mozilla::TimeStamp aDeadline);
|
||||||
|
|
||||||
// Run a cycle collector slice, using the given work budget.
|
// Run a cycle collector slice, using the given work budget.
|
||||||
static void RunCycleCollectorWorkSlice(int64_t aWorkBudget);
|
static void RunCycleCollectorWorkSlice(int64_t aWorkBudget);
|
||||||
|
@ -113,10 +114,10 @@ public:
|
||||||
static void KillShrinkingGCTimer();
|
static void KillShrinkingGCTimer();
|
||||||
|
|
||||||
static void MaybePokeCC();
|
static void MaybePokeCC();
|
||||||
static void KillCCTimer();
|
static void KillCCRunner();
|
||||||
static void KillICCTimer();
|
static void KillICCRunner();
|
||||||
static void KillFullGCTimer();
|
static void KillFullGCTimer();
|
||||||
static void KillInterSliceGCTimer();
|
static void KillInterSliceGCRunner();
|
||||||
|
|
||||||
// Calling LikelyShortLivingObjectCreated() makes a GC more likely.
|
// Calling LikelyShortLivingObjectCreated() makes a GC more likely.
|
||||||
static void LikelyShortLivingObjectCreated();
|
static void LikelyShortLivingObjectCreated();
|
||||||
|
@ -131,7 +132,6 @@ public:
|
||||||
return global ? mGlobalObjectRef.get() : nullptr;
|
return global ? mGlobalObjectRef.get() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void NotifyDidPaint();
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~nsJSContext();
|
virtual ~nsJSContext();
|
||||||
|
|
||||||
|
|
|
@ -490,6 +490,7 @@ ContentChild* ContentChild::sSingleton;
|
||||||
ContentChild::ContentChild()
|
ContentChild::ContentChild()
|
||||||
: mID(uint64_t(-1))
|
: mID(uint64_t(-1))
|
||||||
#if defined(XP_WIN) && defined(ACCESSIBILITY)
|
#if defined(XP_WIN) && defined(ACCESSIBILITY)
|
||||||
|
, mMainChromeTid(0)
|
||||||
, mMsaaID(0)
|
, mMsaaID(0)
|
||||||
#endif
|
#endif
|
||||||
, mCanOverrideProcessName(true)
|
, mCanOverrideProcessName(true)
|
||||||
|
@ -1390,6 +1391,7 @@ StartMacOSContentSandbox()
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAutoCString tempDirPath;
|
nsAutoCString tempDirPath;
|
||||||
|
tempDir->Normalize();
|
||||||
rv = tempDir->GetNativePath(tempDirPath);
|
rv = tempDir->GetNativePath(tempDirPath);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
MOZ_CRASH("Failed to get NS_OS_TEMP_DIR path");
|
MOZ_CRASH("Failed to get NS_OS_TEMP_DIR path");
|
||||||
|
@ -2422,10 +2424,14 @@ ContentChild::RecvFlushMemory(const nsString& reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
mozilla::ipc::IPCResult
|
mozilla::ipc::IPCResult
|
||||||
ContentChild::RecvActivateA11y(const uint32_t& aMsaaID)
|
ContentChild::RecvActivateA11y(const uint32_t& aMainChromeTid,
|
||||||
|
const uint32_t& aMsaaID)
|
||||||
{
|
{
|
||||||
#ifdef ACCESSIBILITY
|
#ifdef ACCESSIBILITY
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
|
MOZ_ASSERT(aMainChromeTid != 0);
|
||||||
|
mMainChromeTid = aMainChromeTid;
|
||||||
|
|
||||||
MOZ_ASSERT(aMsaaID != 0);
|
MOZ_ASSERT(aMsaaID != 0);
|
||||||
mMsaaID = aMsaaID;
|
mMsaaID = aMsaaID;
|
||||||
#endif // XP_WIN
|
#endif // XP_WIN
|
||||||
|
|
|
@ -388,7 +388,8 @@ public:
|
||||||
|
|
||||||
virtual mozilla::ipc::IPCResult RecvFlushMemory(const nsString& reason) override;
|
virtual mozilla::ipc::IPCResult RecvFlushMemory(const nsString& reason) override;
|
||||||
|
|
||||||
virtual mozilla::ipc::IPCResult RecvActivateA11y(const uint32_t& aMsaaID) override;
|
virtual mozilla::ipc::IPCResult RecvActivateA11y(const uint32_t& aMainChromeTid,
|
||||||
|
const uint32_t& aMsaaID) override;
|
||||||
virtual mozilla::ipc::IPCResult RecvShutdownA11y() override;
|
virtual mozilla::ipc::IPCResult RecvShutdownA11y() override;
|
||||||
|
|
||||||
virtual mozilla::ipc::IPCResult RecvGarbageCollect() override;
|
virtual mozilla::ipc::IPCResult RecvGarbageCollect() override;
|
||||||
|
@ -499,6 +500,8 @@ public:
|
||||||
ContentParentId GetID() const { return mID; }
|
ContentParentId GetID() const { return mID; }
|
||||||
|
|
||||||
#if defined(XP_WIN) && defined(ACCESSIBILITY)
|
#if defined(XP_WIN) && defined(ACCESSIBILITY)
|
||||||
|
uint32_t GetChromeMainThreadId() const { return mMainChromeTid; }
|
||||||
|
|
||||||
uint32_t GetMsaaID() const { return mMsaaID; }
|
uint32_t GetMsaaID() const { return mMsaaID; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -699,6 +702,11 @@ private:
|
||||||
ContentParentId mID;
|
ContentParentId mID;
|
||||||
|
|
||||||
#if defined(XP_WIN) && defined(ACCESSIBILITY)
|
#if defined(XP_WIN) && defined(ACCESSIBILITY)
|
||||||
|
/**
|
||||||
|
* The thread ID of the main thread in the chrome process.
|
||||||
|
*/
|
||||||
|
uint32_t mMainChromeTid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is an a11y-specific unique id for the content process that is
|
* This is an a11y-specific unique id for the content process that is
|
||||||
* generated by the chrome process.
|
* generated by the chrome process.
|
||||||
|
|
|
@ -1364,10 +1364,15 @@ ContentParent::Init()
|
||||||
// process.
|
// process.
|
||||||
if (nsIPresShell::IsAccessibilityActive()) {
|
if (nsIPresShell::IsAccessibilityActive()) {
|
||||||
#if defined(XP_WIN)
|
#if defined(XP_WIN)
|
||||||
Unused <<
|
#if defined(RELEASE_OR_BETA)
|
||||||
SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
|
// On Windows we currently only enable a11y in the content process
|
||||||
|
// for testing purposes.
|
||||||
|
if (Preferences::GetBool(kForceEnableE10sPref, false))
|
||||||
|
#endif
|
||||||
|
Unused << SendActivateA11y(::GetCurrentThreadId(),
|
||||||
|
a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
|
||||||
#else
|
#else
|
||||||
Unused << SendActivateA11y(0);
|
Unused << SendActivateA11y(0, 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -2772,10 +2777,15 @@ ContentParent::Observe(nsISupports* aSubject,
|
||||||
// Make sure accessibility is running in content process when
|
// Make sure accessibility is running in content process when
|
||||||
// accessibility gets initiated in chrome process.
|
// accessibility gets initiated in chrome process.
|
||||||
#if defined(XP_WIN)
|
#if defined(XP_WIN)
|
||||||
Unused <<
|
#if defined(RELEASE_OR_BETA)
|
||||||
SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
|
// On Windows we currently only enable a11y in the content process
|
||||||
|
// for testing purposes.
|
||||||
|
if (Preferences::GetBool(kForceEnableE10sPref, false))
|
||||||
|
#endif
|
||||||
|
Unused << SendActivateA11y(::GetCurrentThreadId(),
|
||||||
|
a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
|
||||||
#else
|
#else
|
||||||
Unused << SendActivateA11y(0);
|
Unused << SendActivateA11y(0, 0);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
// If possible, shut down accessibility in content process when
|
// If possible, shut down accessibility in content process when
|
||||||
|
|
|
@ -443,11 +443,13 @@ child:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start accessibility engine in content process.
|
* Start accessibility engine in content process.
|
||||||
|
* @param aTid is the thread ID of the chrome process main thread. Only used
|
||||||
|
* on Windows; pass 0 on other platforms.
|
||||||
* @param aMsaaID is an a11y-specific unique id for the content process
|
* @param aMsaaID is an a11y-specific unique id for the content process
|
||||||
* that is generated by the chrome process. Only used on
|
* that is generated by the chrome process. Only used on
|
||||||
* Windows; pass 0 on other platforms.
|
* Windows; pass 0 on other platforms.
|
||||||
*/
|
*/
|
||||||
async ActivateA11y(uint32_t aMsaaID);
|
async ActivateA11y(uint32_t aMainChromeTid, uint32_t aMsaaID);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shutdown accessibility engine in content process (if not in use).
|
* Shutdown accessibility engine in content process (if not in use).
|
||||||
|
|
|
@ -303,8 +303,7 @@ SVGMarkerElement::GetPreserveAspectRatio()
|
||||||
|
|
||||||
gfx::Matrix
|
gfx::Matrix
|
||||||
SVGMarkerElement::GetMarkerTransform(float aStrokeWidth,
|
SVGMarkerElement::GetMarkerTransform(float aStrokeWidth,
|
||||||
float aX, float aY, float aAutoAngle,
|
const nsSVGMark& aMark)
|
||||||
bool aIsStart)
|
|
||||||
{
|
{
|
||||||
float scale = mEnumAttributes[MARKERUNITS].GetAnimValue() ==
|
float scale = mEnumAttributes[MARKERUNITS].GetAnimValue() ==
|
||||||
SVG_MARKERUNITS_STROKEWIDTH ? aStrokeWidth : 1.0f;
|
SVG_MARKERUNITS_STROKEWIDTH ? aStrokeWidth : 1.0f;
|
||||||
|
@ -312,10 +311,10 @@ SVGMarkerElement::GetMarkerTransform(float aStrokeWidth,
|
||||||
float angle;
|
float angle;
|
||||||
switch (mOrientType.GetAnimValueInternal()) {
|
switch (mOrientType.GetAnimValueInternal()) {
|
||||||
case SVG_MARKER_ORIENT_AUTO:
|
case SVG_MARKER_ORIENT_AUTO:
|
||||||
angle = aAutoAngle;
|
angle = aMark.angle;
|
||||||
break;
|
break;
|
||||||
case SVG_MARKER_ORIENT_AUTO_START_REVERSE:
|
case SVG_MARKER_ORIENT_AUTO_START_REVERSE:
|
||||||
angle = aAutoAngle + (aIsStart ? M_PI : 0.0f);
|
angle = aMark.angle + (aMark.type == nsSVGMark::eStart ? M_PI : 0.0f);
|
||||||
break;
|
break;
|
||||||
default: // SVG_MARKER_ORIENT_ANGLE
|
default: // SVG_MARKER_ORIENT_ANGLE
|
||||||
angle = mAngleAttributes[ORIENT].GetAnimValue() * M_PI / 180.0f;
|
angle = mAngleAttributes[ORIENT].GetAnimValue() * M_PI / 180.0f;
|
||||||
|
@ -324,7 +323,7 @@ SVGMarkerElement::GetMarkerTransform(float aStrokeWidth,
|
||||||
|
|
||||||
return gfx::Matrix(cos(angle) * scale, sin(angle) * scale,
|
return gfx::Matrix(cos(angle) * scale, sin(angle) * scale,
|
||||||
-sin(angle) * scale, cos(angle) * scale,
|
-sin(angle) * scale, cos(angle) * scale,
|
||||||
aX, aY);
|
aMark.x, aMark.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsSVGViewBoxRect
|
nsSVGViewBoxRect
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "mozilla/dom/SVGAnimatedEnumeration.h"
|
#include "mozilla/dom/SVGAnimatedEnumeration.h"
|
||||||
|
|
||||||
class nsSVGMarkerFrame;
|
class nsSVGMarkerFrame;
|
||||||
|
struct nsSVGMark;
|
||||||
|
|
||||||
nsresult NS_NewSVGMarkerElement(nsIContent **aResult,
|
nsresult NS_NewSVGMarkerElement(nsIContent **aResult,
|
||||||
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
|
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
|
||||||
|
@ -121,9 +122,7 @@ public:
|
||||||
virtual bool HasValidDimensions() const override;
|
virtual bool HasValidDimensions() const override;
|
||||||
|
|
||||||
// public helpers
|
// public helpers
|
||||||
gfx::Matrix GetMarkerTransform(float aStrokeWidth,
|
gfx::Matrix GetMarkerTransform(float aStrokeWidth, const nsSVGMark& aMark);
|
||||||
float aX, float aY, float aAutoAngle,
|
|
||||||
bool aIsStart);
|
|
||||||
nsSVGViewBoxRect GetViewBoxRect();
|
nsSVGViewBoxRect GetViewBoxRect();
|
||||||
gfx::Matrix GetViewBoxTransform();
|
gfx::Matrix GetViewBoxTransform();
|
||||||
|
|
||||||
|
|
|
@ -894,6 +894,11 @@ public:
|
||||||
* normally return the same SourceSurface object.
|
* normally return the same SourceSurface object.
|
||||||
*/
|
*/
|
||||||
virtual already_AddRefed<SourceSurface> Snapshot() = 0;
|
virtual already_AddRefed<SourceSurface> Snapshot() = 0;
|
||||||
|
|
||||||
|
// Snapshots the contents and returns an alpha mask
|
||||||
|
// based on the RGB values.
|
||||||
|
virtual already_AddRefed<SourceSurface> IntoLuminanceSource(LuminanceType aLuminanceType,
|
||||||
|
float aOpacity);
|
||||||
virtual IntSize GetSize() = 0;
|
virtual IntSize GetSize() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,9 +9,162 @@
|
||||||
|
|
||||||
#include "DrawTargetCapture.h"
|
#include "DrawTargetCapture.h"
|
||||||
|
|
||||||
|
#ifdef BUILD_ARM_NEON
|
||||||
|
#include "mozilla/arm.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Byte offsets of channels in a native packed gfxColor or cairo image surface.
|
||||||
|
*/
|
||||||
|
#ifdef IS_BIG_ENDIAN
|
||||||
|
#define GFX_ARGB32_OFFSET_A 0
|
||||||
|
#define GFX_ARGB32_OFFSET_R 1
|
||||||
|
#define GFX_ARGB32_OFFSET_G 2
|
||||||
|
#define GFX_ARGB32_OFFSET_B 3
|
||||||
|
#else
|
||||||
|
#define GFX_ARGB32_OFFSET_A 3
|
||||||
|
#define GFX_ARGB32_OFFSET_R 2
|
||||||
|
#define GFX_ARGB32_OFFSET_G 1
|
||||||
|
#define GFX_ARGB32_OFFSET_B 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// c = n / 255
|
||||||
|
// c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)) * 255 + 0.5
|
||||||
|
static const uint8_t gsRGBToLinearRGBMap[256] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 2, 2, 2, 2, 2, 2,
|
||||||
|
2, 2, 3, 3, 3, 3, 3, 3,
|
||||||
|
4, 4, 4, 4, 4, 5, 5, 5,
|
||||||
|
5, 6, 6, 6, 6, 7, 7, 7,
|
||||||
|
8, 8, 8, 8, 9, 9, 9, 10,
|
||||||
|
10, 10, 11, 11, 12, 12, 12, 13,
|
||||||
|
13, 13, 14, 14, 15, 15, 16, 16,
|
||||||
|
17, 17, 17, 18, 18, 19, 19, 20,
|
||||||
|
20, 21, 22, 22, 23, 23, 24, 24,
|
||||||
|
25, 25, 26, 27, 27, 28, 29, 29,
|
||||||
|
30, 30, 31, 32, 32, 33, 34, 35,
|
||||||
|
35, 36, 37, 37, 38, 39, 40, 41,
|
||||||
|
41, 42, 43, 44, 45, 45, 46, 47,
|
||||||
|
48, 49, 50, 51, 51, 52, 53, 54,
|
||||||
|
55, 56, 57, 58, 59, 60, 61, 62,
|
||||||
|
63, 64, 65, 66, 67, 68, 69, 70,
|
||||||
|
71, 72, 73, 74, 76, 77, 78, 79,
|
||||||
|
80, 81, 82, 84, 85, 86, 87, 88,
|
||||||
|
90, 91, 92, 93, 95, 96, 97, 99,
|
||||||
|
100, 101, 103, 104, 105, 107, 108, 109,
|
||||||
|
111, 112, 114, 115, 116, 118, 119, 121,
|
||||||
|
122, 124, 125, 127, 128, 130, 131, 133,
|
||||||
|
134, 136, 138, 139, 141, 142, 144, 146,
|
||||||
|
147, 149, 151, 152, 154, 156, 157, 159,
|
||||||
|
161, 163, 164, 166, 168, 170, 171, 173,
|
||||||
|
175, 177, 179, 181, 183, 184, 186, 188,
|
||||||
|
190, 192, 194, 196, 198, 200, 202, 204,
|
||||||
|
206, 208, 210, 212, 214, 216, 218, 220,
|
||||||
|
222, 224, 226, 229, 231, 233, 235, 237,
|
||||||
|
239, 242, 244, 246, 248, 250, 253, 255
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
ComputesRGBLuminanceMask(const uint8_t *aSourceData,
|
||||||
|
int32_t aSourceStride,
|
||||||
|
uint8_t *aDestData,
|
||||||
|
int32_t aDestStride,
|
||||||
|
const IntSize &aSize,
|
||||||
|
float aOpacity)
|
||||||
|
{
|
||||||
|
int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity
|
||||||
|
int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity
|
||||||
|
int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721
|
||||||
|
int32_t sourceOffset = aSourceStride - 4 * aSize.width;
|
||||||
|
const uint8_t *sourcePixel = aSourceData;
|
||||||
|
int32_t destOffset = aDestStride - aSize.width;
|
||||||
|
uint8_t *destPixel = aDestData;
|
||||||
|
|
||||||
|
for (int32_t y = 0; y < aSize.height; y++) {
|
||||||
|
for (int32_t x = 0; x < aSize.width; x++) {
|
||||||
|
uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A];
|
||||||
|
|
||||||
|
if (a) {
|
||||||
|
*destPixel = (redFactor * sourcePixel[GFX_ARGB32_OFFSET_R] +
|
||||||
|
greenFactor * sourcePixel[GFX_ARGB32_OFFSET_G] +
|
||||||
|
blueFactor * sourcePixel[GFX_ARGB32_OFFSET_B]) >> 8;
|
||||||
|
} else {
|
||||||
|
*destPixel = 0;
|
||||||
|
}
|
||||||
|
sourcePixel += 4;
|
||||||
|
destPixel++;
|
||||||
|
}
|
||||||
|
sourcePixel += sourceOffset;
|
||||||
|
destPixel += destOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ComputeLinearRGBLuminanceMask(const uint8_t *aSourceData,
|
||||||
|
int32_t aSourceStride,
|
||||||
|
uint8_t *aDestData,
|
||||||
|
int32_t aDestStride,
|
||||||
|
const IntSize &aSize,
|
||||||
|
float aOpacity)
|
||||||
|
{
|
||||||
|
int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity
|
||||||
|
int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity
|
||||||
|
int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721
|
||||||
|
int32_t sourceOffset = aSourceStride - 4 * aSize.width;
|
||||||
|
const uint8_t *sourcePixel = aSourceData;
|
||||||
|
int32_t destOffset = aDestStride - aSize.width;
|
||||||
|
uint8_t *destPixel = aDestData;
|
||||||
|
|
||||||
|
for (int32_t y = 0; y < aSize.height; y++) {
|
||||||
|
for (int32_t x = 0; x < aSize.width; x++) {
|
||||||
|
uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A];
|
||||||
|
|
||||||
|
// unpremultiply
|
||||||
|
if (a) {
|
||||||
|
if (a == 255) {
|
||||||
|
/* sRGB -> linearRGB -> intensity */
|
||||||
|
*destPixel =
|
||||||
|
static_cast<uint8_t>
|
||||||
|
((gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_R]] *
|
||||||
|
redFactor +
|
||||||
|
gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_G]] *
|
||||||
|
greenFactor +
|
||||||
|
gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_B]] *
|
||||||
|
blueFactor) >> 8);
|
||||||
|
} else {
|
||||||
|
uint8_t tempPixel[4];
|
||||||
|
tempPixel[GFX_ARGB32_OFFSET_B] =
|
||||||
|
(255 * sourcePixel[GFX_ARGB32_OFFSET_B]) / a;
|
||||||
|
tempPixel[GFX_ARGB32_OFFSET_G] =
|
||||||
|
(255 * sourcePixel[GFX_ARGB32_OFFSET_G]) / a;
|
||||||
|
tempPixel[GFX_ARGB32_OFFSET_R] =
|
||||||
|
(255 * sourcePixel[GFX_ARGB32_OFFSET_R]) / a;
|
||||||
|
|
||||||
|
/* sRGB -> linearRGB -> intensity */
|
||||||
|
*destPixel =
|
||||||
|
static_cast<uint8_t>
|
||||||
|
(((gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_R]] *
|
||||||
|
redFactor +
|
||||||
|
gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_G]] *
|
||||||
|
greenFactor +
|
||||||
|
gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_B]] *
|
||||||
|
blueFactor) >> 8) * (a / 255.0f));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*destPixel = 0;
|
||||||
|
}
|
||||||
|
sourcePixel += 4;
|
||||||
|
destPixel++;
|
||||||
|
}
|
||||||
|
sourcePixel += sourceOffset;
|
||||||
|
destPixel += destOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
already_AddRefed<DrawTargetCapture>
|
already_AddRefed<DrawTargetCapture>
|
||||||
DrawTarget::CreateCaptureDT(const IntSize& aSize)
|
DrawTarget::CreateCaptureDT(const IntSize& aSize)
|
||||||
{
|
{
|
||||||
|
@ -64,6 +217,51 @@ DrawTarget::StrokeGlyphs(ScaledFont* aFont,
|
||||||
Stroke(path, aPattern, aStrokeOptions, aOptions);
|
Stroke(path, aPattern, aStrokeOptions, aOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
already_AddRefed<SourceSurface>
|
||||||
|
DrawTarget::IntoLuminanceSource(LuminanceType aMaskType, float aOpacity)
|
||||||
|
{
|
||||||
|
RefPtr<SourceSurface> surface = Snapshot();
|
||||||
|
IntSize size = surface->GetSize();
|
||||||
|
|
||||||
|
RefPtr<DataSourceSurface> maskSurface = surface->GetDataSurface();
|
||||||
|
DataSourceSurface::MappedSurface map;
|
||||||
|
if (!maskSurface->Map(DataSourceSurface::MapType::READ, &map)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create alpha channel mask for output
|
||||||
|
RefPtr<DataSourceSurface> destMaskSurface =
|
||||||
|
Factory::CreateDataSourceSurface(size, SurfaceFormat::A8);
|
||||||
|
if (!destMaskSurface) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
DataSourceSurface::MappedSurface destMap;
|
||||||
|
if (!destMaskSurface->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (aMaskType) {
|
||||||
|
case LuminanceType::LUMINANCE:
|
||||||
|
{
|
||||||
|
ComputesRGBLuminanceMask(map.mData, map.mStride,
|
||||||
|
destMap.mData, destMap.mStride,
|
||||||
|
size, aOpacity);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LuminanceType::LINEARRGB:
|
||||||
|
{
|
||||||
|
ComputeLinearRGBLuminanceMask(map.mData, map.mStride,
|
||||||
|
destMap.mData, destMap.mStride,
|
||||||
|
size, aOpacity);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maskSurface->Unmap();
|
||||||
|
destMaskSurface->Unmap();
|
||||||
|
|
||||||
|
return destMaskSurface.forget();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace gfx
|
} // namespace gfx
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
@ -99,6 +99,38 @@ DrawTargetD2D1::Snapshot()
|
||||||
return snapshot.forget();
|
return snapshot.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DrawTargetD2D1::EnsureLuminanceEffect()
|
||||||
|
{
|
||||||
|
if (mLuminanceEffect.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT hr = mDC->CreateEffect(CLSID_D2D1LuminanceToAlpha,
|
||||||
|
getter_AddRefs(mLuminanceEffect));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
gfxWarning() << "Failed to create luminance effect. Code: " << hexa(hr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<SourceSurface>
|
||||||
|
DrawTargetD2D1::IntoLuminanceSource(LuminanceType aLuminanceType, float aOpacity)
|
||||||
|
{
|
||||||
|
//return DrawTarget::IntoLuminanceSource(aLuminanceType, aOpacity);
|
||||||
|
if (aLuminanceType != LuminanceType::LUMINANCE) {
|
||||||
|
return DrawTarget::IntoLuminanceSource(aLuminanceType, aOpacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the luminance effect
|
||||||
|
EnsureLuminanceEffect();
|
||||||
|
mLuminanceEffect->SetInput(0, mBitmap);
|
||||||
|
|
||||||
|
RefPtr<ID2D1Image> luminanceOutput;
|
||||||
|
mLuminanceEffect->GetOutput(getter_AddRefs(luminanceOutput));
|
||||||
|
|
||||||
|
return MakeAndAddRef<SourceSurfaceD2D1>(luminanceOutput, mDC, SurfaceFormat::A8, mSize);
|
||||||
|
}
|
||||||
|
|
||||||
// Command lists are kept around by device contexts until EndDraw is called,
|
// Command lists are kept around by device contexts until EndDraw is called,
|
||||||
// this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
|
// this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
|
||||||
// are expensive though, especially relatively when little work is done, so
|
// are expensive though, especially relatively when little work is done, so
|
||||||
|
@ -348,20 +380,48 @@ DrawTargetD2D1::MaskSurface(const Pattern &aSource,
|
||||||
|
|
||||||
PrepareForDrawing(aOptions.mCompositionOp, aSource);
|
PrepareForDrawing(aOptions.mCompositionOp, aSource);
|
||||||
|
|
||||||
|
IntSize size = IntSize::Truncate(aMask->GetSize().width, aMask->GetSize().height);
|
||||||
|
Rect dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height));
|
||||||
|
|
||||||
|
HRESULT hr = image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
|
||||||
|
if (!bitmap || FAILED(hr)) {
|
||||||
|
// D2D says if we have an actual ID2D1Image and not a bitmap underlying the object,
|
||||||
|
// we can't query for a bitmap. Instead, Push/PopLayer
|
||||||
|
gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces. Falling back to push/pop layer";
|
||||||
|
|
||||||
|
RefPtr<ID2D1Brush> source = CreateBrushForPattern(aSource, aOptions.mAlpha);
|
||||||
|
RefPtr<ID2D1ImageBrush> maskBrush;
|
||||||
|
hr = mDC->CreateImageBrush(image,
|
||||||
|
D2D1::ImageBrushProperties(D2D1::RectF(0, 0, size.width, size.height)),
|
||||||
|
D2D1::BrushProperties(1.0f, D2D1::IdentityMatrix()),
|
||||||
|
getter_AddRefs(maskBrush));
|
||||||
|
MOZ_ASSERT(SUCCEEDED(hr));
|
||||||
|
|
||||||
|
mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), nullptr,
|
||||||
|
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
|
||||||
|
D2D1::IdentityMatrix(),
|
||||||
|
1.0f, maskBrush, D2D1_LAYER_OPTIONS1_NONE),
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
mDC->FillRectangle(D2DRect(dest), source);
|
||||||
|
mDC->PopLayer();
|
||||||
|
|
||||||
|
FinalizeDrawing(aOptions.mCompositionOp, aSource);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// If this is a data source surface, we might have created a partial bitmap
|
||||||
|
// for this surface and only uploaded part of the mask. In that case,
|
||||||
|
// we have to fixup our sizes here.
|
||||||
|
size.width = bitmap->GetSize().width;
|
||||||
|
size.height = bitmap->GetSize().height;
|
||||||
|
dest.width = size.width;
|
||||||
|
dest.height = size.height;
|
||||||
|
}
|
||||||
|
|
||||||
// FillOpacityMask only works if the antialias mode is MODE_ALIASED
|
// FillOpacityMask only works if the antialias mode is MODE_ALIASED
|
||||||
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
|
mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
|
||||||
|
|
||||||
image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
|
|
||||||
if (!bitmap) {
|
|
||||||
gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IntSize size = IntSize::Truncate(bitmap->GetSize().width, bitmap->GetSize().height);
|
|
||||||
|
|
||||||
Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height));
|
Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height));
|
||||||
|
|
||||||
Rect dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height));
|
|
||||||
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha);
|
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha);
|
||||||
mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect));
|
mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect));
|
||||||
|
|
||||||
|
@ -1871,7 +1931,6 @@ DrawTargetD2D1::GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTrans
|
||||||
bool aUserSpace)
|
bool aUserSpace)
|
||||||
{
|
{
|
||||||
RefPtr<ID2D1Image> image;
|
RefPtr<ID2D1Image> image;
|
||||||
|
|
||||||
switch (aSurface->GetType()) {
|
switch (aSurface->GetType()) {
|
||||||
case SurfaceType::D2D1_1_IMAGE:
|
case SurfaceType::D2D1_1_IMAGE:
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,6 +36,8 @@ public:
|
||||||
virtual DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; }
|
virtual DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; }
|
||||||
virtual BackendType GetBackendType() const override { return BackendType::DIRECT2D1_1; }
|
virtual BackendType GetBackendType() const override { return BackendType::DIRECT2D1_1; }
|
||||||
virtual already_AddRefed<SourceSurface> Snapshot() override;
|
virtual already_AddRefed<SourceSurface> Snapshot() override;
|
||||||
|
virtual already_AddRefed<SourceSurface> IntoLuminanceSource(LuminanceType aLuminanceType,
|
||||||
|
float aOpacity) override;
|
||||||
virtual IntSize GetSize() override { return mSize; }
|
virtual IntSize GetSize() override { return mSize; }
|
||||||
|
|
||||||
virtual void Flush() override;
|
virtual void Flush() override;
|
||||||
|
@ -295,6 +297,10 @@ private:
|
||||||
static IDWriteFactory *mDWriteFactory;
|
static IDWriteFactory *mDWriteFactory;
|
||||||
// This value is uesed to verify if the DrawTarget is created by a stale device.
|
// This value is uesed to verify if the DrawTarget is created by a stale device.
|
||||||
uint32_t mDeviceSeq;
|
uint32_t mDeviceSeq;
|
||||||
|
|
||||||
|
// List of effects we use
|
||||||
|
void EnsureLuminanceEffect();
|
||||||
|
RefPtr<ID2D1Effect> mLuminanceEffect;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,6 +278,12 @@ enum class SamplingBounds : int8_t {
|
||||||
BOUNDED
|
BOUNDED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Moz2d version for SVG mask types
|
||||||
|
enum class LuminanceType : int8_t {
|
||||||
|
LUMINANCE,
|
||||||
|
LINEARRGB,
|
||||||
|
};
|
||||||
|
|
||||||
/* Color is stored in non-premultiplied form */
|
/* Color is stored in non-premultiplied form */
|
||||||
struct Color
|
struct Color
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
#include "nsRect.h"
|
#include "nsRect.h"
|
||||||
#include "nspr.h"
|
#include "nspr.h"
|
||||||
#include "png.h"
|
#include "png.h"
|
||||||
|
|
||||||
#include "RasterImage.h"
|
#include "RasterImage.h"
|
||||||
|
#include "SurfaceCache.h"
|
||||||
#include "SurfacePipeFactory.h"
|
#include "SurfacePipeFactory.h"
|
||||||
#include "mozilla/DebugOnly.h"
|
#include "mozilla/DebugOnly.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
|
@ -569,6 +571,13 @@ nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
|
||||||
|
|
||||||
// Post our size to the superclass
|
// Post our size to the superclass
|
||||||
decoder->PostSize(frameRect.width, frameRect.height);
|
decoder->PostSize(frameRect.width, frameRect.height);
|
||||||
|
|
||||||
|
if (width >
|
||||||
|
SurfaceCache::MaximumCapacity()/(bit_depth > 8 ? 16:8)) {
|
||||||
|
// libpng needs space to allocate two row buffers
|
||||||
|
png_error(decoder->mPNG, "Image is too wide");
|
||||||
|
}
|
||||||
|
|
||||||
if (decoder->HasError()) {
|
if (decoder->HasError()) {
|
||||||
// Setting the size led to an error.
|
// Setting the size led to an error.
|
||||||
png_error(decoder->mPNG, "Sizing error");
|
png_error(decoder->mPNG, "Sizing error");
|
||||||
|
@ -735,6 +744,11 @@ nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
|
||||||
if (interlace_type == PNG_INTERLACE_ADAM7) {
|
if (interlace_type == PNG_INTERLACE_ADAM7) {
|
||||||
if (frameRect.height < INT32_MAX / (frameRect.width * int32_t(channels))) {
|
if (frameRect.height < INT32_MAX / (frameRect.width * int32_t(channels))) {
|
||||||
const size_t bufferSize = channels * frameRect.width * frameRect.height;
|
const size_t bufferSize = channels * frameRect.width * frameRect.height;
|
||||||
|
|
||||||
|
if (bufferSize > SurfaceCache::MaximumCapacity()) {
|
||||||
|
png_error(decoder->mPNG, "Insufficient memory to deinterlace image");
|
||||||
|
}
|
||||||
|
|
||||||
decoder->interlacebuf = static_cast<uint8_t*>(malloc(bufferSize));
|
decoder->interlacebuf = static_cast<uint8_t*>(malloc(bufferSize));
|
||||||
}
|
}
|
||||||
if (!decoder->interlacebuf) {
|
if (!decoder->interlacebuf) {
|
||||||
|
|
|
@ -6,11 +6,13 @@
|
||||||
|
|
||||||
#define INITGUID
|
#define INITGUID
|
||||||
|
|
||||||
|
#include "mozilla/dom/ContentChild.h"
|
||||||
#include "mozilla/Move.h"
|
#include "mozilla/Move.h"
|
||||||
#include "mozilla/mscom/DispatchForwarder.h"
|
#include "mozilla/mscom/DispatchForwarder.h"
|
||||||
#include "mozilla/mscom/Interceptor.h"
|
#include "mozilla/mscom/Interceptor.h"
|
||||||
#include "mozilla/mscom/InterceptorLog.h"
|
#include "mozilla/mscom/InterceptorLog.h"
|
||||||
#include "mozilla/mscom/MainThreadInvoker.h"
|
#include "mozilla/mscom/MainThreadInvoker.h"
|
||||||
|
#include "mozilla/mscom/Objref.h"
|
||||||
#include "mozilla/mscom/Registration.h"
|
#include "mozilla/mscom/Registration.h"
|
||||||
#include "mozilla/mscom/Utils.h"
|
#include "mozilla/mscom/Utils.h"
|
||||||
#include "MainThreadUtils.h"
|
#include "MainThreadUtils.h"
|
||||||
|
@ -20,6 +22,7 @@
|
||||||
#include "nsDirectoryServiceUtils.h"
|
#include "nsDirectoryServiceUtils.h"
|
||||||
#include "nsRefPtrHashtable.h"
|
#include "nsRefPtrHashtable.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
|
#include "nsXULAppAPI.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace mscom {
|
namespace mscom {
|
||||||
|
@ -142,6 +145,7 @@ Interceptor::GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
|
||||||
aDestContext == MSHCTX_DIFFERENTMACHINE) {
|
aDestContext == MSHCTX_DIFFERENTMACHINE) {
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(mEventSink);
|
MOZ_ASSERT(mEventSink);
|
||||||
return mEventSink->GetHandler(WrapNotNull(aHandlerClsid));
|
return mEventSink->GetHandler(WrapNotNull(aHandlerClsid));
|
||||||
}
|
}
|
||||||
|
@ -177,12 +181,60 @@ Interceptor::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
|
||||||
DWORD dwDestContext, void* pvDestContext,
|
DWORD dwDestContext, void* pvDestContext,
|
||||||
DWORD mshlflags)
|
DWORD mshlflags)
|
||||||
{
|
{
|
||||||
HRESULT hr = mStdMarshal->MarshalInterface(pStm, riid, pv, dwDestContext,
|
HRESULT hr;
|
||||||
|
|
||||||
|
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
||||||
|
// Save the current stream position
|
||||||
|
LARGE_INTEGER seekTo;
|
||||||
|
seekTo.QuadPart = 0;
|
||||||
|
|
||||||
|
ULARGE_INTEGER objrefPos;
|
||||||
|
|
||||||
|
hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &objrefPos);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
||||||
|
|
||||||
|
hr = mStdMarshal->MarshalInterface(pStm, riid, pv, dwDestContext,
|
||||||
pvDestContext, mshlflags);
|
pvDestContext, mshlflags);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
||||||
|
if (XRE_IsContentProcess()) {
|
||||||
|
const DWORD chromeMainTid =
|
||||||
|
dom::ContentChild::GetSingleton()->GetChromeMainThreadId();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CoGetCallerTID() gives us the caller's thread ID when that thread resides
|
||||||
|
* in a single-threaded apartment. Since our chrome main thread does live
|
||||||
|
* inside an STA, we will therefore be able to check whether the caller TID
|
||||||
|
* equals our chrome main thread TID. This enables us to distinguish
|
||||||
|
* between our chrome thread vs other out-of-process callers.
|
||||||
|
*/
|
||||||
|
DWORD callerTid;
|
||||||
|
if (::CoGetCallerTID(&callerTid) == S_FALSE && callerTid != chromeMainTid) {
|
||||||
|
// The caller isn't our chrome process, so do not provide a handler.
|
||||||
|
// First, seek back to the stream position that we prevously saved.
|
||||||
|
seekTo.QuadPart = objrefPos.QuadPart;
|
||||||
|
hr = pStm->Seek(seekTo, STREAM_SEEK_SET, nullptr);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now strip out the handler.
|
||||||
|
if (!StripHandlerFromOBJREF(WrapNotNull(pStm))) {
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
||||||
|
|
||||||
return mEventSink->WriteHandlerPayload(WrapNotNull(pStm));
|
return mEventSink->WriteHandlerPayload(WrapNotNull(pStm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -453,6 +453,7 @@ MainThreadHandoff::GetHandler(NotNull<CLSID*> aHandlerClsid)
|
||||||
if (!mHandlerProvider) {
|
if (!mHandlerProvider) {
|
||||||
return E_NOTIMPL;
|
return E_NOTIMPL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mHandlerProvider->GetHandler(aHandlerClsid);
|
return mHandlerProvider->GetHandler(aHandlerClsid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,249 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "mozilla/mscom/Objref.h"
|
||||||
|
|
||||||
|
#include "mozilla/Assertions.h"
|
||||||
|
#include "mozilla/mscom/Utils.h"
|
||||||
|
#include "mozilla/UniquePtr.h"
|
||||||
|
|
||||||
|
#include <guiddef.h>
|
||||||
|
#include <objidl.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
typedef uint64_t OID;
|
||||||
|
typedef uint64_t OXID;
|
||||||
|
typedef GUID IPID;
|
||||||
|
|
||||||
|
struct STDOBJREF
|
||||||
|
{
|
||||||
|
uint32_t mFlags;
|
||||||
|
uint32_t mPublicRefs;
|
||||||
|
OXID mOxid;
|
||||||
|
OID mOid;
|
||||||
|
IPID mIpid;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum STDOBJREF_FLAGS
|
||||||
|
{
|
||||||
|
SORF_PING = 0,
|
||||||
|
SORF_NOPING = 0x1000
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DUALSTRINGARRAY
|
||||||
|
{
|
||||||
|
static size_t SizeFromNumEntries(const uint16_t aNumEntries)
|
||||||
|
{
|
||||||
|
return sizeof(mNumEntries) + sizeof(mSecurityOffset) +
|
||||||
|
aNumEntries * sizeof(uint16_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t SizeOf() const
|
||||||
|
{
|
||||||
|
return SizeFromNumEntries(mNumEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t mNumEntries;
|
||||||
|
uint16_t mSecurityOffset;
|
||||||
|
uint16_t mStringArray[1]; // Length is mNumEntries
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OBJREF_STANDARD
|
||||||
|
{
|
||||||
|
size_t SizeOf() const
|
||||||
|
{
|
||||||
|
return sizeof(mStd) + mResAddr.SizeOf();
|
||||||
|
}
|
||||||
|
|
||||||
|
STDOBJREF mStd;
|
||||||
|
DUALSTRINGARRAY mResAddr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OBJREF_HANDLER
|
||||||
|
{
|
||||||
|
size_t SizeOf() const
|
||||||
|
{
|
||||||
|
return sizeof(mStd) + sizeof(mClsid) + mResAddr.SizeOf();
|
||||||
|
}
|
||||||
|
|
||||||
|
STDOBJREF mStd;
|
||||||
|
CLSID mClsid;
|
||||||
|
DUALSTRINGARRAY mResAddr;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum OBJREF_FLAGS
|
||||||
|
{
|
||||||
|
OBJREF_TYPE_STANDARD = 0x00000001UL,
|
||||||
|
OBJREF_TYPE_HANDLER = 0x00000002UL,
|
||||||
|
OBJREF_TYPE_CUSTOM = 0x00000004UL,
|
||||||
|
OBJREF_TYPE_EXTENDED = 0x00000008UL,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OBJREF
|
||||||
|
{
|
||||||
|
size_t SizeOf() const
|
||||||
|
{
|
||||||
|
size_t size = sizeof(mSignature) + sizeof(mFlags) + sizeof(mIid);
|
||||||
|
|
||||||
|
switch (mFlags) {
|
||||||
|
case OBJREF_TYPE_STANDARD:
|
||||||
|
size += mObjRefStd.SizeOf();
|
||||||
|
break;
|
||||||
|
case OBJREF_TYPE_HANDLER:
|
||||||
|
size += mObjRefHandler.SizeOf();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_ASSERT_UNREACHABLE("Unsupported OBJREF type");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t mSignature;
|
||||||
|
uint32_t mFlags;
|
||||||
|
IID mIid;
|
||||||
|
union {
|
||||||
|
OBJREF_STANDARD mObjRefStd;
|
||||||
|
OBJREF_HANDLER mObjRefHandler;
|
||||||
|
// There are others but we're not supporting them here
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
enum OBJREF_SIGNATURES
|
||||||
|
{
|
||||||
|
OBJREF_SIGNATURE = 0x574F454DUL
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
struct ByteArrayDeleter
|
||||||
|
{
|
||||||
|
void operator()(void* aPtr)
|
||||||
|
{
|
||||||
|
delete[] reinterpret_cast<uint8_t*>(aPtr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using VarStructUniquePtr = mozilla::UniquePtr<T, ByteArrayDeleter>;
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace mscom {
|
||||||
|
|
||||||
|
bool
|
||||||
|
StripHandlerFromOBJREF(NotNull<IStream*> aStream)
|
||||||
|
{
|
||||||
|
// Get current stream position
|
||||||
|
LARGE_INTEGER seekTo;
|
||||||
|
seekTo.QuadPart = 0;
|
||||||
|
|
||||||
|
ULARGE_INTEGER objrefPos;
|
||||||
|
|
||||||
|
HRESULT hr = aStream->Seek(seekTo, STREAM_SEEK_CUR, &objrefPos);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG bytesRead;
|
||||||
|
|
||||||
|
uint32_t signature;
|
||||||
|
hr = aStream->Read(&signature, sizeof(signature), &bytesRead);
|
||||||
|
if (FAILED(hr) || bytesRead != sizeof(signature) ||
|
||||||
|
signature != OBJREF_SIGNATURE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t type;
|
||||||
|
hr = aStream->Read(&type, sizeof(type), &bytesRead);
|
||||||
|
if (FAILED(hr) || bytesRead != sizeof(type) ||
|
||||||
|
type != OBJREF_TYPE_HANDLER) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IID iid;
|
||||||
|
hr = aStream->Read(&iid, sizeof(iid), &bytesRead);
|
||||||
|
if (FAILED(hr) || bytesRead != sizeof(iid) || !IsValidGUID(iid)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek past fixed-size STDOBJREF and CLSID
|
||||||
|
seekTo.QuadPart = sizeof(STDOBJREF) + sizeof(CLSID);
|
||||||
|
hr = aStream->Seek(seekTo, STREAM_SEEK_CUR, nullptr);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t numEntries;
|
||||||
|
hr = aStream->Read(&numEntries, sizeof(numEntries), &bytesRead);
|
||||||
|
if (FAILED(hr) || bytesRead != sizeof(numEntries)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll try to use a stack buffer if resAddrSize <= kMinDualStringArraySize
|
||||||
|
const uint32_t kMinDualStringArraySize = 12;
|
||||||
|
uint16_t staticResAddrBuf[kMinDualStringArraySize / sizeof(uint16_t)];
|
||||||
|
|
||||||
|
size_t resAddrSize = DUALSTRINGARRAY::SizeFromNumEntries(numEntries);
|
||||||
|
|
||||||
|
DUALSTRINGARRAY* resAddr;
|
||||||
|
VarStructUniquePtr<DUALSTRINGARRAY> dynamicResAddrBuf;
|
||||||
|
|
||||||
|
if (resAddrSize <= kMinDualStringArraySize) {
|
||||||
|
resAddr = reinterpret_cast<DUALSTRINGARRAY*>(staticResAddrBuf);
|
||||||
|
} else {
|
||||||
|
dynamicResAddrBuf.reset(
|
||||||
|
reinterpret_cast<DUALSTRINGARRAY*>(new uint8_t[resAddrSize]));
|
||||||
|
resAddr = dynamicResAddrBuf.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
resAddr->mNumEntries = numEntries;
|
||||||
|
|
||||||
|
// Because we've already read numEntries
|
||||||
|
ULONG bytesToRead = resAddrSize - sizeof(numEntries);
|
||||||
|
|
||||||
|
hr = aStream->Read(&resAddr->mSecurityOffset, bytesToRead, &bytesRead);
|
||||||
|
if (FAILED(hr) || bytesRead != bytesToRead) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature doesn't change so we'll seek past that
|
||||||
|
seekTo.QuadPart = objrefPos.QuadPart + sizeof(signature);
|
||||||
|
hr = aStream->Seek(seekTo, STREAM_SEEK_SET, nullptr);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG bytesWritten;
|
||||||
|
|
||||||
|
uint32_t newType = OBJREF_TYPE_STANDARD;
|
||||||
|
hr = aStream->Write(&newType, sizeof(newType), &bytesWritten);
|
||||||
|
if (FAILED(hr) || bytesWritten != sizeof(newType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip past IID and STDOBJREF since those don't change
|
||||||
|
seekTo.QuadPart = sizeof(IID) + sizeof(STDOBJREF);
|
||||||
|
hr = aStream->Seek(seekTo, STREAM_SEEK_CUR, nullptr);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = aStream->Write(resAddr, resAddrSize, &bytesWritten);
|
||||||
|
if (FAILED(hr) || bytesWritten != resAddrSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mscom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_mscom_Objref_h
|
||||||
|
#define mozilla_mscom_Objref_h
|
||||||
|
|
||||||
|
#include "mozilla/NotNull.h"
|
||||||
|
|
||||||
|
struct IStream;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace mscom {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a buffer containing a serialized proxy to an interface with a handler,
|
||||||
|
* this function strips out the handler and converts it to a standard one.
|
||||||
|
* @param aStream IStream whose pointer is positioned at the beginning of the
|
||||||
|
* OBJREF to be stripped. There should be nothing else written
|
||||||
|
* to the stream past the current OBJREF.
|
||||||
|
* @return true if the handler was successfully stripped, otherwise false.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
StripHandlerFromOBJREF(NotNull<IStream*> aStream);
|
||||||
|
|
||||||
|
} // namespace mscom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_mscom_Objref_h
|
||||||
|
|
|
@ -4,10 +4,12 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#ifdef ACCESSIBILITY
|
#if defined(ACCESSIBILITY)
|
||||||
#include "mozilla/mscom/Registration.h"
|
#include "mozilla/mscom/Registration.h"
|
||||||
|
#if defined(MOZILLA_INTERNAL_API)
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "mozilla/mscom/Utils.h"
|
#include "mozilla/mscom/Utils.h"
|
||||||
#include "mozilla/RefPtr.h"
|
#include "mozilla/RefPtr.h"
|
||||||
|
@ -139,6 +141,8 @@ IsVtableIndexFromParentInterface(REFIID aInterface, unsigned long aVtableIndex)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(MOZILLA_INTERNAL_API)
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
|
IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
|
||||||
unsigned long aVtableIndexHint)
|
unsigned long aVtableIndexHint)
|
||||||
|
@ -223,6 +227,8 @@ IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // defined(MOZILLA_INTERNAL_API)
|
||||||
|
|
||||||
#endif // defined(ACCESSIBILITY)
|
#endif // defined(ACCESSIBILITY)
|
||||||
|
|
||||||
} // namespace mscom
|
} // namespace mscom
|
||||||
|
|
|
@ -11,9 +11,7 @@
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#endif // defined(MOZILLA_INTERNAL_API)
|
#endif // defined(MOZILLA_INTERNAL_API)
|
||||||
|
|
||||||
#if defined(ACCESSIBILITY)
|
|
||||||
#include <guiddef.h>
|
#include <guiddef.h>
|
||||||
#endif // defined(ACCESSIBILITY)
|
|
||||||
|
|
||||||
struct IUnknown;
|
struct IUnknown;
|
||||||
|
|
||||||
|
@ -31,8 +29,10 @@ void GUIDToString(REFGUID aGuid, nsAString& aOutString);
|
||||||
#if defined(ACCESSIBILITY)
|
#if defined(ACCESSIBILITY)
|
||||||
bool IsVtableIndexFromParentInterface(REFIID aInterface,
|
bool IsVtableIndexFromParentInterface(REFIID aInterface,
|
||||||
unsigned long aVtableIndex);
|
unsigned long aVtableIndex);
|
||||||
|
#if defined(MOZILLA_INTERNAL_API)
|
||||||
bool IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
|
bool IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
|
||||||
unsigned long aVtableIndexHint);
|
unsigned long aVtableIndexHint);
|
||||||
|
#endif // defined(MOZILLA_INTERNAL_API)
|
||||||
#endif // defined(ACCESSIBILITY)
|
#endif // defined(ACCESSIBILITY)
|
||||||
|
|
||||||
} // namespace mscom
|
} // namespace mscom
|
||||||
|
|
|
@ -12,6 +12,7 @@ EXPORTS.mozilla.mscom += [
|
||||||
'COMPtrHolder.h',
|
'COMPtrHolder.h',
|
||||||
'EnsureMTA.h',
|
'EnsureMTA.h',
|
||||||
'MainThreadRuntime.h',
|
'MainThreadRuntime.h',
|
||||||
|
'Objref.h',
|
||||||
'ProxyStream.h',
|
'ProxyStream.h',
|
||||||
'Ptr.h',
|
'Ptr.h',
|
||||||
'Utils.h',
|
'Utils.h',
|
||||||
|
@ -21,6 +22,7 @@ UNIFIED_SOURCES += [
|
||||||
'AgileReference.cpp',
|
'AgileReference.cpp',
|
||||||
'EnsureMTA.cpp',
|
'EnsureMTA.cpp',
|
||||||
'MainThreadRuntime.cpp',
|
'MainThreadRuntime.cpp',
|
||||||
|
'Objref.cpp',
|
||||||
'ProxyStream.cpp',
|
'ProxyStream.cpp',
|
||||||
'Utils.cpp',
|
'Utils.cpp',
|
||||||
]
|
]
|
||||||
|
@ -70,6 +72,8 @@ LOCAL_INCLUDES += [
|
||||||
'/xpcom/build',
|
'/xpcom/build',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
DEFINES['MOZ_MSCOM_REMARSHAL_NO_HANDLER'] = True
|
||||||
|
|
||||||
include('/ipc/chromium/chromium-config.mozbuild')
|
include('/ipc/chromium/chromium-config.mozbuild')
|
||||||
|
|
||||||
FINAL_LIBRARY = 'xul'
|
FINAL_LIBRARY = 'xul'
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
|
#include "mozilla/mscom/Objref.h"
|
||||||
#include "nsWindowsHelpers.h"
|
#include "nsWindowsHelpers.h"
|
||||||
|
|
||||||
#include <objbase.h>
|
#include <objbase.h>
|
||||||
|
@ -48,7 +49,7 @@ Handler::Handler(IUnknown* aOuter, HRESULT* aResult)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// mInnerMarshal is a weak ref
|
// mUnmarshal is a weak ref
|
||||||
mUnmarshal->Release();
|
mUnmarshal->Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,14 +170,28 @@ Handler::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
|
||||||
RefPtr<IUnknown> unkToMarshal;
|
RefPtr<IUnknown> unkToMarshal;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
REFIID marshalAs = MarshalAs(riid);
|
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
||||||
if (marshalAs == riid) {
|
LARGE_INTEGER seekTo;
|
||||||
unkToMarshal = static_cast<IUnknown*>(pv);
|
seekTo.QuadPart = 0;
|
||||||
} else {
|
|
||||||
hr = mInnerUnk->QueryInterface(marshalAs, getter_AddRefs(unkToMarshal));
|
ULARGE_INTEGER objrefPos;
|
||||||
|
|
||||||
|
// Save the current position as it points to the location where the OBJREF
|
||||||
|
// will be written.
|
||||||
|
hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &objrefPos);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When marshaling without a handler, we just use the riid as passed in.
|
||||||
|
REFIID marshalAs = riid;
|
||||||
|
#else
|
||||||
|
REFIID marshalAs = MarshalAs(riid);
|
||||||
|
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
||||||
|
|
||||||
|
hr = mInnerUnk->QueryInterface(marshalAs, getter_AddRefs(unkToMarshal));
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = mUnmarshal->MarshalInterface(pStm, marshalAs, unkToMarshal.get(),
|
hr = mUnmarshal->MarshalInterface(pStm, marshalAs, unkToMarshal.get(),
|
||||||
|
@ -185,6 +200,22 @@ Handler::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
||||||
|
// Now the OBJREF has been written, so seek back to its beginning (the
|
||||||
|
// position that we saved earlier).
|
||||||
|
seekTo.QuadPart = objrefPos.QuadPart;
|
||||||
|
hr = pStm->Seek(seekTo, STREAM_SEEK_SET, nullptr);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now strip out the handler.
|
||||||
|
if (!StripHandlerFromOBJREF(WrapNotNull(pStm))) {
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
#else
|
||||||
if (!HasPayload()) {
|
if (!HasPayload()) {
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
@ -192,6 +223,7 @@ Handler::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
|
||||||
// Unfortunately when COM re-marshals a proxy that prevouisly had a payload,
|
// Unfortunately when COM re-marshals a proxy that prevouisly had a payload,
|
||||||
// we must re-serialize it.
|
// we must re-serialize it.
|
||||||
return WriteHandlerPayload(pStm, marshalAs);
|
return WriteHandlerPayload(pStm, marshalAs);
|
||||||
|
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
|
|
|
@ -8,8 +8,10 @@ Library('mscom_oop')
|
||||||
|
|
||||||
SOURCES += [
|
SOURCES += [
|
||||||
'../ActivationContext.cpp',
|
'../ActivationContext.cpp',
|
||||||
|
'../Objref.cpp',
|
||||||
'../Registration.cpp',
|
'../Registration.cpp',
|
||||||
'../StructStream.cpp',
|
'../StructStream.cpp',
|
||||||
|
'../Utils.cpp',
|
||||||
]
|
]
|
||||||
|
|
||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
|
@ -26,6 +28,7 @@ OS_LIBS += [
|
||||||
LIBRARY_DEFINES['UNICODE'] = True
|
LIBRARY_DEFINES['UNICODE'] = True
|
||||||
LIBRARY_DEFINES['_UNICODE'] = True
|
LIBRARY_DEFINES['_UNICODE'] = True
|
||||||
LIBRARY_DEFINES['MOZ_NO_MOZALLOC'] = True
|
LIBRARY_DEFINES['MOZ_NO_MOZALLOC'] = True
|
||||||
|
LIBRARY_DEFINES['MOZ_MSCOM_REMARSHAL_NO_HANDLER'] = True
|
||||||
|
|
||||||
DISABLE_STL_WRAPPING = True
|
DISABLE_STL_WRAPPING = True
|
||||||
NO_EXPAND_LIBS = True
|
NO_EXPAND_LIBS = True
|
||||||
|
|
|
@ -341,6 +341,7 @@ class JSFunction : public js::NativeObject
|
||||||
MOZ_ASSERT(atom);
|
MOZ_ASSERT(atom);
|
||||||
MOZ_ASSERT(!hasGuessedAtom());
|
MOZ_ASSERT(!hasGuessedAtom());
|
||||||
MOZ_ASSERT(!isClassConstructor());
|
MOZ_ASSERT(!isClassConstructor());
|
||||||
|
MOZ_ASSERT(js::AtomIsMarked(zone(), atom));
|
||||||
atom_ = atom;
|
atom_ = atom;
|
||||||
flags_ |= HAS_COMPILE_TIME_NAME;
|
flags_ |= HAS_COMPILE_TIME_NAME;
|
||||||
}
|
}
|
||||||
|
@ -355,6 +356,7 @@ class JSFunction : public js::NativeObject
|
||||||
MOZ_ASSERT(atom);
|
MOZ_ASSERT(atom);
|
||||||
MOZ_ASSERT(!hasCompileTimeName());
|
MOZ_ASSERT(!hasCompileTimeName());
|
||||||
MOZ_ASSERT(!hasGuessedAtom());
|
MOZ_ASSERT(!hasGuessedAtom());
|
||||||
|
MOZ_ASSERT(js::AtomIsMarked(zone(), atom));
|
||||||
atom_ = atom;
|
atom_ = atom;
|
||||||
flags_ |= HAS_GUESSED_ATOM;
|
flags_ |= HAS_GUESSED_ATOM;
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,9 +266,10 @@ DeleteScopeData(ConcreteScopeData* data)
|
||||||
template <typename ConcreteScope, XDRMode mode>
|
template <typename ConcreteScope, XDRMode mode>
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
|
Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
|
||||||
MutableHandle<typename ConcreteScope::Data*> data)
|
MutableHandle<typename ConcreteScope::Data*> data, uint32_t* lengthOut)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!data);
|
MOZ_ASSERT(!data);
|
||||||
|
MOZ_ASSERT(!*lengthOut);
|
||||||
|
|
||||||
JSContext* cx = xdr->cx();
|
JSContext* cx = xdr->cx();
|
||||||
|
|
||||||
|
@ -281,11 +282,13 @@ Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
|
||||||
if (mode == XDR_ENCODE) {
|
if (mode == XDR_ENCODE) {
|
||||||
data.set(&scope->data());
|
data.set(&scope->data());
|
||||||
} else {
|
} else {
|
||||||
|
if (length) {
|
||||||
data.set(NewEmptyScopeData<ConcreteScope>(cx, length).release());
|
data.set(NewEmptyScopeData<ConcreteScope>(cx, length).release());
|
||||||
if (!data)
|
if (!data)
|
||||||
return false;
|
return false;
|
||||||
data->length = length;
|
data->length = length;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < length; i++) {
|
for (uint32_t i = 0; i < length; i++) {
|
||||||
if (!XDRBindingName(xdr, &data->names[i])) {
|
if (!XDRBindingName(xdr, &data->names[i])) {
|
||||||
|
@ -298,6 +301,7 @@ Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*lengthOut = length;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,7 +563,8 @@ LexicalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||||
JSContext* cx = xdr->cx();
|
JSContext* cx = xdr->cx();
|
||||||
|
|
||||||
Rooted<Data*> data(cx);
|
Rooted<Data*> data(cx);
|
||||||
if (!XDRSizedBindingNames<LexicalScope>(xdr, scope.as<LexicalScope>(), &data))
|
uint32_t length = 0;
|
||||||
|
if (!XDRSizedBindingNames<LexicalScope>(xdr, scope.as<LexicalScope>(), &data, &length))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -583,6 +588,9 @@ LexicalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mode == XDR_DECODE) {
|
if (mode == XDR_DECODE) {
|
||||||
|
if (!data)
|
||||||
|
return false;
|
||||||
|
|
||||||
scope.set(create(cx, kind, data, firstFrameSlot, enclosing));
|
scope.set(create(cx, kind, data, firstFrameSlot, enclosing));
|
||||||
if (!scope)
|
if (!scope)
|
||||||
return false;
|
return false;
|
||||||
|
@ -743,7 +751,8 @@ FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosin
|
||||||
{
|
{
|
||||||
JSContext* cx = xdr->cx();
|
JSContext* cx = xdr->cx();
|
||||||
Rooted<Data*> data(cx);
|
Rooted<Data*> data(cx);
|
||||||
if (!XDRSizedBindingNames<FunctionScope>(xdr, scope.as<FunctionScope>(), &data))
|
uint32_t length = 0;
|
||||||
|
if (!XDRSizedBindingNames<FunctionScope>(xdr, scope.as<FunctionScope>(), &data, &length))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -754,30 +763,35 @@ FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosin
|
||||||
|
|
||||||
uint8_t needsEnvironment;
|
uint8_t needsEnvironment;
|
||||||
uint8_t hasParameterExprs;
|
uint8_t hasParameterExprs;
|
||||||
|
uint16_t nonPositionalFormalStart;
|
||||||
|
uint16_t varStart;
|
||||||
uint32_t nextFrameSlot;
|
uint32_t nextFrameSlot;
|
||||||
if (mode == XDR_ENCODE) {
|
if (mode == XDR_ENCODE) {
|
||||||
needsEnvironment = scope->hasEnvironment();
|
needsEnvironment = scope->hasEnvironment();
|
||||||
hasParameterExprs = data->hasParameterExprs;
|
hasParameterExprs = data->hasParameterExprs;
|
||||||
|
nonPositionalFormalStart = data->nonPositionalFormalStart;
|
||||||
|
varStart = data->varStart;
|
||||||
nextFrameSlot = data->nextFrameSlot;
|
nextFrameSlot = data->nextFrameSlot;
|
||||||
}
|
}
|
||||||
if (!xdr->codeUint8(&needsEnvironment))
|
if (!xdr->codeUint8(&needsEnvironment))
|
||||||
return false;
|
return false;
|
||||||
if (!xdr->codeUint8(&hasParameterExprs))
|
if (!xdr->codeUint8(&hasParameterExprs))
|
||||||
return false;
|
return false;
|
||||||
if (!xdr->codeUint16(&data->nonPositionalFormalStart))
|
if (!xdr->codeUint16(&nonPositionalFormalStart))
|
||||||
return false;
|
return false;
|
||||||
if (!xdr->codeUint16(&data->varStart))
|
if (!xdr->codeUint16(&varStart))
|
||||||
return false;
|
return false;
|
||||||
if (!xdr->codeUint32(&nextFrameSlot))
|
if (!xdr->codeUint32(&nextFrameSlot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mode == XDR_DECODE) {
|
if (mode == XDR_DECODE) {
|
||||||
if (!data->length) {
|
MOZ_ASSERT(!length == !data);
|
||||||
MOZ_ASSERT(!data->nonPositionalFormalStart);
|
if (length) {
|
||||||
MOZ_ASSERT(!data->varStart);
|
data->nonPositionalFormalStart = nonPositionalFormalStart;
|
||||||
MOZ_ASSERT(!data->nextFrameSlot);
|
data->varStart = varStart;
|
||||||
DeleteScopeData(data.get());
|
} else {
|
||||||
data = nullptr;
|
MOZ_ASSERT(!nonPositionalFormalStart);
|
||||||
|
MOZ_ASSERT(!varStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.set(create(cx, data, hasParameterExprs, needsEnvironment, fun, enclosing));
|
scope.set(create(cx, data, hasParameterExprs, needsEnvironment, fun, enclosing));
|
||||||
|
@ -786,6 +800,7 @@ FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosin
|
||||||
|
|
||||||
// nextFrameSlot is used only for this correctness check.
|
// nextFrameSlot is used only for this correctness check.
|
||||||
MOZ_ASSERT(nextFrameSlot == scope->as<FunctionScope>().data().nextFrameSlot);
|
MOZ_ASSERT(nextFrameSlot == scope->as<FunctionScope>().data().nextFrameSlot);
|
||||||
|
MOZ_ASSERT_IF(!data, !nextFrameSlot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -871,7 +886,8 @@ VarScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||||
{
|
{
|
||||||
JSContext* cx = xdr->cx();
|
JSContext* cx = xdr->cx();
|
||||||
Rooted<Data*> data(cx);
|
Rooted<Data*> data(cx);
|
||||||
if (!XDRSizedBindingNames<VarScope>(xdr, scope.as<VarScope>(), &data))
|
uint32_t length = 0;
|
||||||
|
if (!XDRSizedBindingNames<VarScope>(xdr, scope.as<VarScope>(), &data, &length))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -896,12 +912,7 @@ VarScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mode == XDR_DECODE) {
|
if (mode == XDR_DECODE) {
|
||||||
if (!data->length) {
|
MOZ_ASSERT(!length == !data);
|
||||||
MOZ_ASSERT(!data->nextFrameSlot);
|
|
||||||
DeleteScopeData(data.get());
|
|
||||||
data = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.set(create(cx, kind, data, firstFrameSlot, needsEnvironment, enclosing));
|
scope.set(create(cx, kind, data, firstFrameSlot, needsEnvironment, enclosing));
|
||||||
if (!scope)
|
if (!scope)
|
||||||
return false;
|
return false;
|
||||||
|
@ -974,7 +985,8 @@ GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope)
|
||||||
|
|
||||||
JSContext* cx = xdr->cx();
|
JSContext* cx = xdr->cx();
|
||||||
Rooted<Data*> data(cx);
|
Rooted<Data*> data(cx);
|
||||||
if (!XDRSizedBindingNames<GlobalScope>(xdr, scope.as<GlobalScope>(), &data))
|
uint32_t length = 0;
|
||||||
|
if (!XDRSizedBindingNames<GlobalScope>(xdr, scope.as<GlobalScope>(), &data, &length))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -983,20 +995,31 @@ GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope)
|
||||||
DeleteScopeData(data.get());
|
DeleteScopeData(data.get());
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!xdr->codeUint32(&data->varStart))
|
uint32_t varStart;
|
||||||
|
uint32_t letStart;
|
||||||
|
uint32_t constStart;
|
||||||
|
if (mode == XDR_ENCODE) {
|
||||||
|
varStart = data->varStart;
|
||||||
|
letStart = data->letStart;
|
||||||
|
constStart = data->constStart;
|
||||||
|
}
|
||||||
|
if (!xdr->codeUint32(&varStart))
|
||||||
return false;
|
return false;
|
||||||
if (!xdr->codeUint32(&data->letStart))
|
if (!xdr->codeUint32(&letStart))
|
||||||
return false;
|
return false;
|
||||||
if (!xdr->codeUint32(&data->constStart))
|
if (!xdr->codeUint32(&constStart))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mode == XDR_DECODE) {
|
if (mode == XDR_DECODE) {
|
||||||
if (!data->length) {
|
MOZ_ASSERT(!length == !data);
|
||||||
MOZ_ASSERT(!data->varStart);
|
if (length) {
|
||||||
MOZ_ASSERT(!data->letStart);
|
data->varStart = varStart;
|
||||||
MOZ_ASSERT(!data->constStart);
|
data->letStart = letStart;
|
||||||
DeleteScopeData(data.get());
|
data->constStart = constStart;
|
||||||
data = nullptr;
|
} else {
|
||||||
|
MOZ_ASSERT(!varStart);
|
||||||
|
MOZ_ASSERT(!letStart);
|
||||||
|
MOZ_ASSERT(!constStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.set(create(cx, kind, data));
|
scope.set(create(cx, kind, data));
|
||||||
|
@ -1106,16 +1129,12 @@ EvalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
||||||
DeleteScopeData(data.get());
|
DeleteScopeData(data.get());
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!XDRSizedBindingNames<EvalScope>(xdr, scope.as<EvalScope>(), &data))
|
uint32_t length = 0;
|
||||||
|
if (!XDRSizedBindingNames<EvalScope>(xdr, scope.as<EvalScope>(), &data, &length))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mode == XDR_DECODE) {
|
if (mode == XDR_DECODE) {
|
||||||
if (!data->length) {
|
MOZ_ASSERT(!length == !data);
|
||||||
MOZ_ASSERT(!data->nextFrameSlot);
|
|
||||||
DeleteScopeData(data.get());
|
|
||||||
data = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.set(create(cx, kind, data, enclosing));
|
scope.set(create(cx, kind, data, enclosing));
|
||||||
if (!scope)
|
if (!scope)
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -248,7 +248,8 @@ class Scope : public js::gc::TenuredCell
|
||||||
|
|
||||||
template <typename ConcreteScope, XDRMode mode>
|
template <typename ConcreteScope, XDRMode mode>
|
||||||
static bool XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
|
static bool XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
|
||||||
MutableHandle<typename ConcreteScope::Data*> data);
|
MutableHandle<typename ConcreteScope::Data*> data,
|
||||||
|
uint32_t* lengthOut);
|
||||||
|
|
||||||
Shape* maybeCloneEnvironmentShape(JSContext* cx);
|
Shape* maybeCloneEnvironmentShape(JSContext* cx);
|
||||||
|
|
||||||
|
|
|
@ -1697,6 +1697,46 @@ nsRefreshDriver::RunFrameRequestCallbacks(TimeStamp aNowTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RunnableWithDelay
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIRunnable> mRunnable;
|
||||||
|
uint32_t mDelay;
|
||||||
|
};
|
||||||
|
|
||||||
|
static AutoTArray<RunnableWithDelay, 8>* sPendingIdleRunnables = nullptr;
|
||||||
|
|
||||||
|
void
|
||||||
|
nsRefreshDriver::DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
|
||||||
|
uint32_t aDelay)
|
||||||
|
{
|
||||||
|
if (!sPendingIdleRunnables) {
|
||||||
|
sPendingIdleRunnables = new AutoTArray<RunnableWithDelay, 8>();
|
||||||
|
}
|
||||||
|
|
||||||
|
RunnableWithDelay rwd = {aRunnable, aDelay};
|
||||||
|
sPendingIdleRunnables->AppendElement(rwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsRefreshDriver::CancelIdleRunnable(nsIRunnable* aRunnable)
|
||||||
|
{
|
||||||
|
if (!sPendingIdleRunnables) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < sPendingIdleRunnables->Length(); ++i) {
|
||||||
|
if ((*sPendingIdleRunnables)[i].mRunnable == aRunnable) {
|
||||||
|
sPendingIdleRunnables->RemoveElementAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sPendingIdleRunnables->IsEmpty()) {
|
||||||
|
delete sPendingIdleRunnables;
|
||||||
|
sPendingIdleRunnables = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
||||||
{
|
{
|
||||||
|
@ -1959,7 +1999,7 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notifyGC = false;
|
bool dispatchRunnablesAfterTick = false;
|
||||||
if (mViewManagerFlushIsPending) {
|
if (mViewManagerFlushIsPending) {
|
||||||
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
|
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
|
||||||
|
|
||||||
|
@ -1998,7 +2038,7 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
||||||
timelines->AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::END);
|
timelines->AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::END);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyGC = true;
|
dispatchRunnablesAfterTick = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef ANDROID /* bug 1142079 */
|
#ifndef ANDROID /* bug 1142079 */
|
||||||
|
@ -2017,10 +2057,14 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
||||||
ScheduleViewManagerFlush();
|
ScheduleViewManagerFlush();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notifyGC && nsContentUtils::XPConnect()) {
|
if (dispatchRunnablesAfterTick && sPendingIdleRunnables) {
|
||||||
GeckoProfilerTracingRAII tracer("Paint", "NotifyDidPaint");
|
AutoTArray<RunnableWithDelay, 8>* runnables = sPendingIdleRunnables;
|
||||||
nsContentUtils::XPConnect()->NotifyDidPaint();
|
sPendingIdleRunnables = nullptr;
|
||||||
nsJSContext::NotifyDidPaint();
|
for (uint32_t i = 0; i < runnables->Length(); ++i) {
|
||||||
|
NS_IdleDispatchToCurrentThread((*runnables)[i].mRunnable.forget(),
|
||||||
|
(*runnables)[i].mDelay);
|
||||||
|
}
|
||||||
|
delete runnables;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ class nsIDocument;
|
||||||
class imgIRequest;
|
class imgIRequest;
|
||||||
class nsIDOMEvent;
|
class nsIDOMEvent;
|
||||||
class nsINode;
|
class nsINode;
|
||||||
|
class nsIRunnable;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
class RefreshDriverTimer;
|
class RefreshDriverTimer;
|
||||||
|
@ -333,6 +334,10 @@ public:
|
||||||
*/
|
*/
|
||||||
static mozilla::TimeStamp GetIdleDeadlineHint(mozilla::TimeStamp aDefault);
|
static mozilla::TimeStamp GetIdleDeadlineHint(mozilla::TimeStamp aDefault);
|
||||||
|
|
||||||
|
static void DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
|
||||||
|
uint32_t aDelay);
|
||||||
|
static void CancelIdleRunnable(nsIRunnable* aRunnable);
|
||||||
|
|
||||||
bool SkippedPaints() const
|
bool SkippedPaints() const
|
||||||
{
|
{
|
||||||
return mSkippedPaints;
|
return mSkippedPaints;
|
||||||
|
|
|
@ -14,7 +14,7 @@ fuzzy-if(skiaContent,1,30000) == mask-mode-a.html mask-mode-ref.html
|
||||||
fuzzy-if(skiaContent,1,30000) == mask-mode-b.html mask-mode-ref.html
|
fuzzy-if(skiaContent,1,30000) == mask-mode-b.html mask-mode-ref.html
|
||||||
fuzzy-if(skiaContent,1,30000) == mask-mode-c.html mask-mode-ref.html
|
fuzzy-if(skiaContent,1,30000) == mask-mode-c.html mask-mode-ref.html
|
||||||
fuzzy-if(skiaContent,1,30000) == mask-mode-d.html mask-mode-ref.html
|
fuzzy-if(skiaContent,1,30000) == mask-mode-d.html mask-mode-ref.html
|
||||||
fuzzy-if(skiaContent,1,30000) == mask-mode-to-mask-type.html mask-mode-to-mask-type-ref.html
|
fuzzy-if(skiaContent,1,30000) fuzzy-if(d2d,1,30000) == mask-mode-to-mask-type.html mask-mode-to-mask-type-ref.html
|
||||||
|
|
||||||
# mask-image test cases
|
# mask-image test cases
|
||||||
== mask-image-1a.html mask-image-1-ref.html
|
== mask-image-1a.html mask-image-1-ref.html
|
||||||
|
@ -27,8 +27,8 @@ fuzzy-if(skiaContent||winWidget,1,20000) == mask-image-2.html mask-image-2-ref.h
|
||||||
fuzzy-if(skiaContent||winWidget,1,43) == mask-image-3c.html mask-image-3-ref.html
|
fuzzy-if(skiaContent||winWidget,1,43) == mask-image-3c.html mask-image-3-ref.html
|
||||||
fuzzy-if(skiaContent||winWidget,1,43) == mask-image-3d.html mask-image-3-ref.html
|
fuzzy-if(skiaContent||winWidget,1,43) == mask-image-3d.html mask-image-3-ref.html
|
||||||
== mask-image-3e.html mask-image-3-ref.html
|
== mask-image-3e.html mask-image-3-ref.html
|
||||||
fuzzy-if(skiaContent||winWidget,50,85) fuzzy-if(webrender,1,126) == mask-image-3f.html mask-image-3-ref.html
|
fuzzy-if(skiaContent||winWidget,50,85) fuzzy-if(webrender,1,126) fuzzy-if(d2d,255,1) == mask-image-3f.html mask-image-3-ref.html
|
||||||
fuzzy-if(skiaContent||winWidget,50,85) fuzzy-if(webrender,1,126) == mask-image-3g.html mask-image-3-ref.html
|
fuzzy-if(skiaContent||winWidget,50,85) fuzzy-if(webrender,1,126) fuzzy-if(d2d,255,1) == mask-image-3g.html mask-image-3-ref.html
|
||||||
pref(layout.css.clip-path-shapes.enabled,true) fuzzy-if(winWidget,1,3) fuzzy-if(skiaContent,2,12) == mask-image-3h.html mask-image-3-ref.html
|
pref(layout.css.clip-path-shapes.enabled,true) fuzzy-if(winWidget,1,3) fuzzy-if(skiaContent,2,12) == mask-image-3h.html mask-image-3-ref.html
|
||||||
fuzzy-if(skiaContent,71,203) == mask-image-3i.html mask-image-3-ref.html
|
fuzzy-if(skiaContent,71,203) == mask-image-3i.html mask-image-3-ref.html
|
||||||
== mask-image-4a.html blank.html
|
== mask-image-4a.html blank.html
|
||||||
|
|
|
@ -668,12 +668,12 @@ SVGGeometryFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
|
||||||
"Number of Marker frames should be equal to eTypeCount");
|
"Number of Marker frames should be equal to eTypeCount");
|
||||||
|
|
||||||
for (uint32_t i = 0; i < num; i++) {
|
for (uint32_t i = 0; i < num; i++) {
|
||||||
nsSVGMark& mark = marks[i];
|
const nsSVGMark& mark = marks[i];
|
||||||
nsSVGMarkerFrame* frame = markerFrames[mark.type];
|
nsSVGMarkerFrame* frame = markerFrames[mark.type];
|
||||||
if (frame) {
|
if (frame) {
|
||||||
SVGBBox mbbox =
|
SVGBBox mbbox =
|
||||||
frame->GetMarkBBoxContribution(aToBBoxUserspace, aFlags, this,
|
frame->GetMarkBBoxContribution(aToBBoxUserspace, aFlags, this,
|
||||||
&marks[i], strokeWidth);
|
mark, strokeWidth);
|
||||||
MOZ_ASSERT(mbbox.IsFinite(), "bbox is about to go bad");
|
MOZ_ASSERT(mbbox.IsFinite(), "bbox is about to go bad");
|
||||||
bbox.UnionEdges(mbbox);
|
bbox.UnionEdges(mbbox);
|
||||||
}
|
}
|
||||||
|
@ -896,10 +896,10 @@ SVGGeometryFrame::PaintMarkers(gfxContext& aContext,
|
||||||
"Number of Marker frames should be equal to eTypeCount");
|
"Number of Marker frames should be equal to eTypeCount");
|
||||||
|
|
||||||
for (uint32_t i = 0; i < num; i++) {
|
for (uint32_t i = 0; i < num; i++) {
|
||||||
nsSVGMark& mark = marks[i];
|
const nsSVGMark& mark = marks[i];
|
||||||
nsSVGMarkerFrame* frame = markerFrames[mark.type];
|
nsSVGMarkerFrame* frame = markerFrames[mark.type];
|
||||||
if (frame) {
|
if (frame) {
|
||||||
frame->PaintMark(aContext, aTransform, this, &mark, strokeWidth,
|
frame->PaintMark(aContext, aTransform, this, mark, strokeWidth,
|
||||||
aImgParams);
|
aImgParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,10 +66,6 @@ UNIFIED_SOURCES += [
|
||||||
'SVGViewFrame.cpp',
|
'SVGViewFrame.cpp',
|
||||||
]
|
]
|
||||||
|
|
||||||
if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']:
|
|
||||||
SOURCES += ['nsSVGMaskFrameNEON.cpp']
|
|
||||||
SOURCES['nsSVGMaskFrameNEON.cpp'].flags += CONFIG['NEON_FLAGS']
|
|
||||||
|
|
||||||
FINAL_LIBRARY = 'xul'
|
FINAL_LIBRARY = 'xul'
|
||||||
LOCAL_INCLUDES += [
|
LOCAL_INCLUDES += [
|
||||||
'../../widget',
|
'../../widget',
|
||||||
|
|
|
@ -80,11 +80,9 @@ nsSVGMarkerFrame::GetCanvasTM()
|
||||||
gfxMatrix markedTM = mMarkedFrame->GetCanvasTM();
|
gfxMatrix markedTM = mMarkedFrame->GetCanvasTM();
|
||||||
mInUse2 = false;
|
mInUse2 = false;
|
||||||
|
|
||||||
Matrix markerTM = content->GetMarkerTransform(mStrokeWidth, mX, mY,
|
|
||||||
mAutoAngle, mIsStart);
|
|
||||||
Matrix viewBoxTM = content->GetViewBoxTransform();
|
Matrix viewBoxTM = content->GetViewBoxTransform();
|
||||||
|
|
||||||
return ThebesMatrix(viewBoxTM * markerTM) * markedTM;
|
return ThebesMatrix(viewBoxTM * mMarkerTM) * markedTM;
|
||||||
}
|
}
|
||||||
|
|
||||||
static nsIFrame*
|
static nsIFrame*
|
||||||
|
@ -99,8 +97,8 @@ GetAnonymousChildFrame(nsIFrame* aFrame)
|
||||||
void
|
void
|
||||||
nsSVGMarkerFrame::PaintMark(gfxContext& aContext,
|
nsSVGMarkerFrame::PaintMark(gfxContext& aContext,
|
||||||
const gfxMatrix& aToMarkedFrameUserSpace,
|
const gfxMatrix& aToMarkedFrameUserSpace,
|
||||||
SVGGeometryFrame *aMarkedFrame,
|
SVGGeometryFrame* aMarkedFrame,
|
||||||
nsSVGMark *aMark, float aStrokeWidth,
|
const nsSVGMark& aMark, float aStrokeWidth,
|
||||||
imgDrawingParams& aImgParams)
|
imgDrawingParams& aImgParams)
|
||||||
{
|
{
|
||||||
// If the flag is set when we get here, it means this marker frame
|
// If the flag is set when we get here, it means this marker frame
|
||||||
|
@ -124,18 +122,11 @@ nsSVGMarkerFrame::PaintMark(gfxContext& aContext,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mStrokeWidth = aStrokeWidth;
|
|
||||||
mX = aMark->x;
|
|
||||||
mY = aMark->y;
|
|
||||||
mAutoAngle = aMark->angle;
|
|
||||||
mIsStart = aMark->type == nsSVGMark::eStart;
|
|
||||||
|
|
||||||
Matrix viewBoxTM = marker->GetViewBoxTransform();
|
Matrix viewBoxTM = marker->GetViewBoxTransform();
|
||||||
|
|
||||||
Matrix markerTM = marker->GetMarkerTransform(mStrokeWidth, mX, mY,
|
mMarkerTM = marker->GetMarkerTransform(aStrokeWidth, aMark);
|
||||||
mAutoAngle, mIsStart);
|
|
||||||
|
|
||||||
gfxMatrix markTM = ThebesMatrix(viewBoxTM) * ThebesMatrix(markerTM) *
|
gfxMatrix markTM = ThebesMatrix(viewBoxTM) * ThebesMatrix(mMarkerTM) *
|
||||||
aToMarkedFrameUserSpace;
|
aToMarkedFrameUserSpace;
|
||||||
|
|
||||||
if (StyleDisplay()->IsScrollableOverflow()) {
|
if (StyleDisplay()->IsScrollableOverflow()) {
|
||||||
|
@ -158,10 +149,10 @@ nsSVGMarkerFrame::PaintMark(gfxContext& aContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
SVGBBox
|
SVGBBox
|
||||||
nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix &aToBBoxUserspace,
|
nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix& aToBBoxUserspace,
|
||||||
uint32_t aFlags,
|
uint32_t aFlags,
|
||||||
SVGGeometryFrame *aMarkedFrame,
|
SVGGeometryFrame* aMarkedFrame,
|
||||||
const nsSVGMark *aMark,
|
const nsSVGMark& aMark,
|
||||||
float aStrokeWidth)
|
float aStrokeWidth)
|
||||||
{
|
{
|
||||||
SVGBBox bbox;
|
SVGBBox bbox;
|
||||||
|
@ -185,17 +176,10 @@ nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix &aToBBoxUserspace,
|
||||||
return bbox;
|
return bbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
mStrokeWidth = aStrokeWidth;
|
mMarkerTM = content->GetMarkerTransform(aStrokeWidth, aMark);
|
||||||
mX = aMark->x;
|
|
||||||
mY = aMark->y;
|
|
||||||
mAutoAngle = aMark->angle;
|
|
||||||
mIsStart = aMark->type == nsSVGMark::eStart;
|
|
||||||
|
|
||||||
Matrix markerTM =
|
|
||||||
content->GetMarkerTransform(mStrokeWidth, mX, mY, mAutoAngle, mIsStart);
|
|
||||||
Matrix viewBoxTM = content->GetViewBoxTransform();
|
Matrix viewBoxTM = content->GetViewBoxTransform();
|
||||||
|
|
||||||
Matrix tm = viewBoxTM * markerTM * aToBBoxUserspace;
|
Matrix tm = viewBoxTM * mMarkerTM * aToBBoxUserspace;
|
||||||
|
|
||||||
nsSVGDisplayableFrame* child = do_QueryFrame(GetAnonymousChildFrame(this));
|
nsSVGDisplayableFrame* child = do_QueryFrame(GetAnonymousChildFrame(this));
|
||||||
// When we're being called to obtain the invalidation area, we need to
|
// When we're being called to obtain the invalidation area, we need to
|
||||||
|
|
|
@ -79,15 +79,15 @@ public:
|
||||||
// nsSVGMarkerFrame methods:
|
// nsSVGMarkerFrame methods:
|
||||||
void PaintMark(gfxContext& aContext,
|
void PaintMark(gfxContext& aContext,
|
||||||
const gfxMatrix& aToMarkedFrameUserSpace,
|
const gfxMatrix& aToMarkedFrameUserSpace,
|
||||||
mozilla::SVGGeometryFrame *aMarkedFrame,
|
mozilla::SVGGeometryFrame* aMarkedFrame,
|
||||||
nsSVGMark *aMark,
|
const nsSVGMark& aMark,
|
||||||
float aStrokeWidth,
|
float aStrokeWidth,
|
||||||
imgDrawingParams& aImgParams);
|
imgDrawingParams& aImgParams);
|
||||||
|
|
||||||
SVGBBox GetMarkBBoxContribution(const Matrix &aToBBoxUserspace,
|
SVGBBox GetMarkBBoxContribution(const Matrix& aToBBoxUserspace,
|
||||||
uint32_t aFlags,
|
uint32_t aFlags,
|
||||||
mozilla::SVGGeometryFrame *aMarkedFrame,
|
mozilla::SVGGeometryFrame* aMarkedFrame,
|
||||||
const nsSVGMark *aMark,
|
const nsSVGMark& aMark,
|
||||||
float aStrokeWidth);
|
float aStrokeWidth);
|
||||||
|
|
||||||
// Update the style on our anonymous box child.
|
// Update the style on our anonymous box child.
|
||||||
|
@ -98,8 +98,7 @@ public:
|
||||||
private:
|
private:
|
||||||
// stuff needed for callback
|
// stuff needed for callback
|
||||||
mozilla::SVGGeometryFrame *mMarkedFrame;
|
mozilla::SVGGeometryFrame *mMarkedFrame;
|
||||||
float mStrokeWidth, mX, mY, mAutoAngle;
|
Matrix mMarkerTM;
|
||||||
bool mIsStart; // whether the callback is for a marker-start marker
|
|
||||||
|
|
||||||
// nsSVGContainerFrame methods:
|
// nsSVGContainerFrame methods:
|
||||||
virtual gfxMatrix GetCanvasTM() override;
|
virtual gfxMatrix GetCanvasTM() override;
|
||||||
|
|
|
@ -14,162 +14,28 @@
|
||||||
#include "mozilla/RefPtr.h"
|
#include "mozilla/RefPtr.h"
|
||||||
#include "nsSVGEffects.h"
|
#include "nsSVGEffects.h"
|
||||||
#include "mozilla/dom/SVGMaskElement.h"
|
#include "mozilla/dom/SVGMaskElement.h"
|
||||||
#ifdef BUILD_ARM_NEON
|
|
||||||
#include "mozilla/arm.h"
|
|
||||||
#include "nsSVGMaskFrameNEON.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::dom;
|
using namespace mozilla::dom;
|
||||||
using namespace mozilla::gfx;
|
using namespace mozilla::gfx;
|
||||||
using namespace mozilla::image;
|
using namespace mozilla::image;
|
||||||
|
|
||||||
// c = n / 255
|
static LuminanceType
|
||||||
// c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)) * 255 + 0.5
|
GetLuminanceType(uint8_t aNSMaskType)
|
||||||
static const uint8_t gsRGBToLinearRGBMap[256] = {
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 1,
|
|
||||||
1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
1, 1, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 3, 3, 3, 3, 3, 3,
|
|
||||||
4, 4, 4, 4, 4, 5, 5, 5,
|
|
||||||
5, 6, 6, 6, 6, 7, 7, 7,
|
|
||||||
8, 8, 8, 8, 9, 9, 9, 10,
|
|
||||||
10, 10, 11, 11, 12, 12, 12, 13,
|
|
||||||
13, 13, 14, 14, 15, 15, 16, 16,
|
|
||||||
17, 17, 17, 18, 18, 19, 19, 20,
|
|
||||||
20, 21, 22, 22, 23, 23, 24, 24,
|
|
||||||
25, 25, 26, 27, 27, 28, 29, 29,
|
|
||||||
30, 30, 31, 32, 32, 33, 34, 35,
|
|
||||||
35, 36, 37, 37, 38, 39, 40, 41,
|
|
||||||
41, 42, 43, 44, 45, 45, 46, 47,
|
|
||||||
48, 49, 50, 51, 51, 52, 53, 54,
|
|
||||||
55, 56, 57, 58, 59, 60, 61, 62,
|
|
||||||
63, 64, 65, 66, 67, 68, 69, 70,
|
|
||||||
71, 72, 73, 74, 76, 77, 78, 79,
|
|
||||||
80, 81, 82, 84, 85, 86, 87, 88,
|
|
||||||
90, 91, 92, 93, 95, 96, 97, 99,
|
|
||||||
100, 101, 103, 104, 105, 107, 108, 109,
|
|
||||||
111, 112, 114, 115, 116, 118, 119, 121,
|
|
||||||
122, 124, 125, 127, 128, 130, 131, 133,
|
|
||||||
134, 136, 138, 139, 141, 142, 144, 146,
|
|
||||||
147, 149, 151, 152, 154, 156, 157, 159,
|
|
||||||
161, 163, 164, 166, 168, 170, 171, 173,
|
|
||||||
175, 177, 179, 181, 183, 184, 186, 188,
|
|
||||||
190, 192, 194, 196, 198, 200, 202, 204,
|
|
||||||
206, 208, 210, 212, 214, 216, 218, 220,
|
|
||||||
222, 224, 226, 229, 231, 233, 235, 237,
|
|
||||||
239, 242, 244, 246, 248, 250, 253, 255
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
ComputesRGBLuminanceMask(const uint8_t *aSourceData,
|
|
||||||
int32_t aSourceStride,
|
|
||||||
uint8_t *aDestData,
|
|
||||||
int32_t aDestStride,
|
|
||||||
const IntSize &aSize,
|
|
||||||
float aOpacity)
|
|
||||||
{
|
{
|
||||||
#ifdef BUILD_ARM_NEON
|
switch (aNSMaskType) {
|
||||||
if (mozilla::supports_neon()) {
|
case NS_STYLE_MASK_TYPE_LUMINANCE:
|
||||||
ComputesRGBLuminanceMask_NEON(aSourceData, aSourceStride,
|
return LuminanceType::LUMINANCE;
|
||||||
aDestData, aDestStride,
|
case NS_STYLE_COLOR_INTERPOLATION_LINEARRGB:
|
||||||
aSize, aOpacity);
|
return LuminanceType::LINEARRGB;
|
||||||
return;
|
default:
|
||||||
|
{
|
||||||
|
NS_WARNING("Unknown SVG mask type, defaulting to luminance");
|
||||||
|
return LuminanceType::LUMINANCE;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity
|
|
||||||
int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity
|
|
||||||
int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721
|
|
||||||
int32_t sourceOffset = aSourceStride - 4 * aSize.width;
|
|
||||||
const uint8_t *sourcePixel = aSourceData;
|
|
||||||
int32_t destOffset = aDestStride - aSize.width;
|
|
||||||
uint8_t *destPixel = aDestData;
|
|
||||||
|
|
||||||
for (int32_t y = 0; y < aSize.height; y++) {
|
|
||||||
for (int32_t x = 0; x < aSize.width; x++) {
|
|
||||||
uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A];
|
|
||||||
|
|
||||||
if (a) {
|
|
||||||
*destPixel = (redFactor * sourcePixel[GFX_ARGB32_OFFSET_R] +
|
|
||||||
greenFactor * sourcePixel[GFX_ARGB32_OFFSET_G] +
|
|
||||||
blueFactor * sourcePixel[GFX_ARGB32_OFFSET_B]) >> 8;
|
|
||||||
} else {
|
|
||||||
*destPixel = 0;
|
|
||||||
}
|
|
||||||
sourcePixel += 4;
|
|
||||||
destPixel++;
|
|
||||||
}
|
|
||||||
sourcePixel += sourceOffset;
|
|
||||||
destPixel += destOffset;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
ComputeLinearRGBLuminanceMask(const uint8_t *aSourceData,
|
|
||||||
int32_t aSourceStride,
|
|
||||||
uint8_t *aDestData,
|
|
||||||
int32_t aDestStride,
|
|
||||||
const IntSize &aSize,
|
|
||||||
float aOpacity)
|
|
||||||
{
|
|
||||||
int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity
|
|
||||||
int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity
|
|
||||||
int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721
|
|
||||||
int32_t sourceOffset = aSourceStride - 4 * aSize.width;
|
|
||||||
const uint8_t *sourcePixel = aSourceData;
|
|
||||||
int32_t destOffset = aDestStride - aSize.width;
|
|
||||||
uint8_t *destPixel = aDestData;
|
|
||||||
|
|
||||||
for (int32_t y = 0; y < aSize.height; y++) {
|
|
||||||
for (int32_t x = 0; x < aSize.width; x++) {
|
|
||||||
uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A];
|
|
||||||
|
|
||||||
// unpremultiply
|
|
||||||
if (a) {
|
|
||||||
if (a == 255) {
|
|
||||||
/* sRGB -> linearRGB -> intensity */
|
|
||||||
*destPixel =
|
|
||||||
static_cast<uint8_t>
|
|
||||||
((gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_R]] *
|
|
||||||
redFactor +
|
|
||||||
gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_G]] *
|
|
||||||
greenFactor +
|
|
||||||
gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_B]] *
|
|
||||||
blueFactor) >> 8);
|
|
||||||
} else {
|
|
||||||
uint8_t tempPixel[4];
|
|
||||||
tempPixel[GFX_ARGB32_OFFSET_B] =
|
|
||||||
(255 * sourcePixel[GFX_ARGB32_OFFSET_B]) / a;
|
|
||||||
tempPixel[GFX_ARGB32_OFFSET_G] =
|
|
||||||
(255 * sourcePixel[GFX_ARGB32_OFFSET_G]) / a;
|
|
||||||
tempPixel[GFX_ARGB32_OFFSET_R] =
|
|
||||||
(255 * sourcePixel[GFX_ARGB32_OFFSET_R]) / a;
|
|
||||||
|
|
||||||
/* sRGB -> linearRGB -> intensity */
|
|
||||||
*destPixel =
|
|
||||||
static_cast<uint8_t>
|
|
||||||
(((gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_R]] *
|
|
||||||
redFactor +
|
|
||||||
gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_G]] *
|
|
||||||
greenFactor +
|
|
||||||
gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_B]] *
|
|
||||||
blueFactor) >> 8) * (a / 255.0f));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*destPixel = 0;
|
|
||||||
}
|
|
||||||
sourcePixel += 4;
|
|
||||||
destPixel++;
|
|
||||||
}
|
|
||||||
sourcePixel += sourceOffset;
|
|
||||||
destPixel += destOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
|
||||||
// Implementation
|
|
||||||
|
|
||||||
nsIFrame*
|
nsIFrame*
|
||||||
NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
||||||
{
|
{
|
||||||
|
@ -258,44 +124,20 @@ nsSVGMaskFrame::GetMaskForMaskedFrame(MaskParams& aParams)
|
||||||
nsSVGUtils::PaintFrameWithEffects(kid, *tmpCtx, m, aParams.imgParams);
|
nsSVGUtils::PaintFrameWithEffects(kid, *tmpCtx, m, aParams.imgParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StyleSVG()->mColorInterpolation ==
|
||||||
|
NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) {
|
||||||
|
maskType = NS_STYLE_COLOR_INTERPOLATION_LINEARRGB;
|
||||||
|
}
|
||||||
|
|
||||||
RefPtr<SourceSurface> surface;
|
RefPtr<SourceSurface> surface;
|
||||||
if (maskType == NS_STYLE_MASK_TYPE_LUMINANCE) {
|
if (maskType == NS_STYLE_MASK_TYPE_LUMINANCE) {
|
||||||
RefPtr<SourceSurface> maskSnapshot = maskDT->Snapshot();
|
RefPtr<SourceSurface> maskSnapshot =
|
||||||
|
maskDT->IntoLuminanceSource(GetLuminanceType(maskType),
|
||||||
|
aParams.opacity);
|
||||||
if (!maskSnapshot) {
|
if (!maskSnapshot) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
surface = maskSnapshot.forget();
|
||||||
RefPtr<DataSourceSurface> maskSurface = maskSnapshot->GetDataSurface();
|
|
||||||
DataSourceSurface::MappedSurface map;
|
|
||||||
if (!maskSurface->Map(DataSourceSurface::MapType::READ, &map)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create alpha channel mask for output
|
|
||||||
RefPtr<DataSourceSurface> destMaskSurface =
|
|
||||||
Factory::CreateDataSourceSurface(maskSurfaceSize, SurfaceFormat::A8);
|
|
||||||
if (!destMaskSurface) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
DataSourceSurface::MappedSurface destMap;
|
|
||||||
if (!destMaskSurface->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StyleSVG()->mColorInterpolation ==
|
|
||||||
NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) {
|
|
||||||
ComputeLinearRGBLuminanceMask(map.mData, map.mStride,
|
|
||||||
destMap.mData, destMap.mStride,
|
|
||||||
maskSurfaceSize, aParams.opacity);
|
|
||||||
} else {
|
|
||||||
ComputesRGBLuminanceMask(map.mData, map.mStride,
|
|
||||||
destMap.mData, destMap.mStride,
|
|
||||||
maskSurfaceSize, aParams.opacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
maskSurface->Unmap();
|
|
||||||
destMaskSurface->Unmap();
|
|
||||||
surface = destMaskSurface.forget();
|
|
||||||
} else {
|
} else {
|
||||||
maskDT->SetTransform(Matrix());
|
maskDT->SetTransform(Matrix());
|
||||||
maskDT->FillRect(Rect(0, 0, maskSurfaceSize.width, maskSurfaceSize.height), ColorPattern(Color(1.0f, 1.0f, 1.0f, aParams.opacity)), DrawOptions(1, CompositionOp::OP_IN));
|
maskDT->FillRect(Rect(0, 0, maskSurfaceSize.width, maskSurfaceSize.height), ColorPattern(Color(1.0f, 1.0f, 1.0f, aParams.opacity)), DrawOptions(1, CompositionOp::OP_IN));
|
||||||
|
|
|
@ -16,21 +16,6 @@
|
||||||
|
|
||||||
class gfxContext;
|
class gfxContext;
|
||||||
|
|
||||||
/**
|
|
||||||
* Byte offsets of channels in a native packed gfxColor or cairo image surface.
|
|
||||||
*/
|
|
||||||
#ifdef IS_BIG_ENDIAN
|
|
||||||
#define GFX_ARGB32_OFFSET_A 0
|
|
||||||
#define GFX_ARGB32_OFFSET_R 1
|
|
||||||
#define GFX_ARGB32_OFFSET_G 2
|
|
||||||
#define GFX_ARGB32_OFFSET_B 3
|
|
||||||
#else
|
|
||||||
#define GFX_ARGB32_OFFSET_A 3
|
|
||||||
#define GFX_ARGB32_OFFSET_R 2
|
|
||||||
#define GFX_ARGB32_OFFSET_G 1
|
|
||||||
#define GFX_ARGB32_OFFSET_B 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class nsSVGMaskFrame final : public nsSVGContainerFrame
|
class nsSVGMaskFrame final : public nsSVGContainerFrame
|
||||||
{
|
{
|
||||||
friend nsIFrame*
|
friend nsIFrame*
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#include "nsSVGMaskFrameNEON.h"
|
|
||||||
#include "nsSVGMaskFrame.h"
|
|
||||||
#include <arm_neon.h>
|
|
||||||
|
|
||||||
using namespace mozilla::gfx;
|
|
||||||
|
|
||||||
void
|
|
||||||
ComputesRGBLuminanceMask_NEON(const uint8_t *aSourceData,
|
|
||||||
int32_t aSourceStride,
|
|
||||||
uint8_t *aDestData,
|
|
||||||
int32_t aDestStride,
|
|
||||||
const IntSize &aSize,
|
|
||||||
float aOpacity)
|
|
||||||
{
|
|
||||||
int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity
|
|
||||||
int32_t greenFactor = 183 * aOpacity; // 255 * 0.7154 * opacity
|
|
||||||
int32_t blueFactor = 18 * aOpacity; // 255 * 0.0721
|
|
||||||
const uint8_t *sourcePixel = aSourceData;
|
|
||||||
int32_t sourceOffset = aSourceStride - 4 * aSize.width;
|
|
||||||
uint8_t *destPixel = aDestData;
|
|
||||||
int32_t destOffset = aDestStride - aSize.width;
|
|
||||||
|
|
||||||
sourcePixel = aSourceData;
|
|
||||||
int32_t remainderWidth = aSize.width % 8;
|
|
||||||
int32_t roundedWidth = aSize.width - remainderWidth;
|
|
||||||
uint16x8_t temp;
|
|
||||||
uint8x8_t gray;
|
|
||||||
uint8x8_t redVector = vdup_n_u8(redFactor);
|
|
||||||
uint8x8_t greenVector = vdup_n_u8(greenFactor);
|
|
||||||
uint8x8_t blueVector = vdup_n_u8(blueFactor);
|
|
||||||
uint8x8_t fullBitVector = vdup_n_u8(255);
|
|
||||||
uint8x8_t oneVector = vdup_n_u8(1);
|
|
||||||
for (int32_t y = 0; y < aSize.height; y++) {
|
|
||||||
// Calculate luminance by neon with 8 pixels per loop
|
|
||||||
for (int32_t x = 0; x < roundedWidth; x += 8) {
|
|
||||||
uint8x8x4_t argb = vld4_u8(sourcePixel);
|
|
||||||
temp = vmull_u8(argb.val[GFX_ARGB32_OFFSET_R], redVector); // temp = red * redFactor
|
|
||||||
temp = vmlal_u8(temp, argb.val[GFX_ARGB32_OFFSET_G], greenVector); // temp += green * greenFactor
|
|
||||||
temp = vmlal_u8(temp, argb.val[GFX_ARGB32_OFFSET_B], blueVector); // temp += blue * blueFactor
|
|
||||||
gray = vshrn_n_u16(temp, 8); // gray = temp >> 8
|
|
||||||
|
|
||||||
// Check alpha value
|
|
||||||
uint8x8_t alphaVector = vtst_u8(argb.val[GFX_ARGB32_OFFSET_A], fullBitVector);
|
|
||||||
gray = vmul_u8(gray, vand_u8(alphaVector, oneVector));
|
|
||||||
|
|
||||||
// Put the result to the 8 pixels
|
|
||||||
vst1_u8(destPixel, gray);
|
|
||||||
sourcePixel += 8 * 4;
|
|
||||||
destPixel += 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the rest pixels of the line by cpu
|
|
||||||
for (int32_t x = 0; x < remainderWidth; x++) {
|
|
||||||
if (sourcePixel[GFX_ARGB32_OFFSET_A] > 0) {
|
|
||||||
*destPixel = (redFactor * sourcePixel[GFX_ARGB32_OFFSET_R]+
|
|
||||||
greenFactor * sourcePixel[GFX_ARGB32_OFFSET_G] +
|
|
||||||
blueFactor * sourcePixel[GFX_ARGB32_OFFSET_B]) >> 8;
|
|
||||||
} else {
|
|
||||||
*destPixel = 0;
|
|
||||||
}
|
|
||||||
sourcePixel += 4;
|
|
||||||
destPixel++;
|
|
||||||
}
|
|
||||||
sourcePixel += sourceOffset;
|
|
||||||
destPixel += destOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
||||||
/* this source code form is subject to the terms of the mozilla public
|
|
||||||
* license, v. 2.0. if a copy of the mpl was not distributed with this file,
|
|
||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#ifndef __NS_SVGMASKFRAMENEON_H__
|
|
||||||
#define __NS_SVGMASKFRAMENEON_H__
|
|
||||||
|
|
||||||
#include "mozilla/gfx/Point.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
ComputesRGBLuminanceMask_NEON(const uint8_t *aSourceData,
|
|
||||||
int32_t aSourceStride,
|
|
||||||
uint8_t *aDestData,
|
|
||||||
int32_t aDestStride,
|
|
||||||
const mozilla::gfx::IntSize &aSize,
|
|
||||||
float aOpacity);
|
|
||||||
|
|
||||||
#endif /* __NS_SVGMASKFRAMENEON_H__ */
|
|
|
@ -144,7 +144,7 @@ function StartTestURI(type, uri, timeout)
|
||||||
// the JS ref tests disable the normal browser chrome and do not otherwise
|
// the JS ref tests disable the normal browser chrome and do not otherwise
|
||||||
// create substatial DOM garbage, the CC tends not to run enough normally.
|
// create substatial DOM garbage, the CC tends not to run enough normally.
|
||||||
++gTestCount;
|
++gTestCount;
|
||||||
if (gTestCount % 1000 == 0) {
|
if (gTestCount % 250 == 0) {
|
||||||
CU.forceGC();
|
CU.forceGC();
|
||||||
CU.forceCC();
|
CU.forceCC();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,15 @@
|
||||||
#define MOZ_EMBEDDED_LIBPNG
|
#define MOZ_EMBEDDED_LIBPNG
|
||||||
|
|
||||||
/* Limit image dimensions (bug #251381, #591822, #967656, and #1283961) */
|
/* Limit image dimensions (bug #251381, #591822, #967656, and #1283961) */
|
||||||
|
#define PNG_USER_LIMITS_SUPPORTED
|
||||||
#ifndef MOZ_PNG_MAX_WIDTH
|
#ifndef MOZ_PNG_MAX_WIDTH
|
||||||
# define MOZ_PNG_MAX_WIDTH 0x7fffffffL /* Unlimited */
|
# define MOZ_PNG_MAX_WIDTH 0x7fffffffL /* Unlimited */
|
||||||
#endif
|
#endif
|
||||||
#ifndef MOZ_PNG_MAX_HEIGHT
|
#ifndef MOZ_PNG_MAX_HEIGHT
|
||||||
# define MOZ_PNG_MAX_HEIGHT 0x7fffffffL /* Unlimited */
|
# define MOZ_PNG_MAX_HEIGHT 0x7fffffffL /* Unlimited */
|
||||||
#endif
|
#endif
|
||||||
|
/* but allow nsPNGDecoder to override the limits (bug #1368407) */
|
||||||
|
#define PNG_SET_USER_LIMITS_SUPPORTED
|
||||||
|
|
||||||
#define PNG_API_RULE 0
|
#define PNG_API_RULE 0
|
||||||
#define PNG_COST_SHIFT 3
|
#define PNG_COST_SHIFT 3
|
||||||
|
|
|
@ -1394,7 +1394,7 @@ pref("javascript.options.mem.high_water_mark", 128);
|
||||||
pref("javascript.options.mem.max", -1);
|
pref("javascript.options.mem.max", -1);
|
||||||
pref("javascript.options.mem.gc_per_zone", true);
|
pref("javascript.options.mem.gc_per_zone", true);
|
||||||
pref("javascript.options.mem.gc_incremental", true);
|
pref("javascript.options.mem.gc_incremental", true);
|
||||||
pref("javascript.options.mem.gc_incremental_slice_ms", 10);
|
pref("javascript.options.mem.gc_incremental_slice_ms", 5);
|
||||||
pref("javascript.options.mem.gc_compacting", true);
|
pref("javascript.options.mem.gc_compacting", true);
|
||||||
pref("javascript.options.mem.log", false);
|
pref("javascript.options.mem.log", false);
|
||||||
pref("javascript.options.mem.notify", false);
|
pref("javascript.options.mem.notify", false);
|
||||||
|
|
|
@ -5,48 +5,34 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
// Get bookmark service
|
// Get bookmark service
|
||||||
try {
|
let bm = PlacesUtils.bookmarks;
|
||||||
var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
|
|
||||||
getService(Ci.nsINavBookmarksService);
|
|
||||||
} catch (ex) {
|
|
||||||
do_throw("Could not get nav-bookmarks-service\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
var gTestRoot;
|
// Test that Bookmarks fetch properly orders its results based on
|
||||||
var gURI;
|
// the last modified value. Note we cannot rely on dateAdded due to
|
||||||
var gItemId1;
|
// the low PR_Now() resolution.
|
||||||
var gItemId2;
|
|
||||||
|
|
||||||
// main
|
add_task(async function sort_bookmark_by_relevance() {
|
||||||
function run_test() {
|
let now = new Date();
|
||||||
gURI = uri("http://foo.tld.com/");
|
let modifiedTime = new Date(now.setHours(now.getHours() - 2));
|
||||||
gTestRoot = bmsvc.createFolder(bmsvc.placesRoot, "test folder",
|
|
||||||
bmsvc.DEFAULT_INDEX);
|
|
||||||
|
|
||||||
// test getBookmarkIdsForURI
|
let url = "http://foo.tld.com/";
|
||||||
// getBookmarkIdsForURI sorts by the most recently added/modified (descending)
|
let parentGuid = (await bm.insert({type: bm.TYPE_FOLDER,
|
||||||
//
|
title: "test folder",
|
||||||
// we cannot rely on dateAdded growing when doing so in a simple iteration,
|
parentGuid: bm.unfiledGuid})).guid;
|
||||||
// see PR_Now() documentation
|
let item1Guid = (await bm.insert({url,
|
||||||
do_test_pending();
|
parentGuid})).guid;
|
||||||
|
let item2Guid = (await bm.insert({url,
|
||||||
|
parentGuid,
|
||||||
|
dateAdded: modifiedTime,
|
||||||
|
lastModified: modifiedTime})).guid;
|
||||||
|
let bms = [];
|
||||||
|
await bm.fetch({url}, bm1 => bms.push(bm1));
|
||||||
|
Assert.equal(bms[0].guid, item1Guid);
|
||||||
|
Assert.equal(bms[1].guid, item2Guid);
|
||||||
|
await bm.update({guid: item2Guid, title: "modified"});
|
||||||
|
|
||||||
gItemId1 = bmsvc.insertBookmark(gTestRoot, gURI, bmsvc.DEFAULT_INDEX, "");
|
let bms1 = [];
|
||||||
do_timeout(100, phase2);
|
await bm.fetch({url}, bm2 => bms1.push(bm2));
|
||||||
}
|
Assert.equal(bms1[0].guid, item2Guid);
|
||||||
|
Assert.equal(bms1[1].guid, item1Guid);
|
||||||
function phase2() {
|
});
|
||||||
gItemId2 = bmsvc.insertBookmark(gTestRoot, gURI, bmsvc.DEFAULT_INDEX, "");
|
|
||||||
var b = bmsvc.getBookmarkIdsForURI(gURI);
|
|
||||||
do_check_eq(b[0], gItemId2);
|
|
||||||
do_check_eq(b[1], gItemId1);
|
|
||||||
do_timeout(100, phase3);
|
|
||||||
}
|
|
||||||
|
|
||||||
function phase3() {
|
|
||||||
// trigger last modified change
|
|
||||||
bmsvc.setItemTitle(gItemId1, "");
|
|
||||||
var b = bmsvc.getBookmarkIdsForURI(gURI);
|
|
||||||
do_check_eq(b[0], gItemId1);
|
|
||||||
do_check_eq(b[1], gItemId2);
|
|
||||||
do_test_finished();
|
|
||||||
}
|
|
||||||
|
|
|
@ -527,7 +527,11 @@ add_task(async function test_subprocess_pathSearch() {
|
||||||
|
|
||||||
add_task(async function test_subprocess_workdir() {
|
add_task(async function test_subprocess_workdir() {
|
||||||
let procDir = await OS.File.getCurrentDirectory();
|
let procDir = await OS.File.getCurrentDirectory();
|
||||||
let tmpDir = OS.Constants.Path.tmpDir;
|
let tmpDirFile = Components.classes["@mozilla.org/file/local;1"]
|
||||||
|
.createInstance(Components.interfaces.nsILocalFile);
|
||||||
|
tmpDirFile.initWithPath(OS.Constants.Path.tmpDir);
|
||||||
|
tmpDirFile.normalize();
|
||||||
|
let tmpDir = tmpDirFile.path;
|
||||||
|
|
||||||
notEqual(procDir, tmpDir,
|
notEqual(procDir, tmpDir,
|
||||||
"Current process directory must not be the current temp directory");
|
"Current process directory must not be the current temp directory");
|
||||||
|
|
|
@ -5019,9 +5019,7 @@ MultiprocessBlockPolicy() {
|
||||||
return gMultiprocessBlockPolicy;
|
return gMultiprocessBlockPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(XP_WIN)
|
#if defined(XP_WIN) && defined(RELEASE_OR_BETA)
|
||||||
// These checks are currently only in use under WinXP
|
|
||||||
if (false) { // !IsVistaOrLater()
|
|
||||||
bool disabledForA11y = false;
|
bool disabledForA11y = false;
|
||||||
/**
|
/**
|
||||||
* Avoids enabling e10s if accessibility has recently loaded. Performs the
|
* Avoids enabling e10s if accessibility has recently loaded. Performs the
|
||||||
|
@ -5049,11 +5047,11 @@ MultiprocessBlockPolicy() {
|
||||||
disabledForA11y = true;
|
disabledForA11y = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (disabledForA11y) {
|
if (disabledForA11y) {
|
||||||
gMultiprocessBlockPolicy = kE10sDisabledForAccessibility;
|
gMultiprocessBlockPolicy = kE10sDisabledForAccessibility;
|
||||||
return gMultiprocessBlockPolicy;
|
return gMultiprocessBlockPolicy;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -28,6 +28,7 @@ void AddQuarantineMetadataToFile(const CFStringRef filePath,
|
||||||
const CFURLRef sourceURL,
|
const CFURLRef sourceURL,
|
||||||
const CFURLRef referrerURL,
|
const CFURLRef referrerURL,
|
||||||
const bool isFromWeb);
|
const bool isFromWeb);
|
||||||
|
CFURLRef GetTemporaryFolderCFURLRef();
|
||||||
|
|
||||||
} // namespace CocoaFileUtils
|
} // namespace CocoaFileUtils
|
||||||
|
|
||||||
|
|
|
@ -257,4 +257,11 @@ void AddQuarantineMetadataToFile(const CFStringRef filePath,
|
||||||
::CFRelease(mutQuarantineProps);
|
::CFRelease(mutQuarantineProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CFURLRef GetTemporaryFolderCFURLRef()
|
||||||
|
{
|
||||||
|
NSString* tempDir = ::NSTemporaryDirectory();
|
||||||
|
return tempDir == nil ? NULL : (CFURLRef)[NSURL fileURLWithPath:tempDir
|
||||||
|
isDirectory:YES];
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace CocoaFileUtils
|
} // namespace CocoaFileUtils
|
||||||
|
|
|
@ -27,6 +27,9 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include "prenv.h"
|
#include "prenv.h"
|
||||||
|
#if defined(MOZ_WIDGET_COCOA)
|
||||||
|
#include "CocoaFileUtils.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -766,10 +769,20 @@ GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
|
||||||
nsresult
|
nsresult
|
||||||
GetOSXFolderType(short aDomain, OSType aFolderType, nsIFile** aLocalFile)
|
GetOSXFolderType(short aDomain, OSType aFolderType, nsIFile** aLocalFile)
|
||||||
{
|
{
|
||||||
OSErr err;
|
|
||||||
FSRef fsRef;
|
|
||||||
nsresult rv = NS_ERROR_FAILURE;
|
nsresult rv = NS_ERROR_FAILURE;
|
||||||
|
|
||||||
|
if (aFolderType == kTemporaryFolderType) {
|
||||||
|
NS_NewLocalFile(EmptyString(), true, aLocalFile);
|
||||||
|
nsCOMPtr<nsILocalFileMac> localMacFile(do_QueryInterface(*aLocalFile));
|
||||||
|
if (localMacFile) {
|
||||||
|
rv = localMacFile->InitWithCFURL(
|
||||||
|
CocoaFileUtils::GetTemporaryFolderCFURLRef());
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSErr err;
|
||||||
|
FSRef fsRef;
|
||||||
err = ::FSFindFolder(aDomain, aFolderType, kCreateFolder, &fsRef);
|
err = ::FSFindFolder(aDomain, aFolderType, kCreateFolder, &fsRef);
|
||||||
if (err == noErr) {
|
if (err == noErr) {
|
||||||
NS_NewLocalFile(EmptyString(), true, aLocalFile);
|
NS_NewLocalFile(EmptyString(), true, aLocalFile);
|
||||||
|
|
|
@ -25,6 +25,10 @@ function test_normalized_vs_non_normalized()
|
||||||
if (!exists)
|
if (!exists)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// the test logic below assumes we're starting with a normalized path, but the
|
||||||
|
// default location on macos is a symbolic link, so resolve it before starting
|
||||||
|
tmp1.normalize();
|
||||||
|
|
||||||
// this has the same exact path as tmp1, it should equal tmp1
|
// this has the same exact path as tmp1, it should equal tmp1
|
||||||
var tmp2 = new LocalFile(tmp1.path);
|
var tmp2 = new LocalFile(tmp1.path);
|
||||||
do_check_true(tmp1.equals(tmp2));
|
do_check_true(tmp1.equals(tmp2));
|
||||||
|
|
Загрузка…
Ссылка в новой задаче