зеркало из 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.
|
||||
DEFINES['ENTRY_PREFIX'] = 'Proxy'
|
||||
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
|
||||
# finer granularity than the standard preprocessor definitions offer.
|
||||
|
|
|
@ -540,7 +540,8 @@ BrowserGlue.prototype = {
|
|||
this._flashHangCount = 0;
|
||||
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
|
||||
// and e10s are active together.
|
||||
E10SAccessibilityCheck.init();
|
||||
|
|
|
@ -12,13 +12,14 @@ const {
|
|||
} = require("devtools/client/shared/vendor/react");
|
||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||
const Actions = require("../actions/index");
|
||||
const { FILTER_SEARCH_DELAY, FILTER_FLAGS } = require("../constants");
|
||||
const { FILTER_SEARCH_DELAY } = require("../constants");
|
||||
const {
|
||||
getDisplayedRequestsSummary,
|
||||
getRequestFilterTypes,
|
||||
isNetworkDetailsToggleButtonDisabled,
|
||||
} = require("../selectors/index");
|
||||
|
||||
const { autocompleteProvider } = require("../utils/filter-text-utils");
|
||||
const { L10N } = require("../utils/l10n");
|
||||
|
||||
// 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 (
|
||||
span({ className: "devtools-toolbar devtools-toolbar-container" },
|
||||
span({ className: "devtools-toolbar-group" },
|
||||
|
@ -114,7 +110,7 @@ const Toolbar = createClass({
|
|||
placeholder: SEARCH_PLACE_HOLDER,
|
||||
type: "filter",
|
||||
onChange: setRequestFilterText,
|
||||
autocompleteList,
|
||||
autocompleteProvider,
|
||||
}),
|
||||
button({
|
||||
className: toggleButtonClassName.join(" "),
|
||||
|
|
|
@ -242,6 +242,50 @@ function isFreetextMatch(item, text) {
|
|||
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 = {
|
||||
isFreetextMatch,
|
||||
autocompleteProvider,
|
||||
};
|
||||
|
|
|
@ -10,7 +10,15 @@ module.exports = createClass({
|
|||
displayName: "AutocompletePopup",
|
||||
|
||||
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,
|
||||
onItemSelected: PropTypes.func.isRequired,
|
||||
},
|
||||
|
@ -32,14 +40,11 @@ module.exports = createClass({
|
|||
}
|
||||
},
|
||||
|
||||
computeState({ filter, list }) {
|
||||
let filteredList = list.filter((item) => {
|
||||
return item.toLowerCase().startsWith(filter.toLowerCase())
|
||||
&& item.toLowerCase() !== filter.toLowerCase();
|
||||
}).sort();
|
||||
let selectedIndex = filteredList.length == 1 ? 0 : -1;
|
||||
computeState({ autocompleteProvider, filter }) {
|
||||
let list = autocompleteProvider(filter);
|
||||
let selectedIndex = list.length == 1 ? 0 : -1;
|
||||
|
||||
return { filteredList, selectedIndex };
|
||||
return { list, selectedIndex };
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -55,8 +60,7 @@ module.exports = createClass({
|
|||
* This method is public.
|
||||
*/
|
||||
jumpToBottom() {
|
||||
let selectedIndex = this.state.filteredList.length - 1;
|
||||
this.setState({ selectedIndex });
|
||||
this.setState({ selectedIndex: this.state.list.length - 1 });
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -67,14 +71,14 @@ module.exports = createClass({
|
|||
* @param {number} increment - No. of hops in the direction
|
||||
*/
|
||||
jumpBy(increment = 1) {
|
||||
let { filteredList, selectedIndex } = this.state;
|
||||
let { list, selectedIndex } = this.state;
|
||||
let nextIndex = selectedIndex + increment;
|
||||
if (increment > 0) {
|
||||
// Positive cycling
|
||||
nextIndex = nextIndex > filteredList.length - 1 ? 0 : nextIndex;
|
||||
nextIndex = nextIndex > list.length - 1 ? 0 : nextIndex;
|
||||
} else if (increment < 0) {
|
||||
// Inverse cycling
|
||||
nextIndex = nextIndex < 0 ? filteredList.length - 1 : nextIndex;
|
||||
nextIndex = nextIndex < 0 ? list.length - 1 : nextIndex;
|
||||
}
|
||||
this.setState({selectedIndex: nextIndex});
|
||||
},
|
||||
|
@ -85,7 +89,7 @@ module.exports = createClass({
|
|||
*/
|
||||
select() {
|
||||
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() {
|
||||
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" },
|
||||
dom.ul(
|
||||
{ className: "devtools-autocomplete-listbox" },
|
||||
filteredList.map((item, i) => {
|
||||
list.map((item, i) => {
|
||||
let isSelected = this.state.selectedIndex == i;
|
||||
let itemClassList = ["autocomplete-item"];
|
||||
|
||||
|
@ -111,10 +115,11 @@ module.exports = createClass({
|
|||
return dom.li({
|
||||
key: i,
|
||||
"data-index": i,
|
||||
"data-value": item.value,
|
||||
className: itemClassList.join(" "),
|
||||
ref: isSelected ? "selected" : null,
|
||||
onMouseDown: this.onMouseDown,
|
||||
}, item);
|
||||
}, item.displayValue);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
|
|
@ -22,13 +22,7 @@ module.exports = createClass({
|
|||
onChange: PropTypes.func,
|
||||
placeholder: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
autocompleteList: PropTypes.array,
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
autocompleteList: [],
|
||||
};
|
||||
autocompleteProvider: PropTypes.func,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
|
@ -66,6 +60,7 @@ module.exports = createClass({
|
|||
onChange() {
|
||||
if (this.state.value !== this.refs.input.value) {
|
||||
this.setState({
|
||||
focused: true,
|
||||
value: this.refs.input.value,
|
||||
});
|
||||
}
|
||||
|
@ -102,10 +97,8 @@ module.exports = createClass({
|
|||
},
|
||||
|
||||
onKeyDown(e) {
|
||||
let { autocompleteList } = this.props;
|
||||
let { autocomplete } = this.refs;
|
||||
|
||||
if (autocompleteList.length == 0) {
|
||||
if (!autocomplete || autocomplete.state.list.length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -144,13 +137,12 @@ module.exports = createClass({
|
|||
let {
|
||||
type = "search",
|
||||
placeholder,
|
||||
autocompleteList
|
||||
autocompleteProvider,
|
||||
} = this.props;
|
||||
let { value } = this.state;
|
||||
let divClassList = ["devtools-searchbox", "has-clear-btn"];
|
||||
let inputClassList = [`devtools-${type}input`];
|
||||
let showAutocomplete =
|
||||
autocompleteList.length > 0 && this.state.focused && value !== "";
|
||||
let showAutocomplete = autocompleteProvider && this.state.focused && value !== "";
|
||||
|
||||
if (value !== "") {
|
||||
inputClassList.push("filled");
|
||||
|
@ -173,7 +165,7 @@ module.exports = createClass({
|
|||
onClick: this.onClearButtonClick
|
||||
}),
|
||||
showAutocomplete && AutocompletePopup({
|
||||
list: autocompleteList,
|
||||
autocompleteProvider,
|
||||
filter: value,
|
||||
ref: "autocomplete",
|
||||
onItemSelected: (itemValue) => {
|
||||
|
|
|
@ -43,7 +43,8 @@ window.onload = async function () {
|
|||
);
|
||||
const { component, $ } = await createComponentTest(SearchBox, {
|
||||
type: "search",
|
||||
autocompleteList: [
|
||||
autocompleteProvider: (filter) => {
|
||||
let baseList = [
|
||||
"foo",
|
||||
"BAR",
|
||||
"baZ",
|
||||
|
@ -56,7 +57,30 @@ window.onload = async function () {
|
|||
"a3",
|
||||
"a4",
|
||||
"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,
|
||||
});
|
||||
const { refs } = component;
|
||||
|
@ -150,6 +174,8 @@ window.onload = async function () {
|
|||
ok(!$(".devtools-autocomplete-popup"), "Enter/Return hides the popup");
|
||||
|
||||
// Escape should remove the autocomplete component
|
||||
synthesizeKey("VK_BACK_SPACE", {});
|
||||
await forceRender(component);
|
||||
synthesizeKey("VK_ESCAPE", {});
|
||||
await forceRender(component);
|
||||
ok(!$(".devtools-autocomplete-popup"),
|
||||
|
@ -182,10 +208,25 @@ window.onload = async function () {
|
|||
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 () {
|
||||
await testSearchBoxWithAutocomplete();
|
||||
await testKeyEventsWithAutocomplete();
|
||||
await testMouseEventsWithAutocomplete();
|
||||
await testTokenizedAutocomplete();
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -81,6 +81,7 @@ const ErrorDocs = {
|
|||
JSMSG_BAD_URI: "Malformed_URI",
|
||||
JSMSG_DEPRECATED_DELETE_OPERAND: "Delete_in_strict_mode",
|
||||
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";
|
||||
|
|
|
@ -414,8 +414,10 @@ DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time
|
|||
if (intersectionRect.isSome() && !isSameDoc) {
|
||||
nsRect rect = intersectionRect.value();
|
||||
nsPresContext* presContext = targetFrame->PresContext();
|
||||
nsLayoutUtils::TransformRect(rootFrame,
|
||||
presContext->PresShell()->GetRootScrollFrame(), rect);
|
||||
nsIFrame* rootScrollFrame = presContext->PresShell()->GetRootScrollFrame();
|
||||
if (rootScrollFrame) {
|
||||
nsLayoutUtils::TransformRect(rootFrame, rootScrollFrame, 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 1158412.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 1251361.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-2.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.IntersectionObserver.enabled,true) load 1353529.xul
|
||||
pref(dom.IntersectionObserver.enabled,true) load 1369363.xul
|
||||
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 "nsScriptNameSpaceManager.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/MainThreadIdlePeriod.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/dom/DOMExceptionBinding.h"
|
||||
#include "mozilla/dom/ErrorEvent.h"
|
||||
|
@ -63,7 +65,7 @@
|
|||
#include "nsAXPCNativeCallContext.h"
|
||||
#include "mozilla/CycleCollectedJSRuntime.h"
|
||||
#include "mozilla/SystemGroup.h"
|
||||
|
||||
#include "nsRefreshDriver.h"
|
||||
#include "nsJSPrincipals.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
|
@ -111,21 +113,24 @@ const size_t gStackSize = 8192;
|
|||
// Maximum amount of time that should elapse between incremental GC slices
|
||||
#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)
|
||||
// and doing the actual CC.
|
||||
#define NS_CC_DELAY 6000 // 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
|
||||
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
|
||||
// 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
|
||||
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
|
||||
#define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
|
||||
|
||||
class CollectorRunner;
|
||||
|
||||
// if you add statics here, add them to the list in StartupJSEnvironment
|
||||
|
||||
static nsITimer *sGCTimer;
|
||||
static nsITimer *sShrinkingGCTimer;
|
||||
static nsITimer *sCCTimer;
|
||||
static nsITimer *sICCTimer;
|
||||
static StaticRefPtr<CollectorRunner> sCCRunner;
|
||||
static StaticRefPtr<CollectorRunner> sICCRunner;
|
||||
static nsITimer *sFullGCTimer;
|
||||
static nsITimer *sInterSliceGCTimer;
|
||||
static StaticRefPtr<CollectorRunner> sInterSliceGCRunner;
|
||||
|
||||
static TimeStamp sLastCCEndTime;
|
||||
|
||||
|
@ -176,7 +183,7 @@ static uint32_t sCCollectedZonesWaitingForGC;
|
|||
static uint32_t sLikelyShortLivingObjectsNeedingGC;
|
||||
static bool sPostGCEventsToConsole;
|
||||
static bool sPostGCEventsToObserver;
|
||||
static int32_t sCCTimerFireCount = 0;
|
||||
static int32_t sCCRunnerFireCount = 0;
|
||||
static uint32_t sMinForgetSkippableTime = UINT32_MAX;
|
||||
static uint32_t sMaxForgetSkippableTime = 0;
|
||||
static uint32_t sTotalForgetSkippableTime = 0;
|
||||
|
@ -188,7 +195,6 @@ static bool sNeedsFullCC = false;
|
|||
static bool sNeedsFullGC = false;
|
||||
static bool sNeedsGCAfterCC = false;
|
||||
static bool sIncrementalCC = false;
|
||||
static bool sDidPaintAfterPreviousICCSlice = false;
|
||||
static int32_t sActiveIntersliceGCBudget = 0; // ms;
|
||||
static nsScriptNameSpaceManager *gNameSpaceManager;
|
||||
|
||||
|
@ -223,6 +229,178 @@ static bool sIsCompactingOnUserInactive = false;
|
|||
static int32_t sExpensiveCollectorPokes = 0;
|
||||
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*
|
||||
ProcessNameForCollectorLog()
|
||||
{
|
||||
|
@ -299,10 +477,10 @@ KillTimers()
|
|||
{
|
||||
nsJSContext::KillGCTimer();
|
||||
nsJSContext::KillShrinkingGCTimer();
|
||||
nsJSContext::KillCCTimer();
|
||||
nsJSContext::KillICCTimer();
|
||||
nsJSContext::KillCCRunner();
|
||||
nsJSContext::KillICCRunner();
|
||||
nsJSContext::KillFullGCTimer();
|
||||
nsJSContext::KillInterSliceGCTimer();
|
||||
nsJSContext::KillInterSliceGCRunner();
|
||||
}
|
||||
|
||||
// If we collected a substantial amount of cycles, poke the GC since more objects
|
||||
|
@ -1451,7 +1629,7 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
|
|||
|
||||
//static
|
||||
void
|
||||
nsJSContext::RunCycleCollectorSlice()
|
||||
nsJSContext::RunCycleCollectorSlice(TimeStamp aDeadline)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
return;
|
||||
|
@ -1467,33 +1645,40 @@ nsJSContext::RunCycleCollectorSlice()
|
|||
js::SliceBudget budget = js::SliceBudget::unlimited();
|
||||
|
||||
if (sIncrementalCC) {
|
||||
int64_t baseBudget = kICCSliceBudget;
|
||||
if (!aDeadline.IsNull()) {
|
||||
baseBudget = int64_t((aDeadline - TimeStamp::Now()).ToMilliseconds());
|
||||
}
|
||||
|
||||
if (gCCStats.mBeginTime.IsNull()) {
|
||||
// If no CC is in progress, use the standard slice time.
|
||||
budget = js::SliceBudget(js::TimeBudget(kICCSliceBudget));
|
||||
budget = js::SliceBudget(js::TimeBudget(baseBudget));
|
||||
} else {
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
|
||||
// Only run a limited slice if we're within the max running time.
|
||||
uint32_t runningTime = TimeBetween(gCCStats.mBeginTime, now);
|
||||
if (runningTime < kMaxICCDuration) {
|
||||
// Try to make up for a delay in running this slice.
|
||||
float sliceDelayMultiplier = TimeBetween(gCCStats.mEndSliceTime, now) / (float)kICCIntersliceDelay;
|
||||
float delaySliceBudget = kICCSliceBudget * sliceDelayMultiplier;
|
||||
const float maxSlice = MainThreadIdlePeriod::GetLongIdlePeriod();
|
||||
|
||||
// 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.
|
||||
float percentToHalfDone = std::min(2.0f * runningTime / kMaxICCDuration, 1.0f);
|
||||
const float maxLaterSlice = 40.0f;
|
||||
float laterSliceBudget = maxLaterSlice * percentToHalfDone;
|
||||
float laterSliceBudget = maxSlice * percentToHalfDone;
|
||||
|
||||
budget = js::SliceBudget(js::TimeBudget(std::max({delaySliceBudget,
|
||||
laterSliceBudget, (float)kICCSliceBudget})));
|
||||
laterSliceBudget, (float)baseBudget})));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCycleCollector_collectSlice(budget, sDidPaintAfterPreviousICCSlice);
|
||||
sDidPaintAfterPreviousICCSlice = false;
|
||||
nsCycleCollector_collectSlice(budget);
|
||||
|
||||
gCCStats.FinishCycleCollectionSlice();
|
||||
}
|
||||
|
@ -1529,11 +1714,11 @@ nsJSContext::GetMaxCCSliceTimeSinceClear()
|
|||
return gCCStats.mMaxSliceTimeSinceClear;
|
||||
}
|
||||
|
||||
static void
|
||||
ICCTimerFired(nsITimer* aTimer, void* aClosure)
|
||||
static bool
|
||||
ICCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||
{
|
||||
if (sDidShutdown) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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();
|
||||
if (sCCLockedOutTime == 0) {
|
||||
sCCLockedOutTime = now;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
nsJSContext::RunCycleCollectorSlice();
|
||||
nsJSContext::RunCycleCollectorSlice(aDeadline);
|
||||
return true;
|
||||
}
|
||||
|
||||
//static
|
||||
|
@ -1562,22 +1748,16 @@ nsJSContext::BeginCycleCollectionCallback()
|
|||
gCCStats.mBeginTime = gCCStats.mBeginSliceTime.IsNull() ? TimeStamp::Now() : gCCStats.mBeginSliceTime;
|
||||
gCCStats.mSuspected = nsCycleCollector_suspectedCount();
|
||||
|
||||
KillCCTimer();
|
||||
KillCCRunner();
|
||||
|
||||
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
|
||||
// an incremental collection, and we want to be sure to finish it.
|
||||
CallCreateInstance("@mozilla.org/timer;1", &sICCTimer);
|
||||
if (sICCTimer) {
|
||||
sICCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
|
||||
sICCTimer->InitWithNamedFuncCallback(ICCTimerFired, nullptr,
|
||||
kICCIntersliceDelay,
|
||||
nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY,
|
||||
"ICCTimerFired");
|
||||
}
|
||||
sICCRunner = CollectorRunner::Create(ICCRunnerFired, kICCIntersliceDelay,
|
||||
kIdleICCSliceBudget, true);
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
nsJSContext::KillICCTimer();
|
||||
nsJSContext::KillICCRunner();
|
||||
|
||||
// Update timing information for the current slice before we log it, if
|
||||
// we previously called PrepareForCycleCollectionSlice(). During shutdown
|
||||
|
@ -1736,16 +1916,24 @@ nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
|
|||
}
|
||||
|
||||
// static
|
||||
void
|
||||
InterSliceGCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||
bool
|
||||
InterSliceGCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||
{
|
||||
nsJSContext::KillInterSliceGCTimer();
|
||||
int64_t budget = XRE_IsE10sParentProcess() && nsContentUtils::GetUserIsInteracting() && sActiveIntersliceGCBudget ?
|
||||
sActiveIntersliceGCBudget : NS_INTERSLICE_GC_BUDGET;
|
||||
nsJSContext::GarbageCollectNow(JS::gcreason::INTER_SLICE_GC,
|
||||
nsJSContext::KillInterSliceGCRunner();
|
||||
MOZ_ASSERT(sActiveIntersliceGCBudget > 0);
|
||||
int64_t budget = sActiveIntersliceGCBudget;
|
||||
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::NonShrinkingGC,
|
||||
budget);
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -1753,9 +1941,12 @@ void
|
|||
GCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
nsJSContext::KillGCTimer();
|
||||
uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
|
||||
nsJSContext::GarbageCollectNow(static_cast<JS::gcreason::Reason>(reason),
|
||||
nsJSContext::IncrementalGC);
|
||||
// Now start the actual GC after initial timer has fired.
|
||||
sInterSliceGCRunner = CollectorRunner::Create(InterSliceGCRunnerFired,
|
||||
NS_INTERSLICE_GC_DELAY,
|
||||
sActiveIntersliceGCBudget,
|
||||
false,
|
||||
aClosure);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -1778,11 +1969,11 @@ ShouldTriggerCC(uint32_t aSuspected)
|
|||
TimeUntilNow(sLastCCEndTime) > NS_CC_FORCED);
|
||||
}
|
||||
|
||||
static void
|
||||
CCTimerFired(nsITimer *aTimer, void *aClosure)
|
||||
static bool
|
||||
CCRunnerFired(TimeStamp aDeadline, void* aData)
|
||||
{
|
||||
if (sDidShutdown) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint32_t ccDelay = NS_CC_DELAY;
|
||||
|
@ -1791,48 +1982,53 @@ CCTimerFired(nsITimer *aTimer, void *aClosure)
|
|||
|
||||
PRTime now = PR_Now();
|
||||
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
|
||||
// forgetSkippable will be called just a few times.
|
||||
// NS_MAX_CC_LOCKEDOUT_TIME limit guarantees that we end up calling
|
||||
// forgetSkippable and CycleCollectNow eventually.
|
||||
sCCTimerFireCount = 0;
|
||||
sCCRunnerFireCount = 0;
|
||||
sCCLockedOutTime = now;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
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
|
||||
// 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
|
||||
// early timer fire to allow cleanup before the CC.
|
||||
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();
|
||||
if (isLateTimerFire && ShouldTriggerCC(suspected)) {
|
||||
if (sCCTimerFireCount == numEarlyTimerFires + 1) {
|
||||
if (sCCRunnerFireCount == numEarlyTimerFires + 1) {
|
||||
FireForgetSkippable(suspected, true);
|
||||
didDoWork = true;
|
||||
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||
// Our efforts to avoid a CC have failed, so we return to let the
|
||||
// timer fire once more to trigger a CC.
|
||||
return;
|
||||
return didDoWork;
|
||||
}
|
||||
} else {
|
||||
// We are in the final timer fire and still meet the conditions for
|
||||
// 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.
|
||||
nsJSContext::RunCycleCollectorSlice();
|
||||
nsJSContext::RunCycleCollectorSlice(aDeadline);
|
||||
didDoWork = true;
|
||||
}
|
||||
} else if (((sPreviousSuspectedCount + 100) <= suspected) ||
|
||||
(sCleanupsSinceLastGC < NS_MAJOR_FORGET_SKIPPABLE_CALLS)) {
|
||||
// Only do a forget skippable if there are more than a few new objects
|
||||
// or we're doing the initial forget skippables.
|
||||
FireForgetSkippable(suspected, false);
|
||||
didDoWork = true;
|
||||
}
|
||||
|
||||
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
|
||||
// next time, so kill the timer.
|
||||
sPreviousSuspectedCount = 0;
|
||||
nsJSContext::KillCCTimer();
|
||||
nsJSContext::KillCCRunner();
|
||||
}
|
||||
|
||||
return didDoWork;
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -1889,13 +2087,13 @@ ReadyToTriggerExpensiveCollectorTimer()
|
|||
}
|
||||
|
||||
|
||||
// Check all of the various collector timers and see if they are waiting to fire.
|
||||
// For the synchronous collector timers, sGCTimer and sCCTimer, we only want to 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
|
||||
// Check all of the various collector timers/runners and see if they are waiting to fire.
|
||||
// For the synchronous collector timers/runners, sGCTimer and sCCRunner, we only want to
|
||||
// trigger the collection occasionally, because they are expensive. The incremental collector
|
||||
// timers, sInterSliceGCRunner and sICCRunner, are fast and need to be run many times, so
|
||||
// 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.
|
||||
|
||||
// static
|
||||
|
@ -1913,8 +2111,8 @@ nsJSContext::RunNextCollectorTimer()
|
|||
return;
|
||||
}
|
||||
|
||||
if (sInterSliceGCTimer) {
|
||||
InterSliceGCTimerFired(nullptr, nullptr);
|
||||
if (sInterSliceGCRunner) {
|
||||
InterSliceGCRunnerFired(TimeStamp(), nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1922,15 +2120,15 @@ nsJSContext::RunNextCollectorTimer()
|
|||
// anything if a GC is in progress.
|
||||
MOZ_ASSERT(!sCCLockedOut, "Don't check the CC timers if the CC is locked out.");
|
||||
|
||||
if (sCCTimer) {
|
||||
if (sCCRunner) {
|
||||
if (ReadyToTriggerExpensiveCollectorTimer()) {
|
||||
CCTimerFired(nullptr, nullptr);
|
||||
CCRunnerFired(TimeStamp(), nullptr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (sICCTimer) {
|
||||
ICCTimerFired(nullptr, nullptr);
|
||||
if (sICCRunner) {
|
||||
ICCRunnerFired(TimeStamp(), nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1952,12 +2150,12 @@ nsJSContext::PokeGC(JS::gcreason::Reason aReason,
|
|||
sNeedsFullGC = true;
|
||||
}
|
||||
|
||||
if (sGCTimer || sInterSliceGCTimer) {
|
||||
if (sGCTimer || sInterSliceGCRunner) {
|
||||
// There's already a timer for GC'ing, just return
|
||||
return;
|
||||
}
|
||||
|
||||
if (sCCTimer) {
|
||||
if (sCCRunner) {
|
||||
// Make sure CC is called...
|
||||
sNeedsFullCC = true;
|
||||
// and GC after it.
|
||||
|
@ -1965,7 +2163,7 @@ nsJSContext::PokeGC(JS::gcreason::Reason aReason,
|
|||
return;
|
||||
}
|
||||
|
||||
if (sICCTimer) {
|
||||
if (sICCRunner) {
|
||||
// Make sure GC is called after the current CC completes.
|
||||
// No need to set sNeedsFullCC because we are currently running a CC.
|
||||
sNeedsGCAfterCC = true;
|
||||
|
@ -1991,6 +2189,7 @@ nsJSContext::PokeGC(JS::gcreason::Reason aReason,
|
|||
: NS_GC_DELAY),
|
||||
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
|
||||
"GCTimerFired");
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
|
@ -2020,24 +2219,19 @@ nsJSContext::PokeShrinkingGC()
|
|||
void
|
||||
nsJSContext::MaybePokeCC()
|
||||
{
|
||||
if (sCCTimer || sICCTimer || sShuttingDown || !sHasRunGC) {
|
||||
if (sCCRunner || sICCRunner || sShuttingDown || !sHasRunGC) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||
sCCTimerFireCount = 0;
|
||||
CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
|
||||
if (!sCCTimer) {
|
||||
return;
|
||||
}
|
||||
sCCRunnerFireCount = 0;
|
||||
|
||||
// We can kill some objects before running forgetSkippable.
|
||||
nsCycleCollector_dispatchDeferredDeletion();
|
||||
|
||||
sCCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
|
||||
sCCTimer->InitWithNamedFuncCallback(CCTimerFired, nullptr,
|
||||
NS_CC_SKIPPABLE_DELAY,
|
||||
nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY,
|
||||
"CCTimerFired");
|
||||
sCCRunner =
|
||||
CollectorRunner::Create(CCRunnerFired, NS_CC_SKIPPABLE_DELAY,
|
||||
kForgetSkippableSliceDuration, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2061,11 +2255,11 @@ nsJSContext::KillFullGCTimer()
|
|||
}
|
||||
|
||||
void
|
||||
nsJSContext::KillInterSliceGCTimer()
|
||||
nsJSContext::KillInterSliceGCRunner()
|
||||
{
|
||||
if (sInterSliceGCTimer) {
|
||||
sInterSliceGCTimer->Cancel();
|
||||
NS_RELEASE(sInterSliceGCTimer);
|
||||
if (sInterSliceGCRunner) {
|
||||
sInterSliceGCRunner->Cancel();
|
||||
sInterSliceGCRunner = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2081,24 +2275,24 @@ nsJSContext::KillShrinkingGCTimer()
|
|||
|
||||
//static
|
||||
void
|
||||
nsJSContext::KillCCTimer()
|
||||
nsJSContext::KillCCRunner()
|
||||
{
|
||||
sCCLockedOutTime = 0;
|
||||
if (sCCTimer) {
|
||||
sCCTimer->Cancel();
|
||||
NS_RELEASE(sCCTimer);
|
||||
if (sCCRunner) {
|
||||
sCCRunner->Cancel();
|
||||
sCCRunner = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void
|
||||
nsJSContext::KillICCTimer()
|
||||
nsJSContext::KillICCRunner()
|
||||
{
|
||||
sCCLockedOutTime = 0;
|
||||
|
||||
if (sICCTimer) {
|
||||
sICCTimer->Cancel();
|
||||
NS_RELEASE(sICCTimer);
|
||||
if (sICCRunner) {
|
||||
sICCRunner->Cancel();
|
||||
sICCRunner = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2170,8 +2364,8 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
|
|||
sCCLockedOut = false;
|
||||
sIsCompactingOnUserInactive = false;
|
||||
|
||||
// May need to kill the inter-slice GC timer
|
||||
nsJSContext::KillInterSliceGCTimer();
|
||||
// May need to kill the inter-slice GC runner
|
||||
nsJSContext::KillInterSliceGCRunner();
|
||||
|
||||
sCCollectedWaitingForGC = 0;
|
||||
sCCollectedZonesWaitingForGC = 0;
|
||||
|
@ -2212,15 +2406,11 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
|
|||
case JS::GC_SLICE_END:
|
||||
|
||||
// Schedule another GC slice if the GC has more work to do.
|
||||
nsJSContext::KillInterSliceGCTimer();
|
||||
nsJSContext::KillInterSliceGCRunner();
|
||||
if (!sShuttingDown && !aDesc.isComplete_) {
|
||||
CallCreateInstance("@mozilla.org/timer;1", &sInterSliceGCTimer);
|
||||
sInterSliceGCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::GarbageCollection));
|
||||
sInterSliceGCTimer->InitWithNamedFuncCallback(InterSliceGCTimerFired,
|
||||
nullptr,
|
||||
NS_INTERSLICE_GC_DELAY,
|
||||
nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
|
||||
"InterSliceGCTimerFired");
|
||||
sInterSliceGCRunner =
|
||||
CollectorRunner::Create(InterSliceGCRunnerFired, NS_INTERSLICE_GC_DELAY,
|
||||
sActiveIntersliceGCBudget, false);
|
||||
}
|
||||
|
||||
if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
|
||||
|
@ -2274,7 +2464,7 @@ void
|
|||
mozilla::dom::StartupJSEnvironment()
|
||||
{
|
||||
// initialize all our statics, so that we can restart XPCOM
|
||||
sGCTimer = sShrinkingGCTimer = sFullGCTimer = sCCTimer = sICCTimer = nullptr;
|
||||
sGCTimer = sShrinkingGCTimer = sFullGCTimer = nullptr;
|
||||
sCCLockedOut = false;
|
||||
sCCLockedOutTime = 0;
|
||||
sLastCCEndTime = TimeStamp();
|
||||
|
@ -2609,52 +2799,6 @@ nsJSContext::EnsureStatics()
|
|||
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*
|
||||
mozilla::dom::GetNameSpaceManager()
|
||||
{
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "nsIXPConnect.h"
|
||||
#include "nsIArray.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
|
@ -91,7 +92,7 @@ public:
|
|||
int32_t aExtraForgetSkippableCalls = 0);
|
||||
|
||||
// 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.
|
||||
static void RunCycleCollectorWorkSlice(int64_t aWorkBudget);
|
||||
|
@ -113,10 +114,10 @@ public:
|
|||
static void KillShrinkingGCTimer();
|
||||
|
||||
static void MaybePokeCC();
|
||||
static void KillCCTimer();
|
||||
static void KillICCTimer();
|
||||
static void KillCCRunner();
|
||||
static void KillICCRunner();
|
||||
static void KillFullGCTimer();
|
||||
static void KillInterSliceGCTimer();
|
||||
static void KillInterSliceGCRunner();
|
||||
|
||||
// Calling LikelyShortLivingObjectCreated() makes a GC more likely.
|
||||
static void LikelyShortLivingObjectCreated();
|
||||
|
@ -131,7 +132,6 @@ public:
|
|||
return global ? mGlobalObjectRef.get() : nullptr;
|
||||
}
|
||||
|
||||
static void NotifyDidPaint();
|
||||
protected:
|
||||
virtual ~nsJSContext();
|
||||
|
||||
|
|
|
@ -490,6 +490,7 @@ ContentChild* ContentChild::sSingleton;
|
|||
ContentChild::ContentChild()
|
||||
: mID(uint64_t(-1))
|
||||
#if defined(XP_WIN) && defined(ACCESSIBILITY)
|
||||
, mMainChromeTid(0)
|
||||
, mMsaaID(0)
|
||||
#endif
|
||||
, mCanOverrideProcessName(true)
|
||||
|
@ -1390,6 +1391,7 @@ StartMacOSContentSandbox()
|
|||
}
|
||||
|
||||
nsAutoCString tempDirPath;
|
||||
tempDir->Normalize();
|
||||
rv = tempDir->GetNativePath(tempDirPath);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_CRASH("Failed to get NS_OS_TEMP_DIR path");
|
||||
|
@ -2422,10 +2424,14 @@ ContentChild::RecvFlushMemory(const nsString& reason)
|
|||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentChild::RecvActivateA11y(const uint32_t& aMsaaID)
|
||||
ContentChild::RecvActivateA11y(const uint32_t& aMainChromeTid,
|
||||
const uint32_t& aMsaaID)
|
||||
{
|
||||
#ifdef ACCESSIBILITY
|
||||
#ifdef XP_WIN
|
||||
MOZ_ASSERT(aMainChromeTid != 0);
|
||||
mMainChromeTid = aMainChromeTid;
|
||||
|
||||
MOZ_ASSERT(aMsaaID != 0);
|
||||
mMsaaID = aMsaaID;
|
||||
#endif // XP_WIN
|
||||
|
|
|
@ -388,7 +388,8 @@ public:
|
|||
|
||||
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 RecvGarbageCollect() override;
|
||||
|
@ -499,6 +500,8 @@ public:
|
|||
ContentParentId GetID() const { return mID; }
|
||||
|
||||
#if defined(XP_WIN) && defined(ACCESSIBILITY)
|
||||
uint32_t GetChromeMainThreadId() const { return mMainChromeTid; }
|
||||
|
||||
uint32_t GetMsaaID() const { return mMsaaID; }
|
||||
#endif
|
||||
|
||||
|
@ -699,6 +702,11 @@ private:
|
|||
ContentParentId mID;
|
||||
|
||||
#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
|
||||
* generated by the chrome process.
|
||||
|
|
|
@ -1364,10 +1364,15 @@ ContentParent::Init()
|
|||
// process.
|
||||
if (nsIPresShell::IsAccessibilityActive()) {
|
||||
#if defined(XP_WIN)
|
||||
Unused <<
|
||||
SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
|
||||
#if defined(RELEASE_OR_BETA)
|
||||
// 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
|
||||
Unused << SendActivateA11y(0);
|
||||
Unused << SendActivateA11y(0, 0);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
@ -2772,10 +2777,15 @@ ContentParent::Observe(nsISupports* aSubject,
|
|||
// Make sure accessibility is running in content process when
|
||||
// accessibility gets initiated in chrome process.
|
||||
#if defined(XP_WIN)
|
||||
Unused <<
|
||||
SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
|
||||
#if defined(RELEASE_OR_BETA)
|
||||
// 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
|
||||
Unused << SendActivateA11y(0);
|
||||
Unused << SendActivateA11y(0, 0);
|
||||
#endif
|
||||
} else {
|
||||
// If possible, shut down accessibility in content process when
|
||||
|
|
|
@ -443,11 +443,13 @@ child:
|
|||
|
||||
/**
|
||||
* 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
|
||||
* that is generated by the chrome process. Only used on
|
||||
* 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).
|
||||
|
|
|
@ -303,8 +303,7 @@ SVGMarkerElement::GetPreserveAspectRatio()
|
|||
|
||||
gfx::Matrix
|
||||
SVGMarkerElement::GetMarkerTransform(float aStrokeWidth,
|
||||
float aX, float aY, float aAutoAngle,
|
||||
bool aIsStart)
|
||||
const nsSVGMark& aMark)
|
||||
{
|
||||
float scale = mEnumAttributes[MARKERUNITS].GetAnimValue() ==
|
||||
SVG_MARKERUNITS_STROKEWIDTH ? aStrokeWidth : 1.0f;
|
||||
|
@ -312,10 +311,10 @@ SVGMarkerElement::GetMarkerTransform(float aStrokeWidth,
|
|||
float angle;
|
||||
switch (mOrientType.GetAnimValueInternal()) {
|
||||
case SVG_MARKER_ORIENT_AUTO:
|
||||
angle = aAutoAngle;
|
||||
angle = aMark.angle;
|
||||
break;
|
||||
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;
|
||||
default: // SVG_MARKER_ORIENT_ANGLE
|
||||
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,
|
||||
-sin(angle) * scale, cos(angle) * scale,
|
||||
aX, aY);
|
||||
aMark.x, aMark.y);
|
||||
}
|
||||
|
||||
nsSVGViewBoxRect
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "mozilla/dom/SVGAnimatedEnumeration.h"
|
||||
|
||||
class nsSVGMarkerFrame;
|
||||
struct nsSVGMark;
|
||||
|
||||
nsresult NS_NewSVGMarkerElement(nsIContent **aResult,
|
||||
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
|
||||
|
@ -121,9 +122,7 @@ public:
|
|||
virtual bool HasValidDimensions() const override;
|
||||
|
||||
// public helpers
|
||||
gfx::Matrix GetMarkerTransform(float aStrokeWidth,
|
||||
float aX, float aY, float aAutoAngle,
|
||||
bool aIsStart);
|
||||
gfx::Matrix GetMarkerTransform(float aStrokeWidth, const nsSVGMark& aMark);
|
||||
nsSVGViewBoxRect GetViewBoxRect();
|
||||
gfx::Matrix GetViewBoxTransform();
|
||||
|
||||
|
|
|
@ -894,6 +894,11 @@ public:
|
|||
* normally return the same SourceSurface object.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,9 +9,162 @@
|
|||
|
||||
#include "DrawTargetCapture.h"
|
||||
|
||||
#ifdef BUILD_ARM_NEON
|
||||
#include "mozilla/arm.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
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>
|
||||
DrawTarget::CreateCaptureDT(const IntSize& aSize)
|
||||
{
|
||||
|
@ -64,6 +217,51 @@ DrawTarget::StrokeGlyphs(ScaledFont* aFont,
|
|||
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 mozilla
|
||||
|
|
|
@ -99,6 +99,38 @@ DrawTargetD2D1::Snapshot()
|
|||
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,
|
||||
// this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
|
||||
// are expensive though, especially relatively when little work is done, so
|
||||
|
@ -348,20 +380,48 @@ DrawTargetD2D1::MaskSurface(const Pattern &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
|
||||
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 dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height));
|
||||
RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha);
|
||||
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)
|
||||
{
|
||||
RefPtr<ID2D1Image> image;
|
||||
|
||||
switch (aSurface->GetType()) {
|
||||
case SurfaceType::D2D1_1_IMAGE:
|
||||
{
|
||||
|
|
|
@ -36,6 +36,8 @@ public:
|
|||
virtual DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; }
|
||||
virtual BackendType GetBackendType() const override { return BackendType::DIRECT2D1_1; }
|
||||
virtual already_AddRefed<SourceSurface> Snapshot() override;
|
||||
virtual already_AddRefed<SourceSurface> IntoLuminanceSource(LuminanceType aLuminanceType,
|
||||
float aOpacity) override;
|
||||
virtual IntSize GetSize() override { return mSize; }
|
||||
|
||||
virtual void Flush() override;
|
||||
|
@ -295,6 +297,10 @@ private:
|
|||
static IDWriteFactory *mDWriteFactory;
|
||||
// This value is uesed to verify if the DrawTarget is created by a stale device.
|
||||
uint32_t mDeviceSeq;
|
||||
|
||||
// List of effects we use
|
||||
void EnsureLuminanceEffect();
|
||||
RefPtr<ID2D1Effect> mLuminanceEffect;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -278,6 +278,12 @@ enum class SamplingBounds : int8_t {
|
|||
BOUNDED
|
||||
};
|
||||
|
||||
// Moz2d version for SVG mask types
|
||||
enum class LuminanceType : int8_t {
|
||||
LUMINANCE,
|
||||
LINEARRGB,
|
||||
};
|
||||
|
||||
/* Color is stored in non-premultiplied form */
|
||||
struct Color
|
||||
{
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
#include "nsRect.h"
|
||||
#include "nspr.h"
|
||||
#include "png.h"
|
||||
|
||||
#include "RasterImage.h"
|
||||
#include "SurfaceCache.h"
|
||||
#include "SurfacePipeFactory.h"
|
||||
#include "mozilla/DebugOnly.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
|
||||
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()) {
|
||||
// Setting the size led to an 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 (frameRect.height < INT32_MAX / (frameRect.width * int32_t(channels))) {
|
||||
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));
|
||||
}
|
||||
if (!decoder->interlacebuf) {
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
|
||||
#define INITGUID
|
||||
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/mscom/DispatchForwarder.h"
|
||||
#include "mozilla/mscom/Interceptor.h"
|
||||
#include "mozilla/mscom/InterceptorLog.h"
|
||||
#include "mozilla/mscom/MainThreadInvoker.h"
|
||||
#include "mozilla/mscom/Objref.h"
|
||||
#include "mozilla/mscom/Registration.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "MainThreadUtils.h"
|
||||
|
@ -20,6 +22,7 @@
|
|||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
@ -142,6 +145,7 @@ Interceptor::GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
|
|||
aDestContext == MSHCTX_DIFFERENTMACHINE) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mEventSink);
|
||||
return mEventSink->GetHandler(WrapNotNull(aHandlerClsid));
|
||||
}
|
||||
|
@ -177,12 +181,60 @@ Interceptor::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
|
|||
DWORD dwDestContext, void* pvDestContext,
|
||||
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);
|
||||
if (FAILED(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));
|
||||
}
|
||||
|
||||
|
|
|
@ -453,6 +453,7 @@ MainThreadHandoff::GetHandler(NotNull<CLSID*> aHandlerClsid)
|
|||
if (!mHandlerProvider) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
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
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
#if defined(ACCESSIBILITY)
|
||||
#include "mozilla/mscom/Registration.h"
|
||||
#if defined(MOZILLA_INTERNAL_API)
|
||||
#include "nsTArray.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
@ -139,6 +141,8 @@ IsVtableIndexFromParentInterface(REFIID aInterface, unsigned long aVtableIndex)
|
|||
return result;
|
||||
}
|
||||
|
||||
#if defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
bool
|
||||
IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
|
||||
unsigned long aVtableIndexHint)
|
||||
|
@ -223,6 +227,8 @@ IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
|
|||
return false;
|
||||
}
|
||||
|
||||
#endif // defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
#endif // defined(ACCESSIBILITY)
|
||||
|
||||
} // namespace mscom
|
||||
|
|
|
@ -11,9 +11,7 @@
|
|||
#include "nsString.h"
|
||||
#endif // defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
#if defined(ACCESSIBILITY)
|
||||
#include <guiddef.h>
|
||||
#endif // defined(ACCESSIBILITY)
|
||||
|
||||
struct IUnknown;
|
||||
|
||||
|
@ -31,8 +29,10 @@ void GUIDToString(REFGUID aGuid, nsAString& aOutString);
|
|||
#if defined(ACCESSIBILITY)
|
||||
bool IsVtableIndexFromParentInterface(REFIID aInterface,
|
||||
unsigned long aVtableIndex);
|
||||
#if defined(MOZILLA_INTERNAL_API)
|
||||
bool IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
|
||||
unsigned long aVtableIndexHint);
|
||||
#endif // defined(MOZILLA_INTERNAL_API)
|
||||
#endif // defined(ACCESSIBILITY)
|
||||
|
||||
} // namespace mscom
|
||||
|
|
|
@ -12,6 +12,7 @@ EXPORTS.mozilla.mscom += [
|
|||
'COMPtrHolder.h',
|
||||
'EnsureMTA.h',
|
||||
'MainThreadRuntime.h',
|
||||
'Objref.h',
|
||||
'ProxyStream.h',
|
||||
'Ptr.h',
|
||||
'Utils.h',
|
||||
|
@ -21,6 +22,7 @@ UNIFIED_SOURCES += [
|
|||
'AgileReference.cpp',
|
||||
'EnsureMTA.cpp',
|
||||
'MainThreadRuntime.cpp',
|
||||
'Objref.cpp',
|
||||
'ProxyStream.cpp',
|
||||
'Utils.cpp',
|
||||
]
|
||||
|
@ -70,6 +72,8 @@ LOCAL_INCLUDES += [
|
|||
'/xpcom/build',
|
||||
]
|
||||
|
||||
DEFINES['MOZ_MSCOM_REMARSHAL_NO_HANDLER'] = True
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/mscom/Objref.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
|
||||
#include <objbase.h>
|
||||
|
@ -48,7 +49,7 @@ Handler::Handler(IUnknown* aOuter, HRESULT* aResult)
|
|||
return;
|
||||
}
|
||||
|
||||
// mInnerMarshal is a weak ref
|
||||
// mUnmarshal is a weak ref
|
||||
mUnmarshal->Release();
|
||||
}
|
||||
|
||||
|
@ -169,14 +170,28 @@ Handler::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
|
|||
RefPtr<IUnknown> unkToMarshal;
|
||||
HRESULT hr;
|
||||
|
||||
REFIID marshalAs = MarshalAs(riid);
|
||||
if (marshalAs == riid) {
|
||||
unkToMarshal = static_cast<IUnknown*>(pv);
|
||||
} else {
|
||||
hr = mInnerUnk->QueryInterface(marshalAs, getter_AddRefs(unkToMarshal));
|
||||
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
||||
LARGE_INTEGER seekTo;
|
||||
seekTo.QuadPart = 0;
|
||||
|
||||
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)) {
|
||||
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(),
|
||||
|
@ -185,6 +200,22 @@ Handler::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
|
|||
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()) {
|
||||
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,
|
||||
// we must re-serialize it.
|
||||
return WriteHandlerPayload(pStm, marshalAs);
|
||||
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
||||
}
|
||||
|
||||
HRESULT
|
||||
|
|
|
@ -8,8 +8,10 @@ Library('mscom_oop')
|
|||
|
||||
SOURCES += [
|
||||
'../ActivationContext.cpp',
|
||||
'../Objref.cpp',
|
||||
'../Registration.cpp',
|
||||
'../StructStream.cpp',
|
||||
'../Utils.cpp',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
@ -26,6 +28,7 @@ OS_LIBS += [
|
|||
LIBRARY_DEFINES['UNICODE'] = True
|
||||
LIBRARY_DEFINES['_UNICODE'] = True
|
||||
LIBRARY_DEFINES['MOZ_NO_MOZALLOC'] = True
|
||||
LIBRARY_DEFINES['MOZ_MSCOM_REMARSHAL_NO_HANDLER'] = True
|
||||
|
||||
DISABLE_STL_WRAPPING = True
|
||||
NO_EXPAND_LIBS = True
|
||||
|
|
|
@ -341,6 +341,7 @@ class JSFunction : public js::NativeObject
|
|||
MOZ_ASSERT(atom);
|
||||
MOZ_ASSERT(!hasGuessedAtom());
|
||||
MOZ_ASSERT(!isClassConstructor());
|
||||
MOZ_ASSERT(js::AtomIsMarked(zone(), atom));
|
||||
atom_ = atom;
|
||||
flags_ |= HAS_COMPILE_TIME_NAME;
|
||||
}
|
||||
|
@ -355,6 +356,7 @@ class JSFunction : public js::NativeObject
|
|||
MOZ_ASSERT(atom);
|
||||
MOZ_ASSERT(!hasCompileTimeName());
|
||||
MOZ_ASSERT(!hasGuessedAtom());
|
||||
MOZ_ASSERT(js::AtomIsMarked(zone(), atom));
|
||||
atom_ = atom;
|
||||
flags_ |= HAS_GUESSED_ATOM;
|
||||
}
|
||||
|
|
|
@ -266,9 +266,10 @@ DeleteScopeData(ConcreteScopeData* data)
|
|||
template <typename ConcreteScope, XDRMode mode>
|
||||
/* static */ bool
|
||||
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(!*lengthOut);
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
|
||||
|
@ -281,11 +282,13 @@ Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
|
|||
if (mode == XDR_ENCODE) {
|
||||
data.set(&scope->data());
|
||||
} else {
|
||||
if (length) {
|
||||
data.set(NewEmptyScopeData<ConcreteScope>(cx, length).release());
|
||||
if (!data)
|
||||
return false;
|
||||
data->length = length;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
if (!XDRBindingName(xdr, &data->names[i])) {
|
||||
|
@ -298,6 +301,7 @@ Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
|
|||
}
|
||||
}
|
||||
|
||||
*lengthOut = length;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -559,7 +563,8 @@ LexicalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
|||
JSContext* cx = xdr->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;
|
||||
|
||||
{
|
||||
|
@ -583,6 +588,9 @@ LexicalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
|||
return false;
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
scope.set(create(cx, kind, data, firstFrameSlot, enclosing));
|
||||
if (!scope)
|
||||
return false;
|
||||
|
@ -743,7 +751,8 @@ FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosin
|
|||
{
|
||||
JSContext* cx = xdr->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;
|
||||
|
||||
{
|
||||
|
@ -754,30 +763,35 @@ FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosin
|
|||
|
||||
uint8_t needsEnvironment;
|
||||
uint8_t hasParameterExprs;
|
||||
uint16_t nonPositionalFormalStart;
|
||||
uint16_t varStart;
|
||||
uint32_t nextFrameSlot;
|
||||
if (mode == XDR_ENCODE) {
|
||||
needsEnvironment = scope->hasEnvironment();
|
||||
hasParameterExprs = data->hasParameterExprs;
|
||||
nonPositionalFormalStart = data->nonPositionalFormalStart;
|
||||
varStart = data->varStart;
|
||||
nextFrameSlot = data->nextFrameSlot;
|
||||
}
|
||||
if (!xdr->codeUint8(&needsEnvironment))
|
||||
return false;
|
||||
if (!xdr->codeUint8(&hasParameterExprs))
|
||||
return false;
|
||||
if (!xdr->codeUint16(&data->nonPositionalFormalStart))
|
||||
if (!xdr->codeUint16(&nonPositionalFormalStart))
|
||||
return false;
|
||||
if (!xdr->codeUint16(&data->varStart))
|
||||
if (!xdr->codeUint16(&varStart))
|
||||
return false;
|
||||
if (!xdr->codeUint32(&nextFrameSlot))
|
||||
return false;
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
if (!data->length) {
|
||||
MOZ_ASSERT(!data->nonPositionalFormalStart);
|
||||
MOZ_ASSERT(!data->varStart);
|
||||
MOZ_ASSERT(!data->nextFrameSlot);
|
||||
DeleteScopeData(data.get());
|
||||
data = nullptr;
|
||||
MOZ_ASSERT(!length == !data);
|
||||
if (length) {
|
||||
data->nonPositionalFormalStart = nonPositionalFormalStart;
|
||||
data->varStart = varStart;
|
||||
} else {
|
||||
MOZ_ASSERT(!nonPositionalFormalStart);
|
||||
MOZ_ASSERT(!varStart);
|
||||
}
|
||||
|
||||
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.
|
||||
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();
|
||||
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;
|
||||
|
||||
{
|
||||
|
@ -896,12 +912,7 @@ VarScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
|||
return false;
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
if (!data->length) {
|
||||
MOZ_ASSERT(!data->nextFrameSlot);
|
||||
DeleteScopeData(data.get());
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!length == !data);
|
||||
scope.set(create(cx, kind, data, firstFrameSlot, needsEnvironment, enclosing));
|
||||
if (!scope)
|
||||
return false;
|
||||
|
@ -974,7 +985,8 @@ GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope)
|
|||
|
||||
JSContext* cx = xdr->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;
|
||||
|
||||
{
|
||||
|
@ -983,20 +995,31 @@ GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope)
|
|||
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;
|
||||
if (!xdr->codeUint32(&data->letStart))
|
||||
if (!xdr->codeUint32(&letStart))
|
||||
return false;
|
||||
if (!xdr->codeUint32(&data->constStart))
|
||||
if (!xdr->codeUint32(&constStart))
|
||||
return false;
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
if (!data->length) {
|
||||
MOZ_ASSERT(!data->varStart);
|
||||
MOZ_ASSERT(!data->letStart);
|
||||
MOZ_ASSERT(!data->constStart);
|
||||
DeleteScopeData(data.get());
|
||||
data = nullptr;
|
||||
MOZ_ASSERT(!length == !data);
|
||||
if (length) {
|
||||
data->varStart = varStart;
|
||||
data->letStart = letStart;
|
||||
data->constStart = constStart;
|
||||
} else {
|
||||
MOZ_ASSERT(!varStart);
|
||||
MOZ_ASSERT(!letStart);
|
||||
MOZ_ASSERT(!constStart);
|
||||
}
|
||||
|
||||
scope.set(create(cx, kind, data));
|
||||
|
@ -1106,16 +1129,12 @@ EvalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
|||
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;
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
if (!data->length) {
|
||||
MOZ_ASSERT(!data->nextFrameSlot);
|
||||
DeleteScopeData(data.get());
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!length == !data);
|
||||
scope.set(create(cx, kind, data, enclosing));
|
||||
if (!scope)
|
||||
return false;
|
||||
|
|
|
@ -248,7 +248,8 @@ class Scope : public js::gc::TenuredCell
|
|||
|
||||
template <typename ConcreteScope, XDRMode mode>
|
||||
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);
|
||||
|
||||
|
|
|
@ -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
|
||||
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) {
|
||||
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
|
||||
|
||||
|
@ -1998,7 +2038,7 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
|||
timelines->AddMarkerForDocShell(docShell, "Paint", MarkerTracingType::END);
|
||||
}
|
||||
|
||||
notifyGC = true;
|
||||
dispatchRunnablesAfterTick = true;
|
||||
}
|
||||
|
||||
#ifndef ANDROID /* bug 1142079 */
|
||||
|
@ -2017,10 +2057,14 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
|||
ScheduleViewManagerFlush();
|
||||
}
|
||||
|
||||
if (notifyGC && nsContentUtils::XPConnect()) {
|
||||
GeckoProfilerTracingRAII tracer("Paint", "NotifyDidPaint");
|
||||
nsContentUtils::XPConnect()->NotifyDidPaint();
|
||||
nsJSContext::NotifyDidPaint();
|
||||
if (dispatchRunnablesAfterTick && sPendingIdleRunnables) {
|
||||
AutoTArray<RunnableWithDelay, 8>* runnables = sPendingIdleRunnables;
|
||||
sPendingIdleRunnables = nullptr;
|
||||
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 nsIDOMEvent;
|
||||
class nsINode;
|
||||
class nsIRunnable;
|
||||
|
||||
namespace mozilla {
|
||||
class RefreshDriverTimer;
|
||||
|
@ -333,6 +334,10 @@ public:
|
|||
*/
|
||||
static mozilla::TimeStamp GetIdleDeadlineHint(mozilla::TimeStamp aDefault);
|
||||
|
||||
static void DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
|
||||
uint32_t aDelay);
|
||||
static void CancelIdleRunnable(nsIRunnable* aRunnable);
|
||||
|
||||
bool SkippedPaints() const
|
||||
{
|
||||
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-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-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-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-3d.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) == 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-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-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
|
||||
fuzzy-if(skiaContent,71,203) == mask-image-3i.html mask-image-3-ref.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");
|
||||
|
||||
for (uint32_t i = 0; i < num; i++) {
|
||||
nsSVGMark& mark = marks[i];
|
||||
const nsSVGMark& mark = marks[i];
|
||||
nsSVGMarkerFrame* frame = markerFrames[mark.type];
|
||||
if (frame) {
|
||||
SVGBBox mbbox =
|
||||
frame->GetMarkBBoxContribution(aToBBoxUserspace, aFlags, this,
|
||||
&marks[i], strokeWidth);
|
||||
mark, strokeWidth);
|
||||
MOZ_ASSERT(mbbox.IsFinite(), "bbox is about to go bad");
|
||||
bbox.UnionEdges(mbbox);
|
||||
}
|
||||
|
@ -896,10 +896,10 @@ SVGGeometryFrame::PaintMarkers(gfxContext& aContext,
|
|||
"Number of Marker frames should be equal to eTypeCount");
|
||||
|
||||
for (uint32_t i = 0; i < num; i++) {
|
||||
nsSVGMark& mark = marks[i];
|
||||
const nsSVGMark& mark = marks[i];
|
||||
nsSVGMarkerFrame* frame = markerFrames[mark.type];
|
||||
if (frame) {
|
||||
frame->PaintMark(aContext, aTransform, this, &mark, strokeWidth,
|
||||
frame->PaintMark(aContext, aTransform, this, mark, strokeWidth,
|
||||
aImgParams);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,10 +66,6 @@ UNIFIED_SOURCES += [
|
|||
'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'
|
||||
LOCAL_INCLUDES += [
|
||||
'../../widget',
|
||||
|
|
|
@ -80,11 +80,9 @@ nsSVGMarkerFrame::GetCanvasTM()
|
|||
gfxMatrix markedTM = mMarkedFrame->GetCanvasTM();
|
||||
mInUse2 = false;
|
||||
|
||||
Matrix markerTM = content->GetMarkerTransform(mStrokeWidth, mX, mY,
|
||||
mAutoAngle, mIsStart);
|
||||
Matrix viewBoxTM = content->GetViewBoxTransform();
|
||||
|
||||
return ThebesMatrix(viewBoxTM * markerTM) * markedTM;
|
||||
return ThebesMatrix(viewBoxTM * mMarkerTM) * markedTM;
|
||||
}
|
||||
|
||||
static nsIFrame*
|
||||
|
@ -99,8 +97,8 @@ GetAnonymousChildFrame(nsIFrame* aFrame)
|
|||
void
|
||||
nsSVGMarkerFrame::PaintMark(gfxContext& aContext,
|
||||
const gfxMatrix& aToMarkedFrameUserSpace,
|
||||
SVGGeometryFrame *aMarkedFrame,
|
||||
nsSVGMark *aMark, float aStrokeWidth,
|
||||
SVGGeometryFrame* aMarkedFrame,
|
||||
const nsSVGMark& aMark, float aStrokeWidth,
|
||||
imgDrawingParams& aImgParams)
|
||||
{
|
||||
// If the flag is set when we get here, it means this marker frame
|
||||
|
@ -124,18 +122,11 @@ nsSVGMarkerFrame::PaintMark(gfxContext& aContext,
|
|||
return;
|
||||
}
|
||||
|
||||
mStrokeWidth = aStrokeWidth;
|
||||
mX = aMark->x;
|
||||
mY = aMark->y;
|
||||
mAutoAngle = aMark->angle;
|
||||
mIsStart = aMark->type == nsSVGMark::eStart;
|
||||
|
||||
Matrix viewBoxTM = marker->GetViewBoxTransform();
|
||||
|
||||
Matrix markerTM = marker->GetMarkerTransform(mStrokeWidth, mX, mY,
|
||||
mAutoAngle, mIsStart);
|
||||
mMarkerTM = marker->GetMarkerTransform(aStrokeWidth, aMark);
|
||||
|
||||
gfxMatrix markTM = ThebesMatrix(viewBoxTM) * ThebesMatrix(markerTM) *
|
||||
gfxMatrix markTM = ThebesMatrix(viewBoxTM) * ThebesMatrix(mMarkerTM) *
|
||||
aToMarkedFrameUserSpace;
|
||||
|
||||
if (StyleDisplay()->IsScrollableOverflow()) {
|
||||
|
@ -158,10 +149,10 @@ nsSVGMarkerFrame::PaintMark(gfxContext& aContext,
|
|||
}
|
||||
|
||||
SVGBBox
|
||||
nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix &aToBBoxUserspace,
|
||||
nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix& aToBBoxUserspace,
|
||||
uint32_t aFlags,
|
||||
SVGGeometryFrame *aMarkedFrame,
|
||||
const nsSVGMark *aMark,
|
||||
SVGGeometryFrame* aMarkedFrame,
|
||||
const nsSVGMark& aMark,
|
||||
float aStrokeWidth)
|
||||
{
|
||||
SVGBBox bbox;
|
||||
|
@ -185,17 +176,10 @@ nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix &aToBBoxUserspace,
|
|||
return bbox;
|
||||
}
|
||||
|
||||
mStrokeWidth = aStrokeWidth;
|
||||
mX = aMark->x;
|
||||
mY = aMark->y;
|
||||
mAutoAngle = aMark->angle;
|
||||
mIsStart = aMark->type == nsSVGMark::eStart;
|
||||
|
||||
Matrix markerTM =
|
||||
content->GetMarkerTransform(mStrokeWidth, mX, mY, mAutoAngle, mIsStart);
|
||||
mMarkerTM = content->GetMarkerTransform(aStrokeWidth, aMark);
|
||||
Matrix viewBoxTM = content->GetViewBoxTransform();
|
||||
|
||||
Matrix tm = viewBoxTM * markerTM * aToBBoxUserspace;
|
||||
Matrix tm = viewBoxTM * mMarkerTM * aToBBoxUserspace;
|
||||
|
||||
nsSVGDisplayableFrame* child = do_QueryFrame(GetAnonymousChildFrame(this));
|
||||
// When we're being called to obtain the invalidation area, we need to
|
||||
|
|
|
@ -79,15 +79,15 @@ public:
|
|||
// nsSVGMarkerFrame methods:
|
||||
void PaintMark(gfxContext& aContext,
|
||||
const gfxMatrix& aToMarkedFrameUserSpace,
|
||||
mozilla::SVGGeometryFrame *aMarkedFrame,
|
||||
nsSVGMark *aMark,
|
||||
mozilla::SVGGeometryFrame* aMarkedFrame,
|
||||
const nsSVGMark& aMark,
|
||||
float aStrokeWidth,
|
||||
imgDrawingParams& aImgParams);
|
||||
|
||||
SVGBBox GetMarkBBoxContribution(const Matrix &aToBBoxUserspace,
|
||||
SVGBBox GetMarkBBoxContribution(const Matrix& aToBBoxUserspace,
|
||||
uint32_t aFlags,
|
||||
mozilla::SVGGeometryFrame *aMarkedFrame,
|
||||
const nsSVGMark *aMark,
|
||||
mozilla::SVGGeometryFrame* aMarkedFrame,
|
||||
const nsSVGMark& aMark,
|
||||
float aStrokeWidth);
|
||||
|
||||
// Update the style on our anonymous box child.
|
||||
|
@ -98,8 +98,7 @@ public:
|
|||
private:
|
||||
// stuff needed for callback
|
||||
mozilla::SVGGeometryFrame *mMarkedFrame;
|
||||
float mStrokeWidth, mX, mY, mAutoAngle;
|
||||
bool mIsStart; // whether the callback is for a marker-start marker
|
||||
Matrix mMarkerTM;
|
||||
|
||||
// nsSVGContainerFrame methods:
|
||||
virtual gfxMatrix GetCanvasTM() override;
|
||||
|
|
|
@ -14,162 +14,28 @@
|
|||
#include "mozilla/RefPtr.h"
|
||||
#include "nsSVGEffects.h"
|
||||
#include "mozilla/dom/SVGMaskElement.h"
|
||||
#ifdef BUILD_ARM_NEON
|
||||
#include "mozilla/arm.h"
|
||||
#include "nsSVGMaskFrameNEON.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::image;
|
||||
|
||||
// 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)
|
||||
static LuminanceType
|
||||
GetLuminanceType(uint8_t aNSMaskType)
|
||||
{
|
||||
#ifdef BUILD_ARM_NEON
|
||||
if (mozilla::supports_neon()) {
|
||||
ComputesRGBLuminanceMask_NEON(aSourceData, aSourceStride,
|
||||
aDestData, aDestStride,
|
||||
aSize, aOpacity);
|
||||
return;
|
||||
switch (aNSMaskType) {
|
||||
case NS_STYLE_MASK_TYPE_LUMINANCE:
|
||||
return LuminanceType::LUMINANCE;
|
||||
case NS_STYLE_COLOR_INTERPOLATION_LINEARRGB:
|
||||
return LuminanceType::LINEARRGB;
|
||||
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*
|
||||
NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
||||
{
|
||||
|
@ -258,44 +124,20 @@ nsSVGMaskFrame::GetMaskForMaskedFrame(MaskParams& aParams)
|
|||
nsSVGUtils::PaintFrameWithEffects(kid, *tmpCtx, m, aParams.imgParams);
|
||||
}
|
||||
|
||||
if (StyleSVG()->mColorInterpolation ==
|
||||
NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) {
|
||||
maskType = NS_STYLE_COLOR_INTERPOLATION_LINEARRGB;
|
||||
}
|
||||
|
||||
RefPtr<SourceSurface> surface;
|
||||
if (maskType == NS_STYLE_MASK_TYPE_LUMINANCE) {
|
||||
RefPtr<SourceSurface> maskSnapshot = maskDT->Snapshot();
|
||||
RefPtr<SourceSurface> maskSnapshot =
|
||||
maskDT->IntoLuminanceSource(GetLuminanceType(maskType),
|
||||
aParams.opacity);
|
||||
if (!maskSnapshot) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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();
|
||||
surface = maskSnapshot.forget();
|
||||
} else {
|
||||
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));
|
||||
|
|
|
@ -16,21 +16,6 @@
|
|||
|
||||
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
|
||||
{
|
||||
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
|
||||
// create substatial DOM garbage, the CC tends not to run enough normally.
|
||||
++gTestCount;
|
||||
if (gTestCount % 1000 == 0) {
|
||||
if (gTestCount % 250 == 0) {
|
||||
CU.forceGC();
|
||||
CU.forceCC();
|
||||
}
|
||||
|
|
|
@ -8,12 +8,15 @@
|
|||
#define MOZ_EMBEDDED_LIBPNG
|
||||
|
||||
/* Limit image dimensions (bug #251381, #591822, #967656, and #1283961) */
|
||||
#define PNG_USER_LIMITS_SUPPORTED
|
||||
#ifndef MOZ_PNG_MAX_WIDTH
|
||||
# define MOZ_PNG_MAX_WIDTH 0x7fffffffL /* Unlimited */
|
||||
#endif
|
||||
#ifndef MOZ_PNG_MAX_HEIGHT
|
||||
# define MOZ_PNG_MAX_HEIGHT 0x7fffffffL /* Unlimited */
|
||||
#endif
|
||||
/* but allow nsPNGDecoder to override the limits (bug #1368407) */
|
||||
#define PNG_SET_USER_LIMITS_SUPPORTED
|
||||
|
||||
#define PNG_API_RULE 0
|
||||
#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.gc_per_zone", 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.log", false);
|
||||
pref("javascript.options.mem.notify", false);
|
||||
|
|
|
@ -5,48 +5,34 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Get bookmark service
|
||||
try {
|
||||
var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
|
||||
getService(Ci.nsINavBookmarksService);
|
||||
} catch (ex) {
|
||||
do_throw("Could not get nav-bookmarks-service\n");
|
||||
}
|
||||
let bm = PlacesUtils.bookmarks;
|
||||
|
||||
var gTestRoot;
|
||||
var gURI;
|
||||
var gItemId1;
|
||||
var gItemId2;
|
||||
// Test that Bookmarks fetch properly orders its results based on
|
||||
// the last modified value. Note we cannot rely on dateAdded due to
|
||||
// the low PR_Now() resolution.
|
||||
|
||||
// main
|
||||
function run_test() {
|
||||
gURI = uri("http://foo.tld.com/");
|
||||
gTestRoot = bmsvc.createFolder(bmsvc.placesRoot, "test folder",
|
||||
bmsvc.DEFAULT_INDEX);
|
||||
add_task(async function sort_bookmark_by_relevance() {
|
||||
let now = new Date();
|
||||
let modifiedTime = new Date(now.setHours(now.getHours() - 2));
|
||||
|
||||
// test getBookmarkIdsForURI
|
||||
// getBookmarkIdsForURI sorts by the most recently added/modified (descending)
|
||||
//
|
||||
// we cannot rely on dateAdded growing when doing so in a simple iteration,
|
||||
// see PR_Now() documentation
|
||||
do_test_pending();
|
||||
let url = "http://foo.tld.com/";
|
||||
let parentGuid = (await bm.insert({type: bm.TYPE_FOLDER,
|
||||
title: "test folder",
|
||||
parentGuid: bm.unfiledGuid})).guid;
|
||||
let item1Guid = (await bm.insert({url,
|
||||
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, "");
|
||||
do_timeout(100, phase2);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
let bms1 = [];
|
||||
await bm.fetch({url}, bm2 => bms1.push(bm2));
|
||||
Assert.equal(bms1[0].guid, item2Guid);
|
||||
Assert.equal(bms1[1].guid, item1Guid);
|
||||
});
|
||||
|
|
|
@ -527,7 +527,11 @@ add_task(async function test_subprocess_pathSearch() {
|
|||
|
||||
add_task(async function test_subprocess_workdir() {
|
||||
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,
|
||||
"Current process directory must not be the current temp directory");
|
||||
|
|
|
@ -5019,9 +5019,7 @@ MultiprocessBlockPolicy() {
|
|||
return gMultiprocessBlockPolicy;
|
||||
}
|
||||
|
||||
#if defined(XP_WIN)
|
||||
// These checks are currently only in use under WinXP
|
||||
if (false) { // !IsVistaOrLater()
|
||||
#if defined(XP_WIN) && defined(RELEASE_OR_BETA)
|
||||
bool disabledForA11y = false;
|
||||
/**
|
||||
* Avoids enabling e10s if accessibility has recently loaded. Performs the
|
||||
|
@ -5049,11 +5047,11 @@ MultiprocessBlockPolicy() {
|
|||
disabledForA11y = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (disabledForA11y) {
|
||||
gMultiprocessBlockPolicy = kE10sDisabledForAccessibility;
|
||||
return gMultiprocessBlockPolicy;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
|
@ -28,6 +28,7 @@ void AddQuarantineMetadataToFile(const CFStringRef filePath,
|
|||
const CFURLRef sourceURL,
|
||||
const CFURLRef referrerURL,
|
||||
const bool isFromWeb);
|
||||
CFURLRef GetTemporaryFolderCFURLRef();
|
||||
|
||||
} // namespace CocoaFileUtils
|
||||
|
||||
|
|
|
@ -257,4 +257,11 @@ void AddQuarantineMetadataToFile(const CFStringRef filePath,
|
|||
::CFRelease(mutQuarantineProps);
|
||||
}
|
||||
|
||||
CFURLRef GetTemporaryFolderCFURLRef()
|
||||
{
|
||||
NSString* tempDir = ::NSTemporaryDirectory();
|
||||
return tempDir == nil ? NULL : (CFURLRef)[NSURL fileURLWithPath:tempDir
|
||||
isDirectory:YES];
|
||||
}
|
||||
|
||||
} // namespace CocoaFileUtils
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
#include <stdlib.h>
|
||||
#include <sys/param.h>
|
||||
#include "prenv.h"
|
||||
#if defined(MOZ_WIDGET_COCOA)
|
||||
#include "CocoaFileUtils.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -766,10 +769,20 @@ GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
|
|||
nsresult
|
||||
GetOSXFolderType(short aDomain, OSType aFolderType, nsIFile** aLocalFile)
|
||||
{
|
||||
OSErr err;
|
||||
FSRef fsRef;
|
||||
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);
|
||||
if (err == noErr) {
|
||||
NS_NewLocalFile(EmptyString(), true, aLocalFile);
|
||||
|
|
|
@ -25,6 +25,10 @@ function test_normalized_vs_non_normalized()
|
|||
if (!exists)
|
||||
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
|
||||
var tmp2 = new LocalFile(tmp1.path);
|
||||
do_check_true(tmp1.equals(tmp2));
|
||||
|
|
Загрузка…
Ссылка в новой задаче