mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-14 04:03:47 +00:00
merge mozilla-central to autoland. r=merge a=merge
This commit is contained in:
commit
3c8747ae61
10
.cron.yml
10
.cron.yml
@ -102,7 +102,13 @@ jobs:
|
||||
job:
|
||||
type: decision-task
|
||||
treeherder-symbol: Nfile
|
||||
target-tasks-method: nightly_file_update
|
||||
target-tasks-method: file_update
|
||||
run-on-projects:
|
||||
- mozilla-central
|
||||
when: [] # don't run for now due to unforeseen issues
|
||||
when:
|
||||
by-project:
|
||||
# No default branch
|
||||
mozilla-central:
|
||||
# Buildbot start time is 10:02am UTC, until we are able to
|
||||
# disable buildbot scheduling, use +12h
|
||||
- {hour: 22, minute: 0}
|
||||
|
@ -34,7 +34,7 @@ ProxyAccessible::GetCOMInterface(void** aOutAccessible) const
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mCOMProxy) {
|
||||
if (!mCOMProxy && mSafeToRecurse) {
|
||||
// See if we can lazily obtain a COM proxy
|
||||
AccessibleWrap* wrap = WrapperFor(this);
|
||||
bool isDefunct = false;
|
||||
|
@ -27,6 +27,7 @@ public:
|
||||
ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
|
||||
DocAccessibleParent* aDoc, role aRole, uint32_t aInterfaces)
|
||||
: ProxyAccessibleBase(aID, aParent, aDoc, aRole, aInterfaces)
|
||||
, mSafeToRecurse(true)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ProxyAccessible);
|
||||
}
|
||||
@ -40,7 +41,16 @@ public:
|
||||
|
||||
bool GetCOMInterface(void** aOutAccessible) const;
|
||||
void SetCOMInterface(const RefPtr<IAccessible>& aIAccessible)
|
||||
{ mCOMProxy = aIAccessible; }
|
||||
{
|
||||
if (aIAccessible) {
|
||||
mCOMProxy = aIAccessible;
|
||||
} else {
|
||||
// If we were supposed to be receiving an interface (hence the call to
|
||||
// this function), but the interface turns out to be null, then we're
|
||||
// broken for some reason.
|
||||
mSafeToRecurse = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit ProxyAccessible(DocAccessibleParent* aThisAsDoc)
|
||||
@ -49,6 +59,7 @@ protected:
|
||||
|
||||
private:
|
||||
RefPtr<IAccessible> mCOMProxy;
|
||||
bool mSafeToRecurse;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -323,6 +323,7 @@ toolbarpaletteitem {
|
||||
#main-window[inFullscreen][inDOMFullscreen] #fullscr-toggler,
|
||||
#main-window[inFullscreen][inDOMFullscreen] #sidebar-box,
|
||||
#main-window[inFullscreen][inDOMFullscreen] #sidebar-splitter,
|
||||
#main-window[inFullscreen][inDOMFullscreen] #developer-toolbar,
|
||||
#main-window[inFullscreen]:not([OSXLionFullscreen]) toolbar:not([fullscreentoolbar=true]),
|
||||
#main-window[inFullscreen] #global-notificationbox,
|
||||
#main-window[inFullscreen] #high-priority-global-notificationbox {
|
||||
|
@ -572,10 +572,6 @@
|
||||
<hbox id="titlebar-content">
|
||||
<spacer id="titlebar-spacer" flex="1"/>
|
||||
<hbox id="titlebar-buttonbox-container">
|
||||
#ifdef XP_WIN
|
||||
<button class="accessibility-indicator" tooltiptext="&accessibilityIndicator.tooltip;" aria-live="polite"/>
|
||||
<hbox class="private-browsing-indicator"/>
|
||||
#endif
|
||||
<hbox id="titlebar-buttonbox">
|
||||
<toolbarbutton class="titlebar-button" id="titlebar-min" oncommand="window.minimize();"/>
|
||||
<toolbarbutton class="titlebar-button" id="titlebar-max" oncommand="onTitlebarMaxClick();"/>
|
||||
|
@ -5399,10 +5399,6 @@
|
||||
onget="return this.mCurrentBrowser.contentViewerEdit;"
|
||||
readonly="true"/>
|
||||
|
||||
<property name="contentViewerFile"
|
||||
onget="return this.mCurrentBrowser.contentViewerFile;"
|
||||
readonly="true"/>
|
||||
|
||||
<property name="contentDocument"
|
||||
onget="return this.mCurrentBrowser.contentDocument;"
|
||||
readonly="true"/>
|
||||
|
@ -1,5 +1,5 @@
|
||||
This is the PDF.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 1.9.607
|
||||
Current extension version is: 1.9.630
|
||||
|
||||
Taken from upstream commit: b3f84112
|
||||
Taken from upstream commit: ec469673
|
||||
|
@ -706,9 +706,6 @@ var Util = function UtilClosure() {
|
||||
}
|
||||
return result;
|
||||
};
|
||||
Util.sign = function Util_sign(num) {
|
||||
return num < 0 ? -1 : 1;
|
||||
};
|
||||
var ROMAN_NUMBER_MAP = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
|
||||
Util.toRoman = function Util_toRoman(number, lowerCase) {
|
||||
assert(Number.isInteger(number) && number > 0, 'The number should be a positive integer.');
|
||||
@ -1993,7 +1990,7 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
|
||||
if (worker.destroyed) {
|
||||
return Promise.reject(new Error('Worker was destroyed'));
|
||||
}
|
||||
let apiVersion = '1.9.607';
|
||||
let apiVersion = '1.9.630';
|
||||
source.disableAutoFetch = (0, _dom_utils.getDefaultSetting)('disableAutoFetch');
|
||||
source.disableStream = (0, _dom_utils.getDefaultSetting)('disableStream');
|
||||
source.chunkedViewerLoading = !!pdfDataRangeTransport;
|
||||
@ -3314,8 +3311,8 @@ var _UnsupportedManager = function UnsupportedManagerClosure() {
|
||||
}();
|
||||
var version, build;
|
||||
{
|
||||
exports.version = version = '1.9.607';
|
||||
exports.build = build = 'b3f84112';
|
||||
exports.version = version = '1.9.630';
|
||||
exports.build = build = 'ec469673';
|
||||
}
|
||||
exports.getDocument = getDocument;
|
||||
exports.LoopbackPort = LoopbackPort;
|
||||
@ -5054,8 +5051,8 @@ exports.SVGGraphics = SVGGraphics;
|
||||
"use strict";
|
||||
|
||||
|
||||
var pdfjsVersion = '1.9.607';
|
||||
var pdfjsBuild = 'b3f84112';
|
||||
var pdfjsVersion = '1.9.630';
|
||||
var pdfjsBuild = 'ec469673';
|
||||
var pdfjsSharedUtil = __w_pdfjs_require__(0);
|
||||
var pdfjsDisplayGlobal = __w_pdfjs_require__(13);
|
||||
var pdfjsDisplayAPI = __w_pdfjs_require__(3);
|
||||
@ -8153,7 +8150,7 @@ if (isReadableStreamSupported) {
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.PDFJS = exports.isWorker = exports.globalScope = undefined;
|
||||
exports.PDFJS = exports.globalScope = undefined;
|
||||
|
||||
var _api = __w_pdfjs_require__(3);
|
||||
|
||||
@ -8175,14 +8172,13 @@ var _svg = __w_pdfjs_require__(8);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
var isWorker = typeof window === 'undefined';
|
||||
if (!_global_scope2.default.PDFJS) {
|
||||
_global_scope2.default.PDFJS = {};
|
||||
}
|
||||
var PDFJS = _global_scope2.default.PDFJS;
|
||||
{
|
||||
PDFJS.version = '1.9.607';
|
||||
PDFJS.build = 'b3f84112';
|
||||
PDFJS.version = '1.9.630';
|
||||
PDFJS.build = 'ec469673';
|
||||
}
|
||||
PDFJS.pdfBug = false;
|
||||
if (PDFJS.verbosity !== undefined) {
|
||||
@ -8260,7 +8256,6 @@ PDFJS.Metadata = _metadata.Metadata;
|
||||
PDFJS.SVGGraphics = _svg.SVGGraphics;
|
||||
PDFJS.UnsupportedManager = _api._UnsupportedManager;
|
||||
exports.globalScope = _global_scope2.default;
|
||||
exports.isWorker = isWorker;
|
||||
exports.PDFJS = PDFJS;
|
||||
|
||||
/***/ }),
|
||||
|
@ -706,9 +706,6 @@ var Util = function UtilClosure() {
|
||||
}
|
||||
return result;
|
||||
};
|
||||
Util.sign = function Util_sign(num) {
|
||||
return num < 0 ? -1 : 1;
|
||||
};
|
||||
var ROMAN_NUMBER_MAP = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
|
||||
Util.toRoman = function Util_toRoman(number, lowerCase) {
|
||||
assert(Number.isInteger(number) && number > 0, 'The number should be a positive integer.');
|
||||
@ -23826,8 +23823,8 @@ exports.PostScriptCompiler = PostScriptCompiler;
|
||||
"use strict";
|
||||
|
||||
|
||||
var pdfjsVersion = '1.9.607';
|
||||
var pdfjsBuild = 'b3f84112';
|
||||
var pdfjsVersion = '1.9.630';
|
||||
var pdfjsBuild = 'ec469673';
|
||||
var pdfjsCoreWorker = __w_pdfjs_require__(18);
|
||||
exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
|
||||
|
||||
@ -24022,7 +24019,7 @@ var WorkerMessageHandler = {
|
||||
var cancelXHRs = null;
|
||||
var WorkerTasks = [];
|
||||
let apiVersion = docParams.apiVersion;
|
||||
let workerVersion = '1.9.607';
|
||||
let workerVersion = '1.9.630';
|
||||
if (apiVersion !== null && apiVersion !== workerVersion) {
|
||||
throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
|
||||
}
|
||||
|
@ -4441,7 +4441,7 @@ class PDFHistory {
|
||||
this._blockHashChange = 0;
|
||||
this._currentHash = getCurrentHash();
|
||||
this._numPositionUpdates = 0;
|
||||
this._currentUid = this._uid = 0;
|
||||
this._uid = this._maxUid = 0;
|
||||
this._destination = null;
|
||||
this._position = null;
|
||||
if (!this._isValidState(state) || resetHistory) {
|
||||
@ -4526,7 +4526,7 @@ class PDFHistory {
|
||||
return;
|
||||
}
|
||||
let state = window.history.state;
|
||||
if (this._isValidState(state) && state.uid < this._uid - 1) {
|
||||
if (this._isValidState(state) && state.uid < this._maxUid) {
|
||||
window.history.forward();
|
||||
}
|
||||
}
|
||||
@ -4537,13 +4537,14 @@ class PDFHistory {
|
||||
let shouldReplace = forceReplace || !this._destination;
|
||||
let newState = {
|
||||
fingerprint: this.fingerprint,
|
||||
uid: shouldReplace ? this._currentUid : this._uid,
|
||||
uid: shouldReplace ? this._uid : this._uid + 1,
|
||||
destination
|
||||
};
|
||||
this._updateInternalState(destination, newState.uid);
|
||||
if (shouldReplace) {
|
||||
window.history.replaceState(newState, '');
|
||||
} else {
|
||||
this._maxUid = this._uid;
|
||||
window.history.pushState(newState, '');
|
||||
}
|
||||
}
|
||||
@ -4603,8 +4604,7 @@ class PDFHistory {
|
||||
delete destination.temporary;
|
||||
}
|
||||
this._destination = destination;
|
||||
this._currentUid = uid;
|
||||
this._uid = this._currentUid + 1;
|
||||
this._uid = uid;
|
||||
this._numPositionUpdates = 0;
|
||||
}
|
||||
_updateViewarea({ location }) {
|
||||
@ -4638,7 +4638,7 @@ class PDFHistory {
|
||||
hashChanged = this._currentHash !== newHash;
|
||||
this._currentHash = newHash;
|
||||
if (!state || false) {
|
||||
this._currentUid = this._uid;
|
||||
this._uid++;
|
||||
let { hash, page, rotation } = parseCurrentHash(this.linkService);
|
||||
this._pushOrReplaceState({
|
||||
hash,
|
||||
|
@ -1,997 +0,0 @@
|
||||
{
|
||||
"ach": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"af": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"an": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ar": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"as": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ast": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"az": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"be": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"bg": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"bn-BD": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"bn-IN": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"br": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"bs": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ca": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"cak": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"cs": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"cy": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"da": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"de": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"dsb": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"el": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"en-GB": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"en-ZA": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"eo": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"es-AR": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"es-CL": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"es-ES": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"es-MX": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"et": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"eu": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"fa": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ff": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"fi": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"fr": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"fy-NL": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ga-IE": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"gd": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"gl": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"gn": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"gu-IN": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"he": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"hi-IN": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"hr": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"hsb": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"hu": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"hy-AM": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ia": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"id": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"is": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"it": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ja": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ja-JP-mac": {
|
||||
"platforms": [
|
||||
"macosx64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ka": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"kab": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"kk": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"km": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"kn": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ko": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"lij": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"lo": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"lt": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ltg": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"lv": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"mai": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"mk": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ml": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"mr": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ms": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"my": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"nb-NO": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ne-NP": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"nl": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"nn-NO": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"or": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"pa-IN": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"pl": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"pt-BR": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"pt-PT": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"rm": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ro": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ru": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"si": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"sk": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"sl": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"son": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"sq": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"sr": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"sv-SE": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ta": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"te": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"th": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"tl": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"tr": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"uk": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"ur": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"uz": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"vi": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"xh": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"zh-CN": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
"zh-TW": {
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"macosx64",
|
||||
"win32",
|
||||
"win64"
|
||||
],
|
||||
"revision": "default"
|
||||
}
|
||||
}
|
@ -2,9 +2,8 @@
|
||||
- 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/. -->
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-opacity=".15" d="M5,1H1C0.4,1,0,1.4,0,2v12.1C0,14.6,0.4,15,0.9,15h14.2c0.5,0,0.9-0.4,0.9-0.9V3.9C16,3.4,15.6,3,15.1,3H7 L6.2,1.9C6.2,1.9,5.6,1,5,1L5,1z"/>
|
||||
<path d="M4.9,2C5,2.1,5.2,2.3,5.4,2.5l0.8,1.1L6.5,4H7h7.5C14.8,4,15,4.2,15,4.5v9c0,0.3-0.2,0.5-0.5,0.5h-13 C1.2,14,1,13.8,1,13.5v-11C1,2.2,1.2,2,1.5,2H4.9 M5,1H1C0.4,1,0,1.4,0,2v12.1C0,14.6,0.4,15,0.9,15h14.2c0.5,0,0.9-0.4,0.9-0.9V3.9 C16,3.4,15.6,3,15.1,3H7L6.2,1.9C6.2,1.9,5.6,1,5,1L5,1z"/>
|
||||
<path fill-opacity=".15" d="M14,5H2C0.9,5,0,5.9,0,7v7c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1V7C16,5.9,15.1,5,14,5L14,5z"/>
|
||||
<path fill-opacity=".15" d="M16,13H0v1c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1V13z"/>
|
||||
<path d="M14,6c0.6,0,1,0.4,1,1v6.5c0,0.3-0.2,0.5-0.5,0.5h-13C1.2,14,1,13.8,1,13.5V7c0-0.6,0.4-1,1-1H14 M14,5H2 C0.9,5,0,5.9,0,7v7c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1V7C16,5.9,15.1,5,14,5L14,5z"/>
|
||||
<path d="M14.5 3H6.914a.5.5 0 0 1-.354-.146L5.146 1.439A1.491 1.491 0 0 0 4.086 1H1.5A1.5 1.5 0 0 0 0 2.5v11A1.5 1.5 0 0 0 1.5 15h13a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 3zm.5 10.5a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5V6h14zM1 5V2.5a.5.5 0 0 1 .5-.5h2.586a.5.5 0 0 1 .354.146l1.414 1.415A1.491 1.491 0 0 0 6.914 4H14.5a.5.5 0 0 1 .5.5V5z"/>
|
||||
<path d="M15 13.5a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5V6h14z" fill-opacity=".2"/>
|
||||
<path d="M1 5V2.5a.5.5 0 0 1 .5-.5h2.586a.5.5 0 0 1 .354.146l1.414 1.415A1.491 1.491 0 0 0 6.914 4H14.5a.5.5 0 0 1 .5.5V5z" fill-opacity=".1"/>
|
||||
<path d="M15 13.5a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5V13h14z" fill-opacity=".05"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.0 KiB |
@ -1076,21 +1076,25 @@ notification[value="translation"] {
|
||||
|
||||
/* Private browsing and accessibility indicators */
|
||||
|
||||
@media (-moz-os-version: windows-win7) {
|
||||
/* Making sure that indicators take up all available vertical space. */
|
||||
:root[tabsintitlebar]:not([inFullscreen]) .private-browsing-indicator,
|
||||
:root[tabsintitlebar]:not([inFullscreen]) .accessibility-indicator {
|
||||
height: var(--tab-min-height);
|
||||
}
|
||||
:root[sizemode="normal"][chromehidden~="menubar"] #toolbar-menubar ~ #TabsToolbar > .private-browsing-indicator,
|
||||
:root[sizemode="normal"][chromehidden~="menubar"] #toolbar-menubar ~ #TabsToolbar > .accessibility-indicator,
|
||||
:root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] ~ #TabsToolbar > .private-browsing-indicator,
|
||||
:root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] ~ #TabsToolbar > .accessibility-indicator {
|
||||
margin-top: calc(-1 * var(--space-above-tabbar));
|
||||
}
|
||||
|
||||
:root[tabsintitlebar][sizemode="normal"]:not([inFullscreen]) .private-browsing-indicator,
|
||||
:root[tabsintitlebar][sizemode="normal"]:not([inFullscreen]) .accessibility-indicator {
|
||||
height: calc(var(--tab-min-height) + 4px);
|
||||
/* Compensate for 4px extra margin on top of the tabs toolbar on Windows 7. */
|
||||
@media (-moz-os-version: windows-win7) {
|
||||
:root[sizemode="normal"][chromehidden~="menubar"] #toolbar-menubar ~ #TabsToolbar > .private-browsing-indicator,
|
||||
:root[sizemode="normal"][chromehidden~="menubar"] #toolbar-menubar ~ #TabsToolbar > .accessibility-indicator,
|
||||
:root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] ~ #TabsToolbar > .private-browsing-indicator,
|
||||
:root[sizemode="normal"] #toolbar-menubar[autohide="true"][inactive] ~ #TabsToolbar > .accessibility-indicator {
|
||||
margin-top: calc(-1 * (var(--space-above-tabbar) + 4px));
|
||||
}
|
||||
}
|
||||
|
||||
:root:-moz-any([tabsintitlebar], [inFullscreen]):not([privatebrowsingmode=temporary]) .accessibility-indicator,
|
||||
:root:-moz-any([tabsintitlebar], [inFullscreen]) .private-browsing-indicator {
|
||||
:root:not([privatebrowsingmode=temporary]) .accessibility-indicator,
|
||||
.private-browsing-indicator {
|
||||
margin-inline-end: 12px;
|
||||
}
|
||||
|
||||
@ -1099,13 +1103,6 @@ notification[value="translation"] {
|
||||
margin-inline-start: 12px;
|
||||
}
|
||||
|
||||
:root[accessibilitymode][tabsintitlebar]:not([inFullscreen]) > #tab-view-deck > #browser-panel > #navigator-toolbox > #TabsToolbar > .accessibility-indicator,
|
||||
:root[privatebrowsingmode=temporary][tabsintitlebar]:not([inFullscreen]) > #tab-view-deck > #browser-panel > #navigator-toolbox > #TabsToolbar > .private-browsing-indicator,
|
||||
:root[accessibilitymode]:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .accessibility-indicator,
|
||||
:root[privatebrowsingmode=temporary]:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > .private-browsing-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* End private browsing and accessibility indicators */
|
||||
|
||||
%include ../shared/UITour.inc.css
|
||||
|
@ -1250,9 +1250,9 @@ Inspector.prototype = {
|
||||
}));
|
||||
menu.append(new MenuItem({
|
||||
id: "node-menu-collapse",
|
||||
label: INSPECTOR_L10N.getStr("inspectorCollapseNode.label"),
|
||||
label: INSPECTOR_L10N.getStr("inspectorCollapseAll.label"),
|
||||
disabled: !isNodeWithChildren || !markupContainer.expanded,
|
||||
click: () => this.collapseNode(),
|
||||
click: () => this.collapseAll(),
|
||||
}));
|
||||
|
||||
menu.append(new MenuItem({
|
||||
@ -1981,8 +1981,8 @@ Inspector.prototype = {
|
||||
this.markup.expandAll(this.selection.nodeFront);
|
||||
},
|
||||
|
||||
collapseNode: function () {
|
||||
this.markup.collapseNode(this.selection.nodeFront);
|
||||
collapseAll: function () {
|
||||
this.markup.collapseAll(this.selection.nodeFront);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -263,10 +263,10 @@ inspectorUseInConsole.label=Use in Console
|
||||
# mark-up elements
|
||||
inspectorExpandNode.label=Expand All
|
||||
|
||||
# LOCALIZATION NOTE (inspectorCollapseNode.label): This is the label
|
||||
# LOCALIZATION NOTE (inspectorCollapseAll.label): This is the label
|
||||
# shown in the inspector contextual-menu for recursively collapsing
|
||||
# mark-up elements
|
||||
inspectorCollapseNode.label=Collapse
|
||||
inspectorCollapseAll.label=Collapse All
|
||||
|
||||
# LOCALIZATION NOTE (inspectorScreenshotNode.label): This is the label
|
||||
# shown in the inspector contextual-menu for the item that lets users take
|
||||
|
@ -3,10 +3,18 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
:root {
|
||||
--markup-hidden-attr-name-color: #CA60AC;
|
||||
--markup-hidden-attr-value-color: #5C6D87;
|
||||
--markup-hidden-punctuation-color: #909090;
|
||||
--markup-hidden-tag-color: #97A4B3;
|
||||
--markup-outline: var(--theme-splitter-color);
|
||||
}
|
||||
|
||||
.theme-dark:root {
|
||||
--markup-hidden-attr-name-color: #CC8EC8;
|
||||
--markup-hidden-attr-value-color: #9893A3;
|
||||
--markup-hidden-punctuation-color: #909090;
|
||||
--markup-hidden-tag-color: #AFB5BF;
|
||||
--markup-outline: var(--theme-selection-background);
|
||||
}
|
||||
|
||||
@ -332,6 +340,22 @@ ul.children + .tag-line::before {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.not-displayed {
|
||||
color: var(--markup-hidden-punctuation-color);
|
||||
}
|
||||
|
||||
.not-displayed .attr-name {
|
||||
color: var(--markup-hidden-attr-name-color);
|
||||
}
|
||||
|
||||
.not-displayed .attr-value {
|
||||
color: var(--markup-hidden-attr-value-color);
|
||||
}
|
||||
|
||||
.not-displayed .tag {
|
||||
color: var(--markup-hidden-tag-color);
|
||||
}
|
||||
|
||||
/* Firebug Theme */
|
||||
|
||||
.theme-firebug .theme-fg-color3 {
|
||||
@ -353,6 +377,12 @@ ul.children + .tag-line::before {
|
||||
font-size: var(--theme-toolbar-font-size);
|
||||
}
|
||||
|
||||
/* In case a node isn't displayed in the page, we fade the syntax highlighting */
|
||||
.theme-firebug .not-displayed .open,
|
||||
.theme-firebug .not-displayed .close {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
/* Selected nodes in the tree should have light selected text.
|
||||
theme-selected doesn't work in this case since the text is a
|
||||
sibling of the class, not a child. */
|
||||
@ -383,12 +413,6 @@ ul.children + .tag-line::before {
|
||||
color: #787878;
|
||||
}
|
||||
|
||||
/* In case a node isn't displayed in the page, we fade the syntax highlighting */
|
||||
.not-displayed .open,
|
||||
.not-displayed .close {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
/* Events */
|
||||
.markupview-events {
|
||||
font-size: 8px;
|
||||
|
@ -810,8 +810,8 @@ WebConsoleActor.prototype =
|
||||
}
|
||||
|
||||
// See `window` definition. It isn't always a DOM Window.
|
||||
let requestStartTime = this.window && this.window.performance ?
|
||||
this.window.performance.timing.requestStart : 0;
|
||||
let winStartTime = this.window && this.window.performance ?
|
||||
this.window.performance.timing.navigationStart : 0;
|
||||
|
||||
let cache = this.consoleAPIListener
|
||||
.getCachedMessages(!this.parentActor.isRootActor);
|
||||
@ -819,7 +819,7 @@ WebConsoleActor.prototype =
|
||||
// Filter out messages that came from a ServiceWorker but happened
|
||||
// before the page was requested.
|
||||
if (cachedMessage.innerID === "ServiceWorker" &&
|
||||
requestStartTime > cachedMessage.timeStamp) {
|
||||
winStartTime > cachedMessage.timeStamp) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,6 @@ XPIDL_SOURCES += [
|
||||
'nsIContentViewer.idl',
|
||||
'nsIContentViewerContainer.idl',
|
||||
'nsIContentViewerEdit.idl',
|
||||
'nsIContentViewerFile.idl',
|
||||
'nsIContextMenuListener.idl',
|
||||
'nsIContextMenuListener2.idl',
|
||||
'nsIDocCharset.idl',
|
||||
|
@ -1,31 +0,0 @@
|
||||
/* -*- Mode: IDL; tab-width: 4; 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 "nsISupports.idl"
|
||||
|
||||
interface nsIDOMWindow;
|
||||
interface nsIPrintSettings;
|
||||
interface nsIWebProgressListener;
|
||||
|
||||
%{ C++
|
||||
#include <stdio.h>
|
||||
%}
|
||||
|
||||
[ptr] native FILE(FILE);
|
||||
|
||||
/**
|
||||
* The nsIDocShellFile
|
||||
*/
|
||||
|
||||
[scriptable, uuid(564a3276-6228-401e-9b5c-d82cb382a60f)]
|
||||
interface nsIContentViewerFile : nsISupports
|
||||
{
|
||||
readonly attribute boolean printable;
|
||||
|
||||
[noscript] void print(in boolean aSilent,
|
||||
in FILE aDebugFile,
|
||||
in nsIPrintSettings aPrintSettings);
|
||||
};
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "mozilla/dom/CustomElementRegistry.h"
|
||||
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/dom/CustomElementRegistryBinding.h"
|
||||
#include "mozilla/dom/HTMLElementBinding.h"
|
||||
#include "mozilla/dom/WebComponentsBinding.h"
|
||||
|
@ -47,7 +47,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMIntersectionObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueuedEntries)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
@ -55,7 +54,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMIntersectionObserver)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEntries)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
@ -81,7 +79,10 @@ DOMIntersectionObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
|
||||
RefPtr<DOMIntersectionObserver> observer =
|
||||
new DOMIntersectionObserver(window.forget(), aCb);
|
||||
|
||||
observer->mRoot = aOptions.mRoot;
|
||||
if (aOptions.mRoot) {
|
||||
observer->mRoot = aOptions.mRoot;
|
||||
observer->mRoot->RegisterIntersectionObserver(observer);
|
||||
}
|
||||
|
||||
if (!observer->SetRootMargin(aOptions.mRootMargin)) {
|
||||
aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR,
|
||||
@ -175,8 +176,13 @@ DOMIntersectionObserver::Unobserve(Element& aTarget)
|
||||
}
|
||||
|
||||
void
|
||||
DOMIntersectionObserver::UnlinkTarget(Element& aTarget)
|
||||
DOMIntersectionObserver::UnlinkElement(Element& aTarget)
|
||||
{
|
||||
if (mRoot && mRoot == &aTarget) {
|
||||
mRoot = nullptr;
|
||||
Disconnect();
|
||||
return;
|
||||
}
|
||||
mObservationTargets.RemoveElement(&aTarget);
|
||||
if (mObservationTargets.Length() == 0) {
|
||||
Disconnect();
|
||||
|
@ -116,7 +116,11 @@ class DOMIntersectionObserver final : public nsISupports,
|
||||
public:
|
||||
DOMIntersectionObserver(already_AddRefed<nsPIDOMWindowInner>&& aOwner,
|
||||
mozilla::dom::IntersectionCallback& aCb)
|
||||
: mOwner(aOwner), mDocument(mOwner->GetExtantDoc()), mCallback(&aCb), mConnected(false)
|
||||
: mOwner(aOwner),
|
||||
mDocument(mOwner->GetExtantDoc()),
|
||||
mCallback(&aCb),
|
||||
mRoot(nullptr),
|
||||
mConnected(false)
|
||||
{
|
||||
}
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
@ -152,7 +156,7 @@ public:
|
||||
void Observe(Element& aTarget);
|
||||
void Unobserve(Element& aTarget);
|
||||
|
||||
void UnlinkTarget(Element& aTarget);
|
||||
void UnlinkElement(Element& aTarget);
|
||||
void Disconnect();
|
||||
|
||||
void TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal);
|
||||
@ -176,11 +180,12 @@ protected:
|
||||
nsCOMPtr<nsPIDOMWindowInner> mOwner;
|
||||
RefPtr<nsIDocument> mDocument;
|
||||
RefPtr<mozilla::dom::IntersectionCallback> mCallback;
|
||||
RefPtr<Element> mRoot;
|
||||
// Raw pointer which is explicitly cleared by UnlinkElement().
|
||||
Element* mRoot;
|
||||
nsCSSRect mRootMargin;
|
||||
nsTArray<double> mThresholds;
|
||||
|
||||
// Holds raw pointers which are explicitly cleared by UnlinkTarget().
|
||||
// Holds raw pointers which are explicitly cleared by UnlinkElement().
|
||||
nsTArray<Element*> mObservationTargets;
|
||||
|
||||
nsTArray<RefPtr<DOMIntersectionObserverEntry>> mQueuedEntries;
|
||||
|
@ -4194,13 +4194,6 @@ Element::ClearDataset()
|
||||
slots->mDataset = nullptr;
|
||||
}
|
||||
|
||||
nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>*
|
||||
Element::RegisteredIntersectionObservers()
|
||||
{
|
||||
nsExtendedDOMSlots* slots = ExtendedDOMSlots();
|
||||
return &slots->mRegisteredIntersectionObservers;
|
||||
}
|
||||
|
||||
enum nsPreviousIntersectionThreshold {
|
||||
eUninitialized = -2,
|
||||
eNonIntersecting = -1
|
||||
@ -4209,8 +4202,21 @@ enum nsPreviousIntersectionThreshold {
|
||||
void
|
||||
Element::RegisterIntersectionObserver(DOMIntersectionObserver* aObserver)
|
||||
{
|
||||
RegisteredIntersectionObservers()->LookupForAdd(aObserver).OrInsert([]() {
|
||||
// Value can be:
|
||||
IntersectionObserverList* observers =
|
||||
static_cast<IntersectionObserverList*>(
|
||||
GetProperty(nsGkAtoms::intersectionobserverlist)
|
||||
);
|
||||
|
||||
if (!observers) {
|
||||
observers = new IntersectionObserverList();
|
||||
observers->Put(aObserver, eUninitialized);
|
||||
SetProperty(nsGkAtoms::intersectionobserverlist, observers,
|
||||
nsINode::DeleteProperty<IntersectionObserverList>);
|
||||
return;
|
||||
}
|
||||
|
||||
observers->LookupForAdd(aObserver).OrInsert([]() {
|
||||
// If element is being observed, value can be:
|
||||
// -2: Makes sure next calculated threshold always differs, leading to a
|
||||
// notification task being scheduled.
|
||||
// -1: Non-intersecting.
|
||||
@ -4222,14 +4228,44 @@ Element::RegisterIntersectionObserver(DOMIntersectionObserver* aObserver)
|
||||
void
|
||||
Element::UnregisterIntersectionObserver(DOMIntersectionObserver* aObserver)
|
||||
{
|
||||
RegisteredIntersectionObservers()->Remove(aObserver);
|
||||
IntersectionObserverList* observers =
|
||||
static_cast<IntersectionObserverList*>(
|
||||
GetProperty(nsGkAtoms::intersectionobserverlist)
|
||||
);
|
||||
if (observers) {
|
||||
observers->Remove(aObserver);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Element::UnlinkIntersectionObservers()
|
||||
{
|
||||
IntersectionObserverList* observers =
|
||||
static_cast<IntersectionObserverList*>(
|
||||
GetProperty(nsGkAtoms::intersectionobserverlist)
|
||||
);
|
||||
if (!observers) {
|
||||
return;
|
||||
}
|
||||
for (auto iter = observers->Iter(); !iter.Done(); iter.Next()) {
|
||||
DOMIntersectionObserver* observer = iter.Key();
|
||||
observer->UnlinkElement(*this);
|
||||
}
|
||||
observers->Clear();
|
||||
}
|
||||
|
||||
bool
|
||||
Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, int32_t aThreshold)
|
||||
{
|
||||
IntersectionObserverList* observers =
|
||||
static_cast<IntersectionObserverList*>(
|
||||
GetProperty(nsGkAtoms::intersectionobserverlist)
|
||||
);
|
||||
if (!observers) {
|
||||
return false;
|
||||
}
|
||||
bool updated = false;
|
||||
if (auto entry = RegisteredIntersectionObservers()->Lookup(aObserver)) {
|
||||
if (auto entry = observers->Lookup(aObserver)) {
|
||||
updated = entry.Data() != aThreshold;
|
||||
entry.Data() = aThreshold;
|
||||
}
|
||||
|
@ -73,6 +73,8 @@ namespace dom {
|
||||
class ElementOrCSSPseudoElement;
|
||||
class UnrestrictedDoubleOrKeyframeAnimationOptions;
|
||||
enum class CallerType : uint32_t;
|
||||
typedef nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>
|
||||
IntersectionObserverList;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@ -1436,6 +1438,7 @@ public:
|
||||
|
||||
void RegisterIntersectionObserver(DOMIntersectionObserver* aObserver);
|
||||
void UnregisterIntersectionObserver(DOMIntersectionObserver* aObserver);
|
||||
void UnlinkIntersectionObservers();
|
||||
bool UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, int32_t threshold);
|
||||
|
||||
protected:
|
||||
@ -1721,9 +1724,6 @@ protected:
|
||||
nsDOMTokenList* GetTokenList(nsIAtom* aAtom,
|
||||
const DOMTokenListSupportedTokenArray aSupportedTokens = nullptr);
|
||||
|
||||
nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>*
|
||||
RegisteredIntersectionObservers();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Hook for implementing GetClasses. This is guaranteed to only be
|
||||
|
@ -793,14 +793,6 @@ FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb)
|
||||
}
|
||||
}
|
||||
|
||||
for (auto iter = mExtendedSlots->mRegisteredIntersectionObservers.Iter();
|
||||
!iter.Done(); iter.Next()) {
|
||||
DOMIntersectionObserver* observer = iter.Key();
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mExtendedSlots->mRegisteredIntersectionObservers[i]");
|
||||
cb.NoteXPCOMChild(observer);
|
||||
}
|
||||
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mFrameLoaderOrOpener");
|
||||
cb.NoteXPCOMChild(mExtendedSlots->mFrameLoaderOrOpener);
|
||||
}
|
||||
@ -833,7 +825,6 @@ FragmentOrElement::nsDOMSlots::Unlink()
|
||||
}
|
||||
mExtendedSlots->mCustomElementData = nullptr;
|
||||
}
|
||||
mExtendedSlots->mRegisteredIntersectionObservers.Clear();
|
||||
nsCOMPtr<nsIFrameLoader> frameLoader =
|
||||
do_QueryInterface(mExtendedSlots->mFrameLoaderOrOpener);
|
||||
if (frameLoader) {
|
||||
@ -1540,6 +1531,11 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
|
||||
// which is dispatched in UnbindFromTree.
|
||||
|
||||
if (tmp->HasProperties()) {
|
||||
if (tmp->IsElement()) {
|
||||
Element* elem = tmp->AsElement();
|
||||
elem->UnlinkIntersectionObservers();
|
||||
}
|
||||
|
||||
if (tmp->IsHTMLElement() || tmp->IsSVGElement()) {
|
||||
nsIAtom*** props = Element::HTMLSVGPropertiesToTraverseAndUnlink();
|
||||
for (uint32_t i = 0; props[i]; ++i) {
|
||||
@ -1594,14 +1590,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
|
||||
{
|
||||
nsDOMSlots *slots = tmp->GetExistingDOMSlots();
|
||||
if (slots) {
|
||||
if (slots->mExtendedSlots && tmp->IsElement()) {
|
||||
Element* elem = tmp->AsElement();
|
||||
for (auto iter = slots->mExtendedSlots->mRegisteredIntersectionObservers.Iter();
|
||||
!iter.Done(); iter.Next()) {
|
||||
DOMIntersectionObserver* observer = iter.Key();
|
||||
observer->UnlinkTarget(*elem);
|
||||
}
|
||||
}
|
||||
slots->Unlink();
|
||||
}
|
||||
}
|
||||
@ -2122,6 +2110,19 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
|
||||
#endif
|
||||
|
||||
if (tmp->HasProperties()) {
|
||||
if (tmp->IsElement()) {
|
||||
Element* elem = tmp->AsElement();
|
||||
IntersectionObserverList* observers =
|
||||
static_cast<IntersectionObserverList*>(
|
||||
elem->GetProperty(nsGkAtoms::intersectionobserverlist)
|
||||
);
|
||||
if (observers) {
|
||||
for (auto iter = observers->Iter(); !iter.Done(); iter.Next()) {
|
||||
DOMIntersectionObserver* observer = iter.Key();
|
||||
cb.NoteXPCOMChild(observer);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tmp->IsHTMLElement() || tmp->IsSVGElement()) {
|
||||
nsIAtom*** props = Element::HTMLSVGPropertiesToTraverseAndUnlink();
|
||||
for (uint32_t i = 0; props[i]; ++i) {
|
||||
|
@ -40,7 +40,6 @@ namespace mozilla {
|
||||
class DeclarationBlock;
|
||||
namespace dom {
|
||||
struct CustomElementData;
|
||||
class DOMIntersectionObserver;
|
||||
class Element;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@ -313,12 +312,6 @@ public:
|
||||
*/
|
||||
RefPtr<CustomElementData> mCustomElementData;
|
||||
|
||||
/**
|
||||
* Registered Intersection Observers on the element.
|
||||
*/
|
||||
nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>
|
||||
mRegisteredIntersectionObservers;
|
||||
|
||||
/**
|
||||
* For XUL to hold either frameloader or opener.
|
||||
*/
|
||||
|
20
dom/base/crashtests/1405771.html
Normal file
20
dom/base/crashtests/1405771.html
Normal file
@ -0,0 +1,20 @@
|
||||
<html>
|
||||
<head>
|
||||
<script></script>
|
||||
<!-- a -->
|
||||
<script>
|
||||
window.onload=function(){
|
||||
let s=window.getSelection();
|
||||
let r=document.createRange();
|
||||
r.selectNode(document.getElementById('b'));
|
||||
s.addRange(r);
|
||||
try{window.getSelection().modify('extend','forward','word')}catch(e){}
|
||||
let o=document.getElementById('a');
|
||||
o.parentNode.replaceChild(document.createElement('col'), o);
|
||||
}
|
||||
</script>
|
||||
>
|
||||
<template id='a' contenteditable='true'></template>
|
||||
<header id='b'></header>
|
||||
<!-- a -->
|
||||
</html>
|
@ -227,4 +227,5 @@ pref(clipboard.autocopy,true) load 1385272-1.html
|
||||
load 1393806.html
|
||||
load 1400701.html
|
||||
load 1403377.html
|
||||
load 1405771.html
|
||||
load 1406109-1.html
|
||||
|
@ -267,7 +267,6 @@ nsIWordBreaker *nsContentUtils::sWordBreaker;
|
||||
nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nullptr;
|
||||
uint32_t nsContentUtils::sScriptBlockerCount = 0;
|
||||
uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
|
||||
uint32_t nsContentUtils::sMicroTaskLevel = 0;
|
||||
AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners = nullptr;
|
||||
uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
|
||||
nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
|
||||
@ -5766,51 +5765,6 @@ nsContentUtils::GetStableStateEventTarget()
|
||||
return sStableStateEventTarget;
|
||||
}
|
||||
|
||||
void
|
||||
nsContentUtils::EnterMicroTask()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
++sMicroTaskLevel;
|
||||
}
|
||||
|
||||
void
|
||||
nsContentUtils::LeaveMicroTask()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (--sMicroTaskLevel == 0) {
|
||||
PerformMainThreadMicroTaskCheckpoint();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsContentUtils::IsInMicroTask()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return sMicroTaskLevel != 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsContentUtils::MicroTaskLevel()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return sMicroTaskLevel;
|
||||
}
|
||||
|
||||
void
|
||||
nsContentUtils::SetMicroTaskLevel(uint32_t aLevel)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
sMicroTaskLevel = aLevel;
|
||||
}
|
||||
|
||||
void
|
||||
nsContentUtils::PerformMainThreadMicroTaskCheckpoint()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsDOMMutationObserver::HandleMutations();
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function for nsContentUtils::ProcessViewportInfo.
|
||||
*
|
||||
@ -10464,9 +10418,11 @@ nsContentUtils::GetLoadingPrincipalForXULNode(nsIContent* aLoadingNode,
|
||||
nsAutoString loadingStr;
|
||||
aLoadingNode->GetAttr(kNameSpaceID_None, nsGkAtoms::loadingprincipal,
|
||||
loadingStr);
|
||||
if (loadingStr.IsEmpty()) {
|
||||
// Fall back to mContent's principal (SystemPrincipal) if 'loadingprincipal'
|
||||
// isn't specified.
|
||||
|
||||
// Fall back to mContent's principal if 'loadingprincipal' isn't specified,
|
||||
// or if the doc isn't loaded by System Principal.
|
||||
if (loadingStr.IsEmpty() ||
|
||||
!aLoadingNode->OwnerDoc()->NodePrincipal()->GetIsSystemPrincipal()) {
|
||||
loadingPrincipal.forget(aLoadingPrincipal);
|
||||
return result;
|
||||
}
|
||||
|
@ -1948,17 +1948,6 @@ public:
|
||||
*/
|
||||
static nsISerialEventTarget* GetStableStateEventTarget();
|
||||
|
||||
// Call EnterMicroTask when you're entering JS execution.
|
||||
// Usually the best way to do this is to use nsAutoMicroTask.
|
||||
static void EnterMicroTask();
|
||||
static void LeaveMicroTask();
|
||||
|
||||
static bool IsInMicroTask();
|
||||
static uint32_t MicroTaskLevel();
|
||||
static void SetMicroTaskLevel(uint32_t aLevel);
|
||||
|
||||
static void PerformMainThreadMicroTaskCheckpoint();
|
||||
|
||||
/* Process viewport META data. This gives us information for the scale
|
||||
* and zoom of a page on mobile devices. We stick the information in
|
||||
* the document header and use it later on after rendering.
|
||||
@ -3331,7 +3320,7 @@ private:
|
||||
static bool sInitialized;
|
||||
static uint32_t sScriptBlockerCount;
|
||||
static uint32_t sDOMNodeRemovedSuppressCount;
|
||||
static uint32_t sMicroTaskLevel;
|
||||
|
||||
// Not an nsCOMArray because removing elements from those is slower
|
||||
static AutoTArray<nsCOMPtr<nsIRunnable>, 8>* sBlockedScriptRunners;
|
||||
static uint32_t sRunnersCountAtFirstBlocker;
|
||||
@ -3493,19 +3482,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class MOZ_STACK_CLASS nsAutoMicroTask
|
||||
{
|
||||
public:
|
||||
nsAutoMicroTask()
|
||||
{
|
||||
nsContentUtils::EnterMicroTask();
|
||||
}
|
||||
~nsAutoMicroTask()
|
||||
{
|
||||
nsContentUtils::LeaveMicroTask();
|
||||
}
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "nsIDocument.h"
|
||||
#include "mozilla/dom/Animation.h"
|
||||
#include "nsIAnimationObserver.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
class nsDOMMutationObserver;
|
||||
using mozilla::dom::MutationObservingInfo;
|
||||
@ -609,11 +610,7 @@ protected:
|
||||
|
||||
bool Suppressed()
|
||||
{
|
||||
if (mOwner) {
|
||||
nsCOMPtr<nsIDocument> d = mOwner->GetExtantDoc();
|
||||
return d && d->IsInSyncOperation();
|
||||
}
|
||||
return false;
|
||||
return mOwner && nsGlobalWindow::Cast(mOwner)->IsInSyncOperation();
|
||||
}
|
||||
|
||||
static void HandleMutationsInternal();
|
||||
|
@ -50,7 +50,6 @@
|
||||
|
||||
#include "nsViewManager.h"
|
||||
|
||||
#include "nsIDOMHTMLCanvasElement.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
#include "nsIPresShell.h"
|
||||
@ -106,6 +105,7 @@
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsDocument.h"
|
||||
#include "HTMLImageElement.h"
|
||||
#include "HTMLCanvasElement.h"
|
||||
#include "mozilla/css/ImageLoader.h"
|
||||
#include "mozilla/layers/APZCTreeManager.h" // for layers::ZoomToRectBehavior
|
||||
#include "mozilla/dom/Promise.h"
|
||||
@ -1624,26 +1624,19 @@ nsDOMWindowUtils::GetTranslationNodes(nsIDOMNode* aRoot,
|
||||
}
|
||||
|
||||
static already_AddRefed<DataSourceSurface>
|
||||
CanvasToDataSourceSurface(nsIDOMHTMLCanvasElement* aCanvas)
|
||||
CanvasToDataSourceSurface(HTMLCanvasElement* aCanvas)
|
||||
{
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(aCanvas);
|
||||
if (!node) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(node->IsElement(),
|
||||
"An nsINode that implements nsIDOMHTMLCanvasElement should "
|
||||
"be an element.");
|
||||
MOZ_ASSERT(aCanvas);
|
||||
nsLayoutUtils::SurfaceFromElementResult result =
|
||||
nsLayoutUtils::SurfaceFromElement(node->AsElement());
|
||||
nsLayoutUtils::SurfaceFromElement(aCanvas);
|
||||
|
||||
MOZ_ASSERT(result.GetSourceSurface());
|
||||
return result.GetSourceSurface()->GetDataSurface();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::CompareCanvases(nsIDOMHTMLCanvasElement *aCanvas1,
|
||||
nsIDOMHTMLCanvasElement *aCanvas2,
|
||||
nsDOMWindowUtils::CompareCanvases(nsISupports *aCanvas1,
|
||||
nsISupports *aCanvas2,
|
||||
uint32_t* aMaxDifference,
|
||||
uint32_t* retVal)
|
||||
{
|
||||
@ -1652,8 +1645,17 @@ nsDOMWindowUtils::CompareCanvases(nsIDOMHTMLCanvasElement *aCanvas1,
|
||||
retVal == nullptr)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
RefPtr<DataSourceSurface> img1 = CanvasToDataSourceSurface(aCanvas1);
|
||||
RefPtr<DataSourceSurface> img2 = CanvasToDataSourceSurface(aCanvas2);
|
||||
nsCOMPtr<nsIContent> contentCanvas1 = do_QueryInterface(aCanvas1);
|
||||
nsCOMPtr<nsIContent> contentCanvas2 = do_QueryInterface(aCanvas2);
|
||||
auto canvas1 = HTMLCanvasElement::FromContentOrNull(contentCanvas1);
|
||||
auto canvas2 = HTMLCanvasElement::FromContentOrNull(contentCanvas2);
|
||||
|
||||
if (!canvas1 || !canvas2) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<DataSourceSurface> img1 = CanvasToDataSourceSurface(canvas1);
|
||||
RefPtr<DataSourceSurface> img2 = CanvasToDataSourceSurface(canvas2);
|
||||
|
||||
DataSourceSurface::ScopedMap map1(img1, DataSourceSurface::READ);
|
||||
DataSourceSurface::ScopedMap map2(img2, DataSourceSurface::READ);
|
||||
|
@ -13475,8 +13475,12 @@ MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData)
|
||||
|
||||
nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc)
|
||||
{
|
||||
mMicroTaskLevel = nsContentUtils::MicroTaskLevel();
|
||||
nsContentUtils::SetMicroTaskLevel(0);
|
||||
mMicroTaskLevel = 0;
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
mMicroTaskLevel = ccjs->MicroTaskLevel();
|
||||
ccjs->SetMicroTaskLevel(0);
|
||||
}
|
||||
if (aDoc) {
|
||||
if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
|
||||
if (nsCOMPtr<nsPIDOMWindowOuter> top = win->GetTop()) {
|
||||
@ -13495,7 +13499,10 @@ nsAutoSyncOperation::~nsAutoSyncOperation()
|
||||
}
|
||||
mDocuments[i]->SetIsInSyncOperation(false);
|
||||
}
|
||||
nsContentUtils::SetMicroTaskLevel(mMicroTaskLevel);
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->SetMicroTaskLevel(mMicroTaskLevel);
|
||||
}
|
||||
}
|
||||
|
||||
gfxUserFontSet*
|
||||
|
@ -582,6 +582,7 @@ GK_ATOM(int64, "int64")
|
||||
GK_ATOM(integer, "integer")
|
||||
GK_ATOM(integrity, "integrity")
|
||||
GK_ATOM(intersection, "intersection")
|
||||
GK_ATOM(intersectionobserverlist, "intersectionobserverlist")
|
||||
GK_ATOM(is, "is")
|
||||
GK_ATOM(iscontainer, "iscontainer")
|
||||
GK_ATOM(isempty, "isempty")
|
||||
|
@ -1313,6 +1313,11 @@ public:
|
||||
|
||||
void UpdateTopInnerWindow();
|
||||
|
||||
virtual bool IsInSyncOperation() override
|
||||
{
|
||||
return GetExtantDoc() && GetExtantDoc()->IsInSyncOperation();
|
||||
}
|
||||
|
||||
protected:
|
||||
// Web IDL helpers
|
||||
|
||||
|
@ -74,6 +74,8 @@ public:
|
||||
void UnlinkHostObjectURIs();
|
||||
void TraverseHostObjectURIs(nsCycleCollectionTraversalCallback &aCb);
|
||||
|
||||
virtual bool IsInSyncOperation() { return false; }
|
||||
|
||||
protected:
|
||||
virtual ~nsIGlobalObject();
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include "xpcpublic.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/Date.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
@ -152,7 +152,8 @@ nsJSUtils::ExecutionContext::ExecutionContext(JSContext* aCx,
|
||||
{
|
||||
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(nsContentUtils::IsInMicroTask());
|
||||
MOZ_ASSERT(CycleCollectedJSContext::Get() &&
|
||||
CycleCollectedJSContext::Get()->MicroTaskLevel());
|
||||
MOZ_ASSERT(mRetValue.isUndefined());
|
||||
|
||||
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aGlobal) == aGlobal);
|
||||
@ -390,7 +391,8 @@ nsJSUtils::CompileModule(JSContext* aCx,
|
||||
aEvaluationGlobal);
|
||||
MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == aEvaluationGlobal);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(nsContentUtils::IsInMicroTask());
|
||||
MOZ_ASSERT(CycleCollectedJSContext::Get() &&
|
||||
CycleCollectedJSContext::Get()->MicroTaskLevel());
|
||||
|
||||
NS_ENSURE_TRUE(xpc::Scriptability::Get(aEvaluationGlobal).Allowed(), NS_OK);
|
||||
|
||||
@ -408,7 +410,8 @@ nsJSUtils::ModuleInstantiate(JSContext* aCx, JS::Handle<JSObject*> aModule)
|
||||
|
||||
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(nsContentUtils::IsInMicroTask());
|
||||
MOZ_ASSERT(CycleCollectedJSContext::Get() &&
|
||||
CycleCollectedJSContext::Get()->MicroTaskLevel());
|
||||
|
||||
NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);
|
||||
|
||||
@ -426,7 +429,8 @@ nsJSUtils::ModuleEvaluate(JSContext* aCx, JS::Handle<JSObject*> aModule)
|
||||
|
||||
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(nsContentUtils::IsInMicroTask());
|
||||
MOZ_ASSERT(CycleCollectedJSContext::Get() &&
|
||||
CycleCollectedJSContext::Get()->MicroTaskLevel());
|
||||
|
||||
NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);
|
||||
|
||||
|
@ -293,19 +293,6 @@ nsNodeUtils::LastRelease(nsINode* aNode)
|
||||
NodeWillBeDestroyed, (aNode));
|
||||
}
|
||||
|
||||
if (aNode->IsElement()) {
|
||||
Element* elem = aNode->AsElement();
|
||||
FragmentOrElement::nsDOMSlots* domSlots =
|
||||
static_cast<FragmentOrElement::nsDOMSlots*>(slots);
|
||||
if (domSlots->mExtendedSlots) {
|
||||
for (auto iter = domSlots->mExtendedSlots->mRegisteredIntersectionObservers.Iter();
|
||||
!iter.Done(); iter.Next()) {
|
||||
DOMIntersectionObserver* observer = iter.Key();
|
||||
observer->UnlinkTarget(*elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete slots;
|
||||
aNode->mSlots = nullptr;
|
||||
}
|
||||
@ -320,6 +307,12 @@ nsNodeUtils::LastRelease(nsINode* aNode)
|
||||
}
|
||||
else {
|
||||
if (aNode->HasProperties()) {
|
||||
if (aNode->IsElement()) {
|
||||
Element* elem = aNode->AsElement();
|
||||
elem->UnlinkIntersectionObservers();
|
||||
elem->DeleteProperty(nsGkAtoms::intersectionobserverlist);
|
||||
}
|
||||
|
||||
// Strong reference to the document so that deleting properties can't
|
||||
// delete the document.
|
||||
nsCOMPtr<nsIDocument> document = aNode->OwnerDoc();
|
||||
|
@ -947,6 +947,23 @@ nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv)
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
nsRange::NotifySelectionListenersAfterRangeSet()
|
||||
{
|
||||
if (mSelection) {
|
||||
// Our internal code should not move focus with using this instance while
|
||||
// it's calling Selection::NotifySelectionListeners() which may move focus
|
||||
// or calls selection listeners. So, let's set mCalledByJS to false here
|
||||
// since non-*JS() methods don't set it to false.
|
||||
AutoCalledByJSRestore calledByJSRestorer(*this);
|
||||
mCalledByJS = false;
|
||||
// Be aware, this range may be modified or stop being a range for selection
|
||||
// after this call. Additionally, the selection instance may have gone.
|
||||
RefPtr<Selection> selection = mSelection;
|
||||
selection->NotifySelectionListeners(calledByJSRestorer.SavedValue());
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* Private helper routines
|
||||
******************************************************/
|
||||
@ -1030,17 +1047,13 @@ nsRange::DoSetRange(const RawRangeBoundary& aStart,
|
||||
|
||||
// Notify any selection listeners. This has to occur last because otherwise the world
|
||||
// could be observed by a selection listener while the range was in an invalid state.
|
||||
// So we run it off of a script runner to ensure it runs after the mutation observers
|
||||
// have finished running.
|
||||
if (mSelection) {
|
||||
// Our internal code should not move focus with using this instance while
|
||||
// it's calling Selection::NotifySelectionListeners() which may move focus
|
||||
// or calls selection listeners. So, let's set mCalledByJS to false here
|
||||
// since non-*JS() methods don't set it to false.
|
||||
AutoCalledByJSRestore calledByJSRestorer(*this);
|
||||
mCalledByJS = false;
|
||||
// Be aware, this range may be modified or stop being a range for selection
|
||||
// after this call. Additionally, the selection instance may have gone.
|
||||
RefPtr<Selection> selection = mSelection;
|
||||
selection->NotifySelectionListeners(calledByJSRestorer.SavedValue());
|
||||
nsContentUtils::AddScriptRunner(NewRunnableMethod(
|
||||
"NotifySelectionListenersAfterRangeSet",
|
||||
this,
|
||||
&nsRange::NotifySelectionListenersAfterRangeSet));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -427,6 +427,11 @@ public:
|
||||
*/
|
||||
void ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges);
|
||||
|
||||
/**
|
||||
* Notify the selection listeners after a range has been modified.
|
||||
*/
|
||||
void NotifySelectionListenersAfterRangeSet();
|
||||
|
||||
typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable;
|
||||
protected:
|
||||
|
||||
|
@ -624,6 +624,7 @@ skip-if = toolkit == 'android'
|
||||
[test_bug1381710.html]
|
||||
[test_bug1384658.html]
|
||||
skip-if = toolkit == 'android'
|
||||
[test_bug1399603.html]
|
||||
[test_bug1399605.html]
|
||||
[test_caretPositionFromPoint.html]
|
||||
[test_change_policy.html]
|
||||
|
63
dom/base/test/test_bug1399603.html
Normal file
63
dom/base/test/test_bug1399603.html
Normal file
@ -0,0 +1,63 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1399603</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1399603">Mozilla Bug 1399603</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<div id="root">
|
||||
<div id="target"></div>
|
||||
</div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
function waitForNotification(f) {
|
||||
requestAnimationFrame(function() {
|
||||
setTimeout(function() { setTimeout(f); });
|
||||
});
|
||||
}
|
||||
|
||||
function forceGC() {
|
||||
SpecialPowers.gc();
|
||||
SpecialPowers.forceShrinkingGC();
|
||||
SpecialPowers.forceCC();
|
||||
SpecialPowers.gc();
|
||||
SpecialPowers.forceShrinkingGC();
|
||||
SpecialPowers.forceCC();
|
||||
}
|
||||
|
||||
let content = document.getElementById('content');
|
||||
let root = document.getElementById('root');
|
||||
let target = document.getElementById('target');
|
||||
let entries = [];
|
||||
let observer = new IntersectionObserver(function(changes) {
|
||||
entries = entries.concat(changes);
|
||||
}, { root: root });
|
||||
observer.observe(target);
|
||||
|
||||
waitForNotification(function () {
|
||||
is(entries.length, 1, "Initial notification.");
|
||||
root.removeChild(target);
|
||||
content.removeChild(root);
|
||||
root = null;
|
||||
forceGC();
|
||||
waitForNotification(function () {
|
||||
is(entries.length, 1, "No new notifications.");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
<div id="log">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -5,6 +5,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/CallbackObject.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
@ -140,7 +141,10 @@ CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
|
||||
, mIsMainThread(NS_IsMainThread())
|
||||
{
|
||||
if (mIsMainThread) {
|
||||
nsContentUtils::EnterMicroTask();
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->EnterMicroTask();
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the caller's subject principal (if necessary) early, before we
|
||||
@ -349,7 +353,10 @@ CallbackObject::CallSetup::~CallSetup()
|
||||
// It is important that this is the last thing we do, after leaving the
|
||||
// compartment and undoing all our entry/incumbent script changes
|
||||
if (mIsMainThread) {
|
||||
nsContentUtils::LeaveMicroTask();
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->LeaveMicroTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -976,7 +976,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual void DoUpdate() override
|
||||
virtual void OnRenderingChange() override
|
||||
{
|
||||
if (!mContext) {
|
||||
MOZ_CRASH("GFX: This should never be called without a context");
|
||||
|
@ -1819,7 +1819,7 @@ WebGLContext::UpdateContextLossStatus()
|
||||
if (mCanvasElement) {
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
mCanvasElement->OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
|
||||
static_cast<nsIContent*>(mCanvasElement),
|
||||
kEventName,
|
||||
kCanBubble,
|
||||
kIsCancelable,
|
||||
@ -1887,7 +1887,7 @@ WebGLContext::UpdateContextLossStatus()
|
||||
if (mCanvasElement) {
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
mCanvasElement->OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
|
||||
static_cast<nsIContent*>(mCanvasElement),
|
||||
NS_LITERAL_STRING("webglcontextrestored"),
|
||||
true,
|
||||
true);
|
||||
|
@ -38,7 +38,6 @@ already_AddRefed<Promise>
|
||||
CredentialsContainer::Get(const CredentialRequestOptions& aOptions)
|
||||
{
|
||||
RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
|
||||
MOZ_ASSERT(mgr);
|
||||
return mgr->GetAssertion(mParent, aOptions.mPublicKey);
|
||||
}
|
||||
|
||||
@ -46,7 +45,6 @@ already_AddRefed<Promise>
|
||||
CredentialsContainer::Create(const CredentialCreationOptions& aOptions)
|
||||
{
|
||||
RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
|
||||
MOZ_ASSERT(mgr);
|
||||
return mgr->MakeCredential(mParent, aOptions.mPublicKey);
|
||||
}
|
||||
|
||||
|
@ -1100,7 +1100,10 @@ EventListenerManager::HandleEventSubType(Listener* aListener,
|
||||
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
if (mIsMainThreadELM) {
|
||||
nsContentUtils::EnterMicroTask();
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->EnterMicroTask();
|
||||
}
|
||||
}
|
||||
// nsIDOMEvent::currentTarget is set in EventDispatcher.
|
||||
if (listenerHolder.HasWebIDLCallback()) {
|
||||
@ -1112,7 +1115,10 @@ EventListenerManager::HandleEventSubType(Listener* aListener,
|
||||
result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent);
|
||||
}
|
||||
if (mIsMainThreadELM) {
|
||||
nsContentUtils::LeaveMicroTask();
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (ccjs) {
|
||||
ccjs->LeaveMicroTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "MultipartBlobImpl.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "TemporaryBlobImpl.h"
|
||||
#include "StreamBlobImpl.h"
|
||||
#include "StringBlobImpl.h"
|
||||
|
||||
@ -94,17 +93,6 @@ Blob::CreateMemoryBlob(nsISupports* aParent, void* aMemoryBuffer,
|
||||
return blob.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<Blob>
|
||||
Blob::CreateTemporaryBlob(nsISupports* aParent, PRFileDesc* aFD,
|
||||
uint64_t aStartPos, uint64_t aLength,
|
||||
const nsAString& aContentType)
|
||||
{
|
||||
RefPtr<Blob> blob = Blob::Create(aParent,
|
||||
new TemporaryBlobImpl(aFD, aStartPos, aLength, aContentType));
|
||||
MOZ_ASSERT(!blob->mImpl->IsFile());
|
||||
return blob.forget();
|
||||
}
|
||||
|
||||
Blob::Blob(nsISupports* aParent, BlobImpl* aImpl)
|
||||
: mImpl(aImpl)
|
||||
, mParent(aParent)
|
||||
|
@ -59,11 +59,6 @@ public:
|
||||
CreateMemoryBlob(nsISupports* aParent, void* aMemoryBuffer, uint64_t aLength,
|
||||
const nsAString& aContentType);
|
||||
|
||||
static already_AddRefed<Blob>
|
||||
CreateTemporaryBlob(nsISupports* aParent, PRFileDesc* aFD,
|
||||
uint64_t aStartPos, uint64_t aLength,
|
||||
const nsAString& aContentType);
|
||||
|
||||
BlobImpl* Impl() const
|
||||
{
|
||||
return mImpl;
|
||||
|
@ -254,7 +254,7 @@ FileBlobImpl::CreateInputStream(nsIInputStream** aStream, ErrorResult& aRv)
|
||||
}
|
||||
|
||||
RefPtr<SlicedInputStream> slicedInputStream =
|
||||
new SlicedInputStream(stream, mStart, mLength);
|
||||
new SlicedInputStream(stream.forget(), mStart, mLength);
|
||||
slicedInputStream.forget(aStream);
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,6 @@ public:
|
||||
protected:
|
||||
virtual ~FileBlobImpl() = default;
|
||||
|
||||
private:
|
||||
// Create slice
|
||||
FileBlobImpl(const FileBlobImpl* aOther, uint64_t aStart,
|
||||
uint64_t aLength, const nsAString& aContentType);
|
||||
|
@ -7,6 +7,9 @@
|
||||
#include "MutableBlobStorage.h"
|
||||
#include "MemoryBlobImpl.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/dom/ipc/TemporaryIPCBlobChild.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TaskQueue.h"
|
||||
#include "File.h"
|
||||
@ -74,78 +77,6 @@ private:
|
||||
nsresult mRv;
|
||||
};
|
||||
|
||||
// This runnable goes back to the main-thread and informs the BlobStorage about
|
||||
// the temporary file.
|
||||
class FileCreatedRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
FileCreatedRunnable(MutableBlobStorage* aBlobStorage, PRFileDesc* aFD)
|
||||
: Runnable("dom::FileCreatedRunnable")
|
||||
, mBlobStorage(aBlobStorage)
|
||||
, mFD(aFD)
|
||||
{
|
||||
MOZ_ASSERT(aBlobStorage);
|
||||
MOZ_ASSERT(aFD);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mBlobStorage->TemporaryFileCreated(mFD);
|
||||
mFD = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~FileCreatedRunnable()
|
||||
{
|
||||
// If something when wrong, we still have to close the FileDescriptor.
|
||||
if (mFD) {
|
||||
PR_Close(mFD);
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<MutableBlobStorage> mBlobStorage;
|
||||
PRFileDesc* mFD;
|
||||
};
|
||||
|
||||
// This runnable creates the temporary file. When done, FileCreatedRunnable is
|
||||
// dispatched back to the main-thread.
|
||||
class CreateTemporaryFileRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit CreateTemporaryFileRunnable(MutableBlobStorage* aBlobStorage)
|
||||
: Runnable("dom::CreateTemporaryFileRunnable")
|
||||
, mBlobStorage(aBlobStorage)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(aBlobStorage);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(mBlobStorage);
|
||||
|
||||
PRFileDesc* tempFD = nullptr;
|
||||
nsresult rv = NS_OpenAnonymousTemporaryFile(&tempFD);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The ownership of the tempFD is moved to the FileCreatedRunnable.
|
||||
return mBlobStorage->EventTarget()->Dispatch(
|
||||
new FileCreatedRunnable(mBlobStorage, tempFD), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<MutableBlobStorage> mBlobStorage;
|
||||
};
|
||||
|
||||
// Simple runnable to propagate the error to the BlobStorage.
|
||||
class ErrorPropagationRunnable final : public Runnable
|
||||
{
|
||||
@ -281,8 +212,11 @@ private:
|
||||
// This runnable is dispatched to the main-thread from the IO thread and its
|
||||
// task is to create the blob and inform the callback.
|
||||
class CreateBlobRunnable final : public Runnable
|
||||
, public TemporaryIPCBlobChildCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
CreateBlobRunnable(MutableBlobStorage* aBlobStorage,
|
||||
already_AddRefed<nsISupports> aParent,
|
||||
const nsACString& aContentType,
|
||||
@ -302,11 +236,27 @@ public:
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mBlobStorage);
|
||||
mBlobStorage->CreateBlobAndRespond(mParent.forget(), mContentType,
|
||||
mCallback.forget());
|
||||
mBlobStorage->AskForBlob(this, mContentType);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
OperationSucceeded(BlobImpl* aBlobImpl) override
|
||||
{
|
||||
nsCOMPtr<nsISupports> parent(Move(mParent));
|
||||
RefPtr<MutableBlobStorageCallback> callback(Move(mCallback));
|
||||
|
||||
RefPtr<Blob> blob = Blob::Create(parent, aBlobImpl);
|
||||
callback->BlobStoreCompleted(mBlobStorage, blob, NS_OK);
|
||||
}
|
||||
|
||||
void
|
||||
OperationFailed(nsresult aRv) override
|
||||
{
|
||||
RefPtr<MutableBlobStorageCallback> callback(Move(mCallback));
|
||||
callback->BlobStoreCompleted(mBlobStorage, nullptr, aRv);
|
||||
}
|
||||
|
||||
private:
|
||||
~CreateBlobRunnable()
|
||||
{
|
||||
@ -327,17 +277,21 @@ private:
|
||||
RefPtr<MutableBlobStorageCallback> mCallback;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(CreateBlobRunnable, Runnable)
|
||||
|
||||
// This task is used to know when the writing is completed. From the IO thread
|
||||
// it dispatches a CreateBlobRunnable to the main-thread.
|
||||
class LastRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
LastRunnable(MutableBlobStorage* aBlobStorage,
|
||||
PRFileDesc* aFD,
|
||||
nsISupports* aParent,
|
||||
const nsACString& aContentType,
|
||||
MutableBlobStorageCallback* aCallback)
|
||||
: Runnable("dom::LastRunnable")
|
||||
, mBlobStorage(aBlobStorage)
|
||||
, mFD(aFD)
|
||||
, mParent(aParent)
|
||||
, mContentType(aContentType)
|
||||
, mCallback(aCallback)
|
||||
@ -345,13 +299,17 @@ public:
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mBlobStorage);
|
||||
MOZ_ASSERT(aCallback);
|
||||
MOZ_ASSERT(aFD);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mBlobStorage);
|
||||
|
||||
PR_Close(mFD);
|
||||
mFD = nullptr;
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
new CreateBlobRunnable(mBlobStorage, mParent.forget(),
|
||||
mContentType, mCallback.forget());
|
||||
@ -373,6 +331,7 @@ private:
|
||||
}
|
||||
|
||||
RefPtr<MutableBlobStorage> mBlobStorage;
|
||||
PRFileDesc* mFD;
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
nsCString mContentType;
|
||||
RefPtr<MutableBlobStorageCallback> mCallback;
|
||||
@ -381,7 +340,8 @@ private:
|
||||
} // anonymous namespace
|
||||
|
||||
MutableBlobStorage::MutableBlobStorage(MutableBlobStorageType aType,
|
||||
nsIEventTarget* aEventTarget)
|
||||
nsIEventTarget* aEventTarget,
|
||||
uint32_t aMaxMemory)
|
||||
: mData(nullptr)
|
||||
, mDataLen(0)
|
||||
, mDataBufferLen(0)
|
||||
@ -389,6 +349,7 @@ MutableBlobStorage::MutableBlobStorage(MutableBlobStorageType aType,
|
||||
, mFD(nullptr)
|
||||
, mErrorResult(NS_OK)
|
||||
, mEventTarget(aEventTarget)
|
||||
, mMaxMemory(aMaxMemory)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@ -396,6 +357,11 @@ MutableBlobStorage::MutableBlobStorage(MutableBlobStorageType aType,
|
||||
mEventTarget = GetMainThreadEventTarget();
|
||||
}
|
||||
|
||||
if (aMaxMemory == 0 && aType == eCouldBeInTemporaryFile) {
|
||||
mMaxMemory = Preferences::GetUint("dom.blob.memoryToTemporaryFile",
|
||||
BLOB_MEMORY_TEMPORARY_FILE);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mEventTarget);
|
||||
}
|
||||
|
||||
@ -411,6 +377,11 @@ MutableBlobStorage::~MutableBlobStorage()
|
||||
if (mTaskQueue) {
|
||||
mTaskQueue->BeginShutdown();
|
||||
}
|
||||
|
||||
if (mActor) {
|
||||
NS_ProxyRelease("MutableBlobStorage::mActor",
|
||||
EventTarget(), mActor.forget());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -430,18 +401,24 @@ MutableBlobStorage::GetBlobWhenReady(nsISupports* aParent,
|
||||
MOZ_ASSERT(mFD);
|
||||
|
||||
if (NS_FAILED(mErrorResult)) {
|
||||
MOZ_ASSERT(!mActor);
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
new BlobCreationDoneRunnable(this, aCallback, nullptr, mErrorResult);
|
||||
EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mActor);
|
||||
|
||||
// We want to wait until all the WriteRunnable are completed. The way we do
|
||||
// this is to go to the I/O thread and then we come back: the runnables are
|
||||
// executed in order and this LastRunnable will be... the last one.
|
||||
// This Runnable will also close the FD on the I/O thread.
|
||||
RefPtr<Runnable> runnable =
|
||||
new LastRunnable(this, aParent, aContentType, aCallback);
|
||||
new LastRunnable(this, mFD, aParent, aContentType, aCallback);
|
||||
DispatchToIOThread(runnable.forget());
|
||||
mFD = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -490,10 +467,7 @@ MutableBlobStorage::Append(const void* aData, uint32_t aLength)
|
||||
// If eInMemory is the current Storage state, we could maybe migrate to
|
||||
// a temporary file.
|
||||
if (mStorageState == eInMemory && ShouldBeTemporaryStorage(aLength)) {
|
||||
nsresult rv = MaybeCreateTemporaryFile();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
MaybeCreateTemporaryFile();
|
||||
}
|
||||
|
||||
// If we are already in the temporaryFile mode, we have to dispatch a
|
||||
@ -570,30 +544,52 @@ MutableBlobStorage::ShouldBeTemporaryStorage(uint64_t aSize) const
|
||||
return false;
|
||||
}
|
||||
|
||||
return bufferSize.value() >= Preferences::GetUint("dom.blob.memoryToTemporaryFile",
|
||||
BLOB_MEMORY_TEMPORARY_FILE);
|
||||
return bufferSize.value() >= mMaxMemory;
|
||||
}
|
||||
|
||||
nsresult
|
||||
void
|
||||
MutableBlobStorage::MaybeCreateTemporaryFile()
|
||||
{
|
||||
if (XRE_IsParentProcess()) {
|
||||
RefPtr<Runnable> runnable = new CreateTemporaryFileRunnable(this);
|
||||
DispatchToIOThread(runnable.forget());
|
||||
mStorageState = eWaitingForTemporaryFile;
|
||||
|
||||
mozilla::ipc::PBackgroundChild* actor =
|
||||
mozilla::ipc::BackgroundChild::GetForCurrentThread();
|
||||
if (actor) {
|
||||
ActorCreated(actor);
|
||||
} else {
|
||||
RefPtr<MutableBlobStorage> self(this);
|
||||
ContentChild::GetSingleton()->
|
||||
AsyncOpenAnonymousTemporaryFile([self](PRFileDesc* prfile) {
|
||||
if (prfile) {
|
||||
// The ownership of the prfile is moved to the FileCreatedRunnable.
|
||||
self->EventTarget()->Dispatch(
|
||||
new FileCreatedRunnable(self, prfile), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
});
|
||||
mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MutableBlobStorage::ActorFailed()
|
||||
{
|
||||
MOZ_CRASH("Failed to create a PBackgroundChild actor!");
|
||||
}
|
||||
|
||||
void
|
||||
MutableBlobStorage::ActorCreated(PBackgroundChild* aActor)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStorageState == eWaitingForTemporaryFile ||
|
||||
mStorageState == eClosed);
|
||||
MOZ_ASSERT_IF(mPendingCallback, mStorageState == eClosed);
|
||||
|
||||
// If the object has been already closed and we don't need to execute a
|
||||
// callback, we have nothing else to do.
|
||||
if (mStorageState == eClosed && !mPendingCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
mStorageState = eWaitingForTemporaryFile;
|
||||
return NS_OK;
|
||||
mActor = new TemporaryIPCBlobChild(this);
|
||||
aActor->SendPTemporaryIPCBlobConstructor(mActor);
|
||||
|
||||
// We need manually to increase the reference for this actor because the
|
||||
// IPC allocator method is not triggered. The Release() is called by IPDL
|
||||
// when the actor is deleted.
|
||||
mActor.get()->AddRef();
|
||||
|
||||
// The actor will call us when the FileDescriptor is received.
|
||||
}
|
||||
|
||||
void
|
||||
@ -603,12 +599,18 @@ MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD)
|
||||
MOZ_ASSERT(mStorageState == eWaitingForTemporaryFile ||
|
||||
mStorageState == eClosed);
|
||||
MOZ_ASSERT_IF(mPendingCallback, mStorageState == eClosed);
|
||||
MOZ_ASSERT(mActor);
|
||||
MOZ_ASSERT(aFD);
|
||||
|
||||
// If the object has been already closed and we don't need to execute a
|
||||
// callback, we need just to close the file descriptor in the correct thread.
|
||||
if (mStorageState == eClosed && !mPendingCallback) {
|
||||
RefPtr<Runnable> runnable = new CloseFileRunnable(aFD);
|
||||
DispatchToIOThread(runnable.forget());
|
||||
|
||||
// Let's inform the parent that we have nothing else to do.
|
||||
mActor->SendOperationDone(false, EmptyCString());
|
||||
mActor = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -638,9 +640,10 @@ MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD)
|
||||
MOZ_ASSERT(mPendingCallback);
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
new LastRunnable(this, mPendingParent, mPendingContentType,
|
||||
new LastRunnable(this, mFD, mPendingParent, mPendingContentType,
|
||||
mPendingCallback);
|
||||
DispatchToIOThread(runnable.forget());
|
||||
mFD = nullptr;
|
||||
|
||||
mPendingParent = nullptr;
|
||||
mPendingCallback = nullptr;
|
||||
@ -648,24 +651,17 @@ MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD)
|
||||
}
|
||||
|
||||
void
|
||||
MutableBlobStorage::CreateBlobAndRespond(already_AddRefed<nsISupports> aParent,
|
||||
const nsACString& aContentType,
|
||||
already_AddRefed<MutableBlobStorageCallback> aCallback)
|
||||
MutableBlobStorage::AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
|
||||
const nsACString& aContentType)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mStorageState == eClosed);
|
||||
MOZ_ASSERT(mFD);
|
||||
MOZ_ASSERT(!mFD);
|
||||
MOZ_ASSERT(mActor);
|
||||
MOZ_ASSERT(aCallback);
|
||||
|
||||
nsCOMPtr<nsISupports> parent(aParent);
|
||||
RefPtr<MutableBlobStorageCallback> callback(aCallback);
|
||||
|
||||
RefPtr<Blob> blob =
|
||||
File::CreateTemporaryBlob(parent, mFD, 0, mDataLen,
|
||||
NS_ConvertUTF8toUTF16(aContentType));
|
||||
callback->BlobStoreCompleted(this, blob, NS_OK);
|
||||
|
||||
// ownership of this FD is moved to the BlobImpl.
|
||||
mFD = nullptr;
|
||||
mActor->AskForBlob(aCallback, aContentType);
|
||||
mActor = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
@ -673,6 +669,9 @@ MutableBlobStorage::ErrorPropagated(nsresult aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mErrorResult = aRv;
|
||||
|
||||
mActor->SendOperationDone(false, EmptyCString());
|
||||
mActor = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
@ -690,5 +689,14 @@ MutableBlobStorage::DispatchToIOThread(already_AddRefed<nsIRunnable> aRunnable)
|
||||
mTaskQueue->Dispatch(runnable.forget());
|
||||
}
|
||||
|
||||
size_t
|
||||
MutableBlobStorage::SizeOfCurrentMemoryBuffer() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mStorageState < eInTemporaryFile ? mDataLen : 0;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(MutableBlobStorage, nsIIPCBackgroundChildCreateCallback)
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
@ -8,9 +8,11 @@
|
||||
#define mozilla_dom_MutableBlobStorage_h
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsIIPCBackgroundChildCreateCallback.h"
|
||||
#include "prio.h"
|
||||
|
||||
class nsIEventTarget;
|
||||
class nsIRunnable;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -21,6 +23,8 @@ namespace dom {
|
||||
class Blob;
|
||||
class BlobImpl;
|
||||
class MutableBlobStorage;
|
||||
class TemporaryIPCBlobChild;
|
||||
class TemporaryIPCBlobChildCallback;
|
||||
|
||||
class MutableBlobStorageCallback
|
||||
{
|
||||
@ -33,10 +37,11 @@ public:
|
||||
};
|
||||
|
||||
// This class is main-thread only.
|
||||
class MutableBlobStorage final
|
||||
class MutableBlobStorage final : public nsIIPCBackgroundChildCreateCallback
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MutableBlobStorage);
|
||||
NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
enum MutableBlobStorageType
|
||||
{
|
||||
@ -45,7 +50,8 @@ public:
|
||||
};
|
||||
|
||||
explicit MutableBlobStorage(MutableBlobStorageType aType,
|
||||
nsIEventTarget* aEventTarget = nullptr);
|
||||
nsIEventTarget* aEventTarget = nullptr,
|
||||
uint32_t aMaxMemory = 0);
|
||||
|
||||
nsresult Append(const void* aData, uint32_t aLength);
|
||||
|
||||
@ -57,9 +63,8 @@ public:
|
||||
|
||||
void TemporaryFileCreated(PRFileDesc* aFD);
|
||||
|
||||
void CreateBlobAndRespond(already_AddRefed<nsISupports> aParent,
|
||||
const nsACString& aContentType,
|
||||
already_AddRefed<MutableBlobStorageCallback> aCallback);
|
||||
void AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
|
||||
const nsACString& aContentType);
|
||||
|
||||
void ErrorPropagated(nsresult aRv);
|
||||
|
||||
@ -69,6 +74,10 @@ public:
|
||||
return mEventTarget;
|
||||
}
|
||||
|
||||
// Returns the heap size in bytes of our internal buffers.
|
||||
// Note that this intentionally ignores the data in the temp file.
|
||||
size_t SizeOfCurrentMemoryBuffer() const;
|
||||
|
||||
private:
|
||||
~MutableBlobStorage();
|
||||
|
||||
@ -76,7 +85,7 @@ private:
|
||||
|
||||
bool ShouldBeTemporaryStorage(uint64_t aSize) const;
|
||||
|
||||
nsresult MaybeCreateTemporaryFile();
|
||||
void MaybeCreateTemporaryFile();
|
||||
|
||||
void DispatchToIOThread(already_AddRefed<nsIRunnable> aRunnable);
|
||||
|
||||
@ -106,6 +115,14 @@ private:
|
||||
nsCOMPtr<nsISupports> mPendingParent;
|
||||
nsCString mPendingContentType;
|
||||
RefPtr<MutableBlobStorageCallback> mPendingCallback;
|
||||
|
||||
RefPtr<TemporaryIPCBlobChild> mActor;
|
||||
|
||||
// This value is used when we go from eInMemory to eWaitingForTemporaryFile
|
||||
// and eventually eInTemporaryFile. If the size of the buffer is >=
|
||||
// mMaxMemory, the creation of the temporary file will start.
|
||||
// It's not used if mStorageState is eKeepInMemory.
|
||||
uint32_t mMaxMemory;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -49,17 +49,6 @@ StreamBlobImpl::StreamBlobImpl(nsIInputStream* aInputStream,
|
||||
mImmutable = true;
|
||||
}
|
||||
|
||||
StreamBlobImpl::StreamBlobImpl(StreamBlobImpl* aOther,
|
||||
const nsAString& aContentType,
|
||||
uint64_t aStart, uint64_t aLength)
|
||||
: BaseBlobImpl(aContentType, aOther->mStart + aStart, aLength)
|
||||
, mInputStream(new SlicedInputStream(aOther->mInputStream, aStart, aLength))
|
||||
, mIsDirectory(false)
|
||||
, mFileId(-1)
|
||||
{
|
||||
mImmutable = true;
|
||||
}
|
||||
|
||||
StreamBlobImpl::StreamBlobImpl(nsIInputStream* aInputStream,
|
||||
const nsAString& aName,
|
||||
const nsAString& aContentType,
|
||||
@ -106,22 +95,29 @@ StreamBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
|
||||
return impl.forget();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> clonedStream;
|
||||
|
||||
nsCOMPtr<nsICloneableInputStreamWithRange> stream =
|
||||
do_QueryInterface(mInputStream);
|
||||
if (stream) {
|
||||
nsCOMPtr<nsIInputStream> clonedStream;
|
||||
aRv = stream->CloneWithRange(aStart, aLength, getter_AddRefs(clonedStream));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
CreateInputStream(getter_AddRefs(clonedStream), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<BlobImpl> impl =
|
||||
new StreamBlobImpl(clonedStream, aContentType, aLength);
|
||||
return impl.forget();
|
||||
clonedStream =
|
||||
new SlicedInputStream(clonedStream.forget(), aStart, aLength);
|
||||
}
|
||||
|
||||
RefPtr<BlobImpl> impl;
|
||||
impl = new StreamBlobImpl(this, aContentType, aStart, aLength);
|
||||
MOZ_ASSERT(clonedStream);
|
||||
|
||||
RefPtr<BlobImpl> impl =
|
||||
new StreamBlobImpl(clonedStream, aContentType, aLength);
|
||||
return impl.forget();
|
||||
}
|
||||
|
||||
|
@ -90,11 +90,6 @@ private:
|
||||
int64_t aLastModifiedDate,
|
||||
uint64_t aLength);
|
||||
|
||||
StreamBlobImpl(StreamBlobImpl* aOther,
|
||||
const nsAString& aContentType,
|
||||
uint64_t aStart,
|
||||
uint64_t aLength);
|
||||
|
||||
~StreamBlobImpl();
|
||||
|
||||
void MaybeRegisterMemoryReporter();
|
||||
|
@ -1,58 +0,0 @@
|
||||
/* -*- 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 "TemporaryBlobImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(TemporaryBlobImpl, BlobImpl)
|
||||
|
||||
TemporaryBlobImpl::TemporaryBlobImpl(PRFileDesc* aFD, uint64_t aStartPos,
|
||||
uint64_t aLength,
|
||||
const nsAString& aContentType)
|
||||
: BaseBlobImpl(aContentType, aLength)
|
||||
, mStartPos(aStartPos)
|
||||
{
|
||||
mFileDescOwner = new nsTemporaryFileInputStream::FileDescOwner(aFD);
|
||||
}
|
||||
|
||||
TemporaryBlobImpl::TemporaryBlobImpl(const TemporaryBlobImpl* aOther,
|
||||
uint64_t aStart, uint64_t aLength,
|
||||
const nsAString& aContentType)
|
||||
: BaseBlobImpl(aContentType, aLength)
|
||||
, mStartPos(aStart)
|
||||
, mFileDescOwner(aOther->mFileDescOwner)
|
||||
{}
|
||||
|
||||
already_AddRefed<BlobImpl>
|
||||
TemporaryBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
|
||||
const nsAString& aContentType,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aStart + aLength > mLength) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<BlobImpl> impl =
|
||||
new TemporaryBlobImpl(this, aStart + mStartPos,
|
||||
aLength, aContentType);
|
||||
return impl.forget();
|
||||
}
|
||||
|
||||
void
|
||||
TemporaryBlobImpl::CreateInputStream(nsIInputStream** aStream,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> stream =
|
||||
new nsTemporaryFileInputStream(mFileDescOwner, mStartPos,
|
||||
mStartPos + mLength);
|
||||
stream.forget(aStream);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,45 +0,0 @@
|
||||
/* -*- 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_dom_TemporaryBlobImpl_h
|
||||
#define mozilla_dom_TemporaryBlobImpl_h
|
||||
|
||||
#include "BaseBlobImpl.h"
|
||||
#include "nsTemporaryFileInputStream.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class TemporaryBlobImpl final : public BaseBlobImpl
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
TemporaryBlobImpl(PRFileDesc* aFD, uint64_t aStartPos,
|
||||
uint64_t aLength, const nsAString& aContentType);
|
||||
|
||||
virtual void CreateInputStream(nsIInputStream** aStream,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
virtual already_AddRefed<BlobImpl>
|
||||
CreateSlice(uint64_t aStart, uint64_t aLength,
|
||||
const nsAString& aContentType, ErrorResult& aRv) override;
|
||||
|
||||
private:
|
||||
TemporaryBlobImpl(const TemporaryBlobImpl* aOther,
|
||||
uint64_t aStart, uint64_t aLength,
|
||||
const nsAString& aContentType);
|
||||
|
||||
~TemporaryBlobImpl() = default;
|
||||
|
||||
uint64_t mStartPos;
|
||||
RefPtr<nsTemporaryFileInputStream::FileDescOwner> mFileDescOwner;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TemporaryBlobImpl_h
|
142
dom/file/TemporaryFileBlobImpl.cpp
Normal file
142
dom/file/TemporaryFileBlobImpl.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
/* -*- 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 "TemporaryFileBlobImpl.h"
|
||||
|
||||
#include "IPCBlobInputStreamThread.h"
|
||||
#include "nsFileStreams.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
// Here the flags needed in order to keep the temporary file opened.
|
||||
// 1. REOPEN_ON_REWIND -> otherwise the stream is not serializable more than
|
||||
// once.
|
||||
// 2. no DEFER_OPEN -> the file must be kept open on windows in order to be
|
||||
// deleted when used.
|
||||
// 3. no CLOSE_ON_EOF -> the file will be closed by the DTOR. No needs. Also
|
||||
// because the inputStream will not be read directly.
|
||||
// 4. no SHARE_DELETE -> We don't want to allow this file to be deleted.
|
||||
const uint32_t sTemporaryFileStreamFlags =
|
||||
nsIFileInputStream::REOPEN_ON_REWIND;
|
||||
|
||||
class TemporaryFileInputStream final : public nsFileInputStream
|
||||
{
|
||||
public:
|
||||
static nsresult
|
||||
Create(nsIFile* aFile, nsIInputStream** aInputStream)
|
||||
{
|
||||
MOZ_ASSERT(aFile);
|
||||
MOZ_ASSERT(aInputStream);
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
RefPtr<TemporaryFileInputStream> stream =
|
||||
new TemporaryFileInputStream(aFile);
|
||||
|
||||
nsresult rv = stream->Init(aFile, -1, -1, sTemporaryFileStreamFlags);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
stream.forget(aInputStream);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
Serialize(InputStreamParams& aParams,
|
||||
FileDescriptorArray& aFileDescriptors) override
|
||||
{
|
||||
MOZ_CRASH("This inputStream cannot be serialized.");
|
||||
}
|
||||
|
||||
bool
|
||||
Deserialize(const InputStreamParams& aParams,
|
||||
const FileDescriptorArray& aFileDescriptors) override
|
||||
{
|
||||
MOZ_CRASH("This inputStream cannot be deserialized.");
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit TemporaryFileInputStream(nsIFile* aFile)
|
||||
: mFile(aFile)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
}
|
||||
|
||||
~TemporaryFileInputStream()
|
||||
{
|
||||
// Let's delete the file on the IPCBlob Thread.
|
||||
RefPtr<IPCBlobInputStreamThread> thread =
|
||||
IPCBlobInputStreamThread::GetOrCreate();
|
||||
if (NS_WARN_IF(!thread)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> file = Move(mFile);
|
||||
thread->Dispatch(NS_NewRunnableFunction(
|
||||
"TemporaryFileInputStream::Runnable",
|
||||
[file]() {
|
||||
file->Remove(false);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
};
|
||||
|
||||
} // anonymous
|
||||
|
||||
TemporaryFileBlobImpl::TemporaryFileBlobImpl(nsIFile* aFile,
|
||||
const nsAString& aContentType)
|
||||
: FileBlobImpl(aFile, EmptyString(), aContentType)
|
||||
#ifdef DEBUG
|
||||
, mInputStreamCreated(false)
|
||||
#endif
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
}
|
||||
|
||||
TemporaryFileBlobImpl::~TemporaryFileBlobImpl()
|
||||
{
|
||||
MOZ_ASSERT(mInputStreamCreated);
|
||||
}
|
||||
|
||||
already_AddRefed<BlobImpl>
|
||||
TemporaryFileBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
|
||||
const nsAString& aContentType,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_CRASH("This BlobImpl is not meant to be sliced!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
TemporaryFileBlobImpl::CreateInputStream(nsIInputStream** aStream, ErrorResult& aRv)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(!mInputStreamCreated);
|
||||
// CreateInputStream can be called only once.
|
||||
mInputStreamCreated = true;
|
||||
#endif
|
||||
|
||||
aRv = TemporaryFileInputStream::Create(mFile, aStream);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
46
dom/file/TemporaryFileBlobImpl.h
Normal file
46
dom/file/TemporaryFileBlobImpl.h
Normal file
@ -0,0 +1,46 @@
|
||||
/* -*- 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_dom_TemporaryFileBlobImpl_h
|
||||
#define mozilla_dom_TemporaryFileBlobImpl_h
|
||||
|
||||
#include "FileBlobImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// This class is meant to be used by TemporaryIPCBlobParent only.
|
||||
// Don't use it for anything else, please!
|
||||
// Note that CreateInputStream() _must_ be called, and called just once!
|
||||
|
||||
// This class is a BlobImpl because it needs to be sent via IPC using
|
||||
// IPCBlobUtils.
|
||||
class TemporaryFileBlobImpl final : public FileBlobImpl
|
||||
{
|
||||
#ifdef DEBUG
|
||||
bool mInputStreamCreated;
|
||||
#endif
|
||||
|
||||
public:
|
||||
explicit TemporaryFileBlobImpl(nsIFile* aFile, const nsAString& aContentType);
|
||||
|
||||
// Overrides
|
||||
virtual void CreateInputStream(nsIInputStream** aInputStream,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
protected:
|
||||
virtual ~TemporaryFileBlobImpl();
|
||||
|
||||
private:
|
||||
virtual already_AddRefed<BlobImpl>
|
||||
CreateSlice(uint64_t aStart, uint64_t aLength,
|
||||
const nsAString& aContentType, ErrorResult& aRv) override;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_TemporaryFileBlobImpl_h
|
@ -381,30 +381,33 @@ IPCBlobInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
|
||||
}
|
||||
|
||||
void
|
||||
IPCBlobInputStream::StreamReady(nsIInputStream* aInputStream)
|
||||
IPCBlobInputStream::StreamReady(already_AddRefed<nsIInputStream> aInputStream)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> inputStream = Move(aInputStream);
|
||||
|
||||
// We have been closed in the meantime.
|
||||
if (mState == eClosed) {
|
||||
if (aInputStream) {
|
||||
aInputStream->Close();
|
||||
if (inputStream) {
|
||||
inputStream->Close();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If aInputStream is null, it means that the serialization went wrong or the
|
||||
// If inputStream is null, it means that the serialization went wrong or the
|
||||
// stream is not available anymore. We keep the state as pending just to block
|
||||
// any additional operation.
|
||||
|
||||
if (!aInputStream) {
|
||||
if (!inputStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Now it's the right time to apply a slice if needed.
|
||||
if (mStart > 0 || mLength < mActor->Size()) {
|
||||
aInputStream = new SlicedInputStream(aInputStream, mStart, mLength);
|
||||
inputStream =
|
||||
new SlicedInputStream(inputStream.forget(), mStart, mLength);
|
||||
}
|
||||
|
||||
mRemoteStream = aInputStream;
|
||||
mRemoteStream = inputStream;
|
||||
|
||||
MOZ_ASSERT(mState == ePending);
|
||||
mState = eRunning;
|
||||
@ -475,7 +478,8 @@ IPCBlobInputStream::InitWithExistingRange(uint64_t aStart, uint64_t aLength)
|
||||
// because the stream is immediately consumable.
|
||||
if (mState == eRunning && mRemoteStream && XRE_IsParentProcess() &&
|
||||
(mStart > 0 || mLength < mActor->Size())) {
|
||||
mRemoteStream = new SlicedInputStream(mRemoteStream, mStart, mLength);
|
||||
mRemoteStream =
|
||||
new SlicedInputStream(mRemoteStream.forget(), mStart, mLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
explicit IPCBlobInputStream(IPCBlobInputStreamChild* aActor);
|
||||
|
||||
void
|
||||
StreamReady(nsIInputStream* aInputStream);
|
||||
StreamReady(already_AddRefed<nsIInputStream> aInputStream);
|
||||
|
||||
private:
|
||||
~IPCBlobInputStream();
|
||||
|
@ -71,10 +71,10 @@ class StreamReadyRunnable final : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
StreamReadyRunnable(IPCBlobInputStream* aDestinationStream,
|
||||
nsIInputStream* aCreatedStream)
|
||||
already_AddRefed<nsIInputStream> aCreatedStream)
|
||||
: CancelableRunnable("dom::StreamReadyRunnable")
|
||||
, mDestinationStream(aDestinationStream)
|
||||
, mCreatedStream(aCreatedStream)
|
||||
, mCreatedStream(Move(aCreatedStream))
|
||||
{
|
||||
MOZ_ASSERT(mDestinationStream);
|
||||
// mCreatedStream can be null.
|
||||
@ -83,7 +83,7 @@ public:
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
mDestinationStream->StreamReady(mCreatedStream);
|
||||
mDestinationStream->StreamReady(mCreatedStream.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -317,7 +317,7 @@ IPCBlobInputStreamChild::RecvStreamReady(const OptionalIPCStream& aStream)
|
||||
}
|
||||
|
||||
RefPtr<StreamReadyRunnable> runnable =
|
||||
new StreamReadyRunnable(pendingStream, stream);
|
||||
new StreamReadyRunnable(pendingStream, stream.forget());
|
||||
|
||||
// If IPCBlobInputStream::AsyncWait() has been executed without passing an
|
||||
// event target, we run the callback synchronous because any thread could be
|
||||
|
@ -172,7 +172,8 @@ IPCBlobInputStreamStorage::GetStream(const nsID& aID,
|
||||
|
||||
// Now it's the right time to apply a slice if needed.
|
||||
if (aStart > 0 || aLength < size) {
|
||||
clonedStream = new SlicedInputStream(clonedStream, aStart, aLength);
|
||||
clonedStream =
|
||||
new SlicedInputStream(clonedStream.forget(), aStart, aLength);
|
||||
}
|
||||
|
||||
clonedStream.forget(aInputStream);
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIObserver.h"
|
||||
|
||||
class nsIThread;
|
||||
|
||||
|
@ -33,7 +33,6 @@
|
||||
* - a memory buffer: MemoryBlobImpl
|
||||
* - a string: StringBlobImpl
|
||||
* - a real OS file: FileBlobImpl
|
||||
* - a temporary OS file: TemporaryBlobImpl
|
||||
* - a generic nsIInputStream: StreamBlobImpl
|
||||
* - an empty blob: EmptyBlobImpl
|
||||
* - more blobs combined together: MultipartBlobImpl
|
||||
|
41
dom/file/ipc/PTemporaryIPCBlob.ipdl
Normal file
41
dom/file/ipc/PTemporaryIPCBlob.ipdl
Normal file
@ -0,0 +1,41 @@
|
||||
/* 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 protocol PBackground;
|
||||
include protocol PChildToParentStream;
|
||||
include protocol PFileDescriptorSet;
|
||||
include protocol PIPCBlobInputStream;
|
||||
include protocol PParentToChildStream;
|
||||
|
||||
include IPCBlob;
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
union IPCBlobOrError
|
||||
{
|
||||
IPCBlob;
|
||||
nsresult;
|
||||
};
|
||||
|
||||
protocol PTemporaryIPCBlob
|
||||
{
|
||||
manager PBackground;
|
||||
|
||||
// When this actor is created on the child side, the parent will send
|
||||
// immediatelly back a FileDescriptor or a __delete__ in case of error.
|
||||
// When the FileDescriptor is received, the child has to call
|
||||
// OperationDone(). When OperationDone() is received on the parent side, the
|
||||
// parent actor will send a __delete__.
|
||||
|
||||
child:
|
||||
async FileDesc(FileDescriptor aFD);
|
||||
async __delete__(IPCBlobOrError aBlobOrError);
|
||||
|
||||
parent:
|
||||
async OperationDone(bool aSuccess, nsCString aContentType);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
90
dom/file/ipc/TemporaryIPCBlobChild.cpp
Normal file
90
dom/file/ipc/TemporaryIPCBlobChild.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
/* -*- 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 "TemporaryIPCBlobChild.h"
|
||||
#include "mozilla/dom/MutableBlobStorage.h"
|
||||
#include <private/pprio.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
TemporaryIPCBlobChild::TemporaryIPCBlobChild(MutableBlobStorage* aStorage)
|
||||
: mMutableBlobStorage(aStorage)
|
||||
, mActive(true)
|
||||
{
|
||||
MOZ_ASSERT(aStorage);
|
||||
}
|
||||
|
||||
TemporaryIPCBlobChild::~TemporaryIPCBlobChild()
|
||||
{}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TemporaryIPCBlobChild::RecvFileDesc(const FileDescriptor& aFD)
|
||||
{
|
||||
MOZ_ASSERT(mActive);
|
||||
|
||||
auto rawFD = aFD.ClonePlatformHandle();
|
||||
PRFileDesc* prfile = PR_ImportFile(PROsfd(rawFD.release()));
|
||||
|
||||
mMutableBlobStorage->TemporaryFileCreated(prfile);
|
||||
mMutableBlobStorage = nullptr;
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TemporaryIPCBlobChild::Recv__delete__(const IPCBlobOrError& aData)
|
||||
{
|
||||
mActive = false;
|
||||
mMutableBlobStorage = nullptr;
|
||||
|
||||
if (aData.type() == IPCBlobOrError::TIPCBlob) {
|
||||
// This must be always deserialized.
|
||||
RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(aData.get_IPCBlob());
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
if (mCallback) {
|
||||
mCallback->OperationSucceeded(blobImpl);
|
||||
}
|
||||
} else if(mCallback) {
|
||||
MOZ_ASSERT(aData.type() == IPCBlobOrError::Tnsresult);
|
||||
mCallback->OperationFailed(aData.get_nsresult());
|
||||
}
|
||||
|
||||
mCallback = nullptr;
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
TemporaryIPCBlobChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
mActive = false;
|
||||
mMutableBlobStorage = nullptr;
|
||||
|
||||
if (mCallback) {
|
||||
mCallback->OperationFailed(NS_ERROR_FAILURE);
|
||||
mCallback = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TemporaryIPCBlobChild::AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
|
||||
const nsACString& aContentType)
|
||||
{
|
||||
MOZ_ASSERT(aCallback);
|
||||
MOZ_ASSERT(!mCallback);
|
||||
|
||||
if (!mActive) {
|
||||
aCallback->OperationFailed(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
mCallback = aCallback;
|
||||
SendOperationDone(true, nsCString(aContentType));
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
58
dom/file/ipc/TemporaryIPCBlobChild.h
Normal file
58
dom/file/ipc/TemporaryIPCBlobChild.h
Normal file
@ -0,0 +1,58 @@
|
||||
/* -*- 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_dom_ipc_TemporaryIPCBlobChild_h
|
||||
#define mozilla_dom_ipc_TemporaryIPCBlobChild_h
|
||||
|
||||
#include "mozilla/ipc/PTemporaryIPCBlob.h"
|
||||
#include "mozilla/ipc/PTemporaryIPCBlobChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class MutableBlobStorage;
|
||||
|
||||
class TemporaryIPCBlobChildCallback
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
|
||||
|
||||
virtual void OperationSucceeded(BlobImpl* aBlobImpl) = 0;
|
||||
virtual void OperationFailed(nsresult aRv) = 0;
|
||||
};
|
||||
|
||||
class TemporaryIPCBlobChild final : public mozilla::ipc::PTemporaryIPCBlobChild
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(TemporaryIPCBlobChild)
|
||||
|
||||
explicit TemporaryIPCBlobChild(MutableBlobStorage* aMutableBlobStorage);
|
||||
|
||||
void
|
||||
AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
|
||||
const nsACString& aContentType);
|
||||
|
||||
private:
|
||||
~TemporaryIPCBlobChild();
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
RecvFileDesc(const FileDescriptor& aFD) override;
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
Recv__delete__(const IPCBlobOrError& aBlobOrError) override;
|
||||
|
||||
void
|
||||
ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
RefPtr<MutableBlobStorage> mMutableBlobStorage;
|
||||
RefPtr<TemporaryIPCBlobChildCallback> mCallback;
|
||||
bool mActive;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_ipc_TemporaryIPCBlobChild_h
|
105
dom/file/ipc/TemporaryIPCBlobParent.cpp
Normal file
105
dom/file/ipc/TemporaryIPCBlobParent.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
/* -*- 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 "TemporaryIPCBlobParent.h"
|
||||
|
||||
#include "mozilla/dom/FileBlobImpl.h"
|
||||
#include "nsAnonymousTemporaryFile.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "TemporaryFileBlobImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
TemporaryIPCBlobParent::TemporaryIPCBlobParent()
|
||||
: mActive(true)
|
||||
{}
|
||||
|
||||
TemporaryIPCBlobParent::~TemporaryIPCBlobParent()
|
||||
{
|
||||
// If we still have mFile, let's remove it.
|
||||
if (mFile) {
|
||||
mFile->Remove(false);
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TemporaryIPCBlobParent::CreateAndShareFile()
|
||||
{
|
||||
MOZ_ASSERT(mActive);
|
||||
MOZ_ASSERT(!mFile);
|
||||
|
||||
nsresult rv = NS_OpenAnonymousTemporaryNsIFile(getter_AddRefs(mFile));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return SendDeleteError(rv);
|
||||
}
|
||||
|
||||
PRFileDesc* fd;
|
||||
rv = mFile->OpenNSPRFileDesc(PR_RDWR, PR_IRWXU, &fd);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return SendDeleteError(rv);
|
||||
}
|
||||
|
||||
FileDescriptor fdd =
|
||||
FileDescriptor(FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(fd)));
|
||||
|
||||
// The FileDescriptor object owns a duplicate of the file handle; we
|
||||
// must close the original (and clean up the NSPR descriptor).
|
||||
PR_Close(fd);
|
||||
|
||||
Unused << SendFileDesc(fdd);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TemporaryIPCBlobParent::RecvOperationDone(const bool& aSuccess,
|
||||
const nsCString& aContentType)
|
||||
{
|
||||
MOZ_ASSERT(mActive);
|
||||
mActive = false;
|
||||
|
||||
if (!aSuccess) {
|
||||
// Nothing to do.
|
||||
Unused << Send__delete__(this, NS_ERROR_FAILURE);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Let's create the BlobImpl.
|
||||
nsCOMPtr<nsIFile> file = Move(mFile);
|
||||
|
||||
RefPtr<TemporaryFileBlobImpl> blobImpl =
|
||||
new TemporaryFileBlobImpl(file, NS_ConvertUTF8toUTF16(aContentType));
|
||||
|
||||
IPCBlob ipcBlob;
|
||||
nsresult rv = IPCBlobUtils::Serialize(blobImpl, Manager(), ipcBlob);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Unused << Send__delete__(this, NS_ERROR_FAILURE);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
Unused << Send__delete__(this, ipcBlob);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
TemporaryIPCBlobParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
mActive = false;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TemporaryIPCBlobParent::SendDeleteError(nsresult aRv)
|
||||
{
|
||||
MOZ_ASSERT(mActive);
|
||||
mActive = false;
|
||||
|
||||
Unused << Send__delete__(this, aRv);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
46
dom/file/ipc/TemporaryIPCBlobParent.h
Normal file
46
dom/file/ipc/TemporaryIPCBlobParent.h
Normal file
@ -0,0 +1,46 @@
|
||||
/* -*- 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_dom_ipc_TemporaryIPCBlobParent_h
|
||||
#define mozilla_dom_ipc_TemporaryIPCBlobParent_h
|
||||
|
||||
#include "mozilla/ipc/PTemporaryIPCBlob.h"
|
||||
#include "mozilla/ipc/PTemporaryIPCBlobParent.h"
|
||||
|
||||
class nsIFile;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class TemporaryIPCBlobParent final : public mozilla::ipc::PTemporaryIPCBlobParent
|
||||
{
|
||||
public:
|
||||
explicit TemporaryIPCBlobParent();
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
CreateAndShareFile();
|
||||
|
||||
private:
|
||||
~TemporaryIPCBlobParent();
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
RecvOperationDone(const bool& aSuccess,
|
||||
const nsCString& aContentType) override;
|
||||
|
||||
void
|
||||
ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
SendDeleteError(nsresult aRv);
|
||||
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
bool mActive;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_ipc_TemporaryIPCBlobParent_h
|
@ -11,6 +11,8 @@ EXPORTS.mozilla.dom.ipc += [
|
||||
'IPCBlobInputStreamStorage.h',
|
||||
'PendingIPCBlobChild.h',
|
||||
'PendingIPCBlobParent.h',
|
||||
'TemporaryIPCBlobChild.h',
|
||||
'TemporaryIPCBlobParent.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
@ -26,6 +28,8 @@ UNIFIED_SOURCES += [
|
||||
'IPCBlobUtils.cpp',
|
||||
'PendingIPCBlobChild.cpp',
|
||||
'PendingIPCBlobParent.cpp',
|
||||
'TemporaryIPCBlobChild.cpp',
|
||||
'TemporaryIPCBlobParent.cpp',
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
@ -33,6 +37,7 @@ IPDL_SOURCES += [
|
||||
'IPCBlob.ipdlh',
|
||||
'PIPCBlobInputStream.ipdl',
|
||||
'PPendingIPCBlob.ipdl',
|
||||
'PTemporaryIPCBlob.ipdl',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
|
@ -3,3 +3,5 @@ support-files =
|
||||
empty.html
|
||||
|
||||
[browser_ipcBlob.js]
|
||||
[browser_ipcBlob_temporary.js]
|
||||
support-files = temporary.sjs
|
||||
|
114
dom/file/ipc/tests/browser_ipcBlob_temporary.js
Normal file
114
dom/file/ipc/tests/browser_ipcBlob_temporary.js
Normal file
@ -0,0 +1,114 @@
|
||||
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
|
||||
requestLongerTimeout(3);
|
||||
|
||||
|
||||
const BASE_URI = "http://mochi.test:8888/browser/dom/file/ipc/tests/empty.html";
|
||||
|
||||
add_task(async function test() {
|
||||
await SpecialPowers.pushPrefEnv({ "set" : [
|
||||
["dom.blob.memoryToTemporaryFile", 1 ],
|
||||
["dom.ipc.processCount", 4],
|
||||
]});
|
||||
|
||||
let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI);
|
||||
let browser1 = gBrowser.getBrowserForTab(tab1);
|
||||
|
||||
let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI);
|
||||
let browser2 = gBrowser.getBrowserForTab(tab2);
|
||||
|
||||
await ContentTask.spawn(browser2, null, function() {
|
||||
content.window.testPromise = new content.window.Promise(resolve => {
|
||||
let bc = new content.window.BroadcastChannel('foobar');
|
||||
bc.onmessage = e => {
|
||||
function realTest() {
|
||||
return new content.window.Promise(resolve => {
|
||||
let count = 10;
|
||||
for (let i = 0; i < count; ++i) {
|
||||
info("FileReader at the same time: " + i);
|
||||
let fr = new content.window.FileReader();
|
||||
fr.readAsText(e.data);
|
||||
fr.onerror = () => {
|
||||
ok(false, "Something wrong happened.");
|
||||
}
|
||||
|
||||
fr.onloadend = () => {
|
||||
is (fr.result.length, e.data.size, "FileReader worked fine.");
|
||||
if (!--count) {
|
||||
resolve(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let promises = [];
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
promises.push(realTest());
|
||||
}
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
resolve(true);
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
let status = await ContentTask.spawn(browser1, null, function() {
|
||||
let p = new content.window.Promise(resolve => {
|
||||
let xhr = new content.window.XMLHttpRequest();
|
||||
xhr.open('GET', 'temporary.sjs', true);
|
||||
xhr.responseType = 'blob';
|
||||
xhr.onload = () => {
|
||||
resolve(xhr.response);
|
||||
}
|
||||
xhr.send();
|
||||
});
|
||||
|
||||
return p.then(blob => {
|
||||
function realTest() {
|
||||
return new content.window.Promise(resolve => {
|
||||
info("Let's broadcast the blob...");
|
||||
let bc = new content.window.BroadcastChannel('foobar');
|
||||
bc.postMessage(blob);
|
||||
|
||||
info("Here the test...");
|
||||
let count = 10;
|
||||
for (let i = 0; i < count; ++i) {
|
||||
info("FileReader at the same time: " + i);
|
||||
let fr = new content.window.FileReader();
|
||||
fr.readAsText(blob);
|
||||
fr.onerror = () => {
|
||||
ok(false, "Something wrong happened.");
|
||||
}
|
||||
|
||||
fr.onloadend = () => {
|
||||
is (fr.result.length, blob.size, "FileReader worked fine.");
|
||||
if (!--count) {
|
||||
resolve(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let promises = [];
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
promises.push(realTest());
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
});
|
||||
|
||||
ok(status, "All good for tab1!");
|
||||
|
||||
status = await ContentTask.spawn(browser2, null, function() {
|
||||
return content.window.testPromise;
|
||||
});
|
||||
|
||||
ok(status, "All good for tab2!");
|
||||
|
||||
await BrowserTestUtils.removeTab(tab1);
|
||||
await BrowserTestUtils.removeTab(tab2);
|
||||
});
|
7
dom/file/ipc/tests/temporary.sjs
Normal file
7
dom/file/ipc/tests/temporary.sjs
Normal file
@ -0,0 +1,7 @@
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
|
||||
var data = new Array(1024*64).join("1234567890ABCDEF");
|
||||
response.bodyOutputStream.write(data, data.length);
|
||||
}
|
@ -57,10 +57,11 @@ UNIFIED_SOURCES += [
|
||||
'nsHostObjectURI.cpp',
|
||||
'StreamBlobImpl.cpp',
|
||||
'StringBlobImpl.cpp',
|
||||
'TemporaryBlobImpl.cpp',
|
||||
'TemporaryFileBlobImpl.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/file/ipc',
|
||||
'/dom/workers',
|
||||
]
|
||||
|
||||
|
@ -403,9 +403,7 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement, nsGenericHTMLElement,
|
||||
mPrintState, mOriginalCanvas,
|
||||
mOffscreenCanvas)
|
||||
|
||||
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement,
|
||||
nsGenericHTMLElement,
|
||||
nsIDOMHTMLCanvasElement)
|
||||
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLCanvasElement, nsGenericHTMLElement)
|
||||
|
||||
NS_IMPL_ELEMENT_CLONE(HTMLCanvasElement)
|
||||
|
||||
@ -459,10 +457,6 @@ HTMLCanvasElement::GetWidthHeight()
|
||||
return size;
|
||||
}
|
||||
|
||||
NS_IMPL_UINT_ATTR_DEFAULT_VALUE(HTMLCanvasElement, Width, width, DEFAULT_CANVAS_WIDTH)
|
||||
NS_IMPL_UINT_ATTR_DEFAULT_VALUE(HTMLCanvasElement, Height, height, DEFAULT_CANVAS_HEIGHT)
|
||||
NS_IMPL_BOOL_ATTR(HTMLCanvasElement, MozOpaque, moz_opaque)
|
||||
|
||||
nsresult
|
||||
HTMLCanvasElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
|
||||
const nsAttrValue* aValue,
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIDOMHTMLCanvasElement.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
#include "nsGkAtoms.h"
|
||||
@ -116,7 +115,6 @@ protected:
|
||||
};
|
||||
|
||||
class HTMLCanvasElement final : public nsGenericHTMLElement,
|
||||
public nsIDOMHTMLCanvasElement,
|
||||
public CanvasRenderingContextHelper
|
||||
{
|
||||
enum {
|
||||
@ -138,9 +136,6 @@ public:
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// nsIDOMHTMLCanvasElement
|
||||
NS_DECL_NSIDOMHTMLCANVASELEMENT
|
||||
|
||||
// CC
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLCanvasElement,
|
||||
nsGenericHTMLElement)
|
||||
|
@ -34,7 +34,6 @@ interface nsICycleCollectorListener;
|
||||
interface nsIDOMNode;
|
||||
interface nsIDOMNodeList;
|
||||
interface nsIDOMElement;
|
||||
interface nsIDOMHTMLCanvasElement;
|
||||
interface nsIDOMEvent;
|
||||
interface nsIPreloadedStyleSheet;
|
||||
interface nsITransferable;
|
||||
@ -928,8 +927,8 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
*
|
||||
* This method requires chrome privileges.
|
||||
*/
|
||||
uint32_t compareCanvases(in nsIDOMHTMLCanvasElement aCanvas1,
|
||||
in nsIDOMHTMLCanvasElement aCanvas2,
|
||||
uint32_t compareCanvases(in nsISupports aCanvas1,
|
||||
in nsISupports aCanvas2,
|
||||
out unsigned long aMaxDifference);
|
||||
|
||||
/**
|
||||
|
@ -9,7 +9,6 @@ with Files("**"):
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDOMHTMLBaseElement.idl',
|
||||
'nsIDOMHTMLCanvasElement.idl',
|
||||
'nsIDOMHTMLCollection.idl',
|
||||
'nsIDOMHTMLDocument.idl',
|
||||
'nsIDOMHTMLElement.idl',
|
||||
|
@ -1,29 +0,0 @@
|
||||
/* -*- Mode: IDL; 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 "nsIDOMHTMLElement.idl"
|
||||
|
||||
/**
|
||||
* The nsIDOMHTMLCanvasElement interface is the interface to a HTML
|
||||
* <canvas> element.
|
||||
*
|
||||
* For more information on this interface, please see
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/#graphics
|
||||
*
|
||||
* @status UNDER_DEVELOPMENT
|
||||
*/
|
||||
|
||||
interface nsIDOMBlob;
|
||||
interface nsIVariant;
|
||||
interface nsIInputStreamCallback;
|
||||
|
||||
[uuid(4e8f1316-b601-471d-8f44-3c650d91ee9b)]
|
||||
interface nsIDOMHTMLCanvasElement : nsISupports
|
||||
{
|
||||
attribute unsigned long width;
|
||||
attribute unsigned long height;
|
||||
attribute boolean mozOpaque;
|
||||
};
|
||||
|
@ -587,13 +587,6 @@ ContentChild::RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
|
||||
InitXPCOM(aXPCOMInit, aInitialData);
|
||||
InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());
|
||||
|
||||
#ifdef NS_PRINTING
|
||||
// Force the creation of the nsPrintingProxy so that it's IPC counterpart,
|
||||
// PrintingParent, is always available for printing initiated from the parent.
|
||||
// Create nsPrintingProxy instance later than the SystemGroup initialization.
|
||||
RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance();
|
||||
#endif
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
@ -681,6 +674,12 @@ ContentChild::Init(MessageLoop* aIOLoop,
|
||||
mID = aChildID;
|
||||
mIsForBrowser = aIsForBrowser;
|
||||
|
||||
#ifdef NS_PRINTING
|
||||
// Force the creation of the nsPrintingProxy so that it's IPC counterpart,
|
||||
// PrintingParent, is always available for printing initiated from the parent.
|
||||
RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance();
|
||||
#endif
|
||||
|
||||
SetProcessName(NS_LITERAL_STRING("Web Content"));
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
@ -3036,11 +3035,16 @@ ContentChild::RecvShutdown()
|
||||
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCShutdownState"),
|
||||
NS_LITERAL_CSTRING("SendFinishShutdown"));
|
||||
#endif
|
||||
NS_LITERAL_CSTRING("SendFinishShutdown (sending)"));
|
||||
bool sent = SendFinishShutdown();
|
||||
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCShutdownState"),
|
||||
sent ? NS_LITERAL_CSTRING("SendFinishShutdown (sent)")
|
||||
: NS_LITERAL_CSTRING("SendFinishShutdown (failed)"));
|
||||
#else
|
||||
// Ignore errors here. If this fails, the parent will kill us after a
|
||||
// timeout.
|
||||
Unused << SendFinishShutdown();
|
||||
#endif
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -973,9 +973,11 @@ TabParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
|
||||
#ifdef XP_WIN
|
||||
a11y::WrapperFor(doc)->SetID(aMsaaID);
|
||||
MOZ_ASSERT(!aDocCOMProxy.IsNull());
|
||||
#ifdef NIGHTLY_BUILD
|
||||
if (aDocCOMProxy.IsNull()) {
|
||||
return IPC_FAIL(this, "Constructing a top-level PDocAccessible with null COM proxy");
|
||||
}
|
||||
#endif
|
||||
|
||||
RefPtr<IAccessible> proxy(aDocCOMProxy.Get());
|
||||
doc->SetCOMInterface(proxy);
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "nsIWritablePropertyBag2.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "nsSandboxFlags.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "nsILoadInfo.h"
|
||||
#include "nsContentSecurityManager.h"
|
||||
@ -234,7 +235,7 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
|
||||
|
||||
// New script entry point required, due to the "Create a script" step of
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol
|
||||
nsAutoMicroTask mt;
|
||||
mozilla::nsAutoMicroTask mt;
|
||||
AutoEntryScript aes(innerGlobal, "javascript: URI", true);
|
||||
JSContext* cx = aes.cx();
|
||||
JS::Rooted<JSObject*> globalJSObject(cx, innerGlobal->GetGlobalJSObject());
|
||||
|
@ -1,140 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "EncodedBufferCache.h"
|
||||
#include "prio.h"
|
||||
#include "nsAnonymousTemporaryFile.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void
|
||||
EncodedBufferCache::AppendBuffer(nsTArray<uint8_t> & aBuf)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
mDataSize += aBuf.Length();
|
||||
|
||||
mEncodedBuffers.AppendElement()->SwapElements(aBuf);
|
||||
|
||||
if (!mTempFileEnabled && mDataSize > mMaxMemoryStorage) {
|
||||
nsresult rv;
|
||||
PRFileDesc* tempFD = nullptr;
|
||||
{
|
||||
// Release the mMutex because of the sync dispatch to the main thread.
|
||||
MutexAutoUnlock unlock(mMutex);
|
||||
if (XRE_IsParentProcess()) {
|
||||
// In case we are in the parent process, do a synchronous I/O here to open a
|
||||
// temporary file.
|
||||
rv = NS_OpenAnonymousTemporaryFile(&tempFD);
|
||||
} else {
|
||||
// In case we are in the child process, we don't have access to open a file
|
||||
// directly due to sandbox restrictions, so we need to ask the parent process
|
||||
// to do that for us. In order to initiate the IPC, we need to first go to
|
||||
// the main thread. This is done by dispatching a runnable to the main thread.
|
||||
// From there, we start an asynchronous IPC, and we block the current thread
|
||||
// using a monitor while this async work is in progress. When we receive the
|
||||
// resulting file descriptor from the parent process, we notify the monitor
|
||||
// and unblock the current thread and continue.
|
||||
typedef dom::ContentChild::AnonymousTemporaryFileCallback
|
||||
AnonymousTemporaryFileCallback;
|
||||
bool done = false;
|
||||
Monitor monitor("EncodeBufferCache::AppendBuffer");
|
||||
RefPtr<dom::ContentChild> cc = dom::ContentChild::GetSingleton();
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NewRunnableMethod<AnonymousTemporaryFileCallback>(
|
||||
"dom::ContentChild::AsyncOpenAnonymousTemporaryFile",
|
||||
cc,
|
||||
&dom::ContentChild::AsyncOpenAnonymousTemporaryFile,
|
||||
[&](PRFileDesc* aFile) {
|
||||
rv = aFile ? NS_OK : NS_ERROR_FAILURE;
|
||||
tempFD = aFile;
|
||||
MonitorAutoLock lock(monitor);
|
||||
done = true;
|
||||
lock.Notify();
|
||||
});
|
||||
MonitorAutoLock lock(monitor);
|
||||
rv = NS_DispatchToMainThread(runnable);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
while (!done) {
|
||||
lock.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!NS_FAILED(rv)) {
|
||||
// Check the mDataSize again since we release the mMutex before.
|
||||
if (mDataSize > mMaxMemoryStorage) {
|
||||
mFD = tempFD;
|
||||
mTempFileEnabled = true;
|
||||
} else {
|
||||
// Close the tempFD because the data had been taken during the
|
||||
// MutexAutoUnlock.
|
||||
PR_Close(tempFD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mTempFileEnabled) {
|
||||
// has created temporary file, write buffer in it
|
||||
for (uint32_t i = 0; i < mEncodedBuffers.Length(); i++) {
|
||||
int32_t amount = PR_Write(mFD, mEncodedBuffers.ElementAt(i).Elements(), mEncodedBuffers.ElementAt(i).Length());
|
||||
if (amount < 0 || size_t(amount) < mEncodedBuffers.ElementAt(i).Length()) {
|
||||
NS_WARNING("Failed to write media cache block!");
|
||||
}
|
||||
}
|
||||
mEncodedBuffers.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
already_AddRefed<dom::Blob>
|
||||
EncodedBufferCache::ExtractBlob(nsISupports* aParent,
|
||||
const nsAString &aContentType)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
RefPtr<dom::Blob> blob;
|
||||
if (mTempFileEnabled) {
|
||||
// generate new temporary file to write
|
||||
blob = dom::Blob::CreateTemporaryBlob(aParent, mFD, 0, mDataSize,
|
||||
aContentType);
|
||||
// fallback to memory blob
|
||||
mTempFileEnabled = false;
|
||||
mDataSize = 0;
|
||||
mFD = nullptr;
|
||||
} else {
|
||||
void* blobData = malloc(mDataSize);
|
||||
NS_ASSERTION(blobData, "out of memory!!");
|
||||
|
||||
if (blobData) {
|
||||
for (uint32_t i = 0, offset = 0; i < mEncodedBuffers.Length(); i++) {
|
||||
memcpy((uint8_t*)blobData + offset, mEncodedBuffers.ElementAt(i).Elements(),
|
||||
mEncodedBuffers.ElementAt(i).Length());
|
||||
offset += mEncodedBuffers.ElementAt(i).Length();
|
||||
}
|
||||
blob = dom::Blob::CreateMemoryBlob(aParent, blobData, mDataSize,
|
||||
aContentType);
|
||||
mEncodedBuffers.Clear();
|
||||
} else
|
||||
return nullptr;
|
||||
}
|
||||
mDataSize = 0;
|
||||
return blob.forget();
|
||||
}
|
||||
|
||||
size_t
|
||||
EncodedBufferCache::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
return mEncodedBuffers.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
@ -1,66 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 EncodedBufferCache_h_
|
||||
#define EncodedBufferCache_h_
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
struct PRFileDesc;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class Blob;
|
||||
} // namespace dom
|
||||
|
||||
/**
|
||||
* Data is moved into a temporary file when it grows beyond
|
||||
* the maximal size passed in the Init function.
|
||||
* The AppendBuffer and ExtractBlob methods are thread-safe and can be called on
|
||||
* different threads at the same time.
|
||||
*/
|
||||
class EncodedBufferCache
|
||||
{
|
||||
public:
|
||||
explicit EncodedBufferCache(uint32_t aMaxMemoryStorage)
|
||||
: mFD(nullptr),
|
||||
mMutex("EncodedBufferCache.Data.Mutex"),
|
||||
mDataSize(0),
|
||||
mMaxMemoryStorage(aMaxMemoryStorage),
|
||||
mTempFileEnabled(false) { }
|
||||
~EncodedBufferCache()
|
||||
{
|
||||
}
|
||||
// Append buffers in cache, check if the queue is too large then switch to write buffer to file system
|
||||
// aBuf will append to mEncodedBuffers or temporary File, aBuf also be cleared
|
||||
void AppendBuffer(nsTArray<uint8_t> & aBuf);
|
||||
// Read all buffer from memory or file System, also Remove the temporary file or clean the buffers in memory.
|
||||
already_AddRefed<dom::Blob> ExtractBlob(nsISupports* aParent, const nsAString &aContentType);
|
||||
// Returns the heap size in bytes of our internal buffers.
|
||||
// Note that this intentionally ignores the data in the temp file.
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||
|
||||
private:
|
||||
//array for storing the encoded data.
|
||||
nsTArray<nsTArray<uint8_t> > mEncodedBuffers;
|
||||
// File handle for the temporary file
|
||||
PRFileDesc* mFD;
|
||||
// Used to protect the mEncodedBuffer for avoiding AppendBuffer/Consume on different thread at the same time.
|
||||
Mutex mMutex;
|
||||
// the current buffer size can be read
|
||||
uint64_t mDataSize;
|
||||
// The maximal buffer allowed in memory
|
||||
uint32_t mMaxMemoryStorage;
|
||||
// indicate the buffer is stored on temporary file or not
|
||||
bool mTempFileEnabled;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -9,7 +9,6 @@
|
||||
#include "AudioNodeEngine.h"
|
||||
#include "AudioNodeStream.h"
|
||||
#include "DOMMediaStream.h"
|
||||
#include "EncodedBufferCache.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "MediaDecoder.h"
|
||||
#include "MediaEncoder.h"
|
||||
@ -19,6 +18,7 @@
|
||||
#include "mozilla/dom/BlobEvent.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/MediaRecorderErrorEvent.h"
|
||||
#include "mozilla/dom/MutableBlobStorage.h"
|
||||
#include "mozilla/dom/VideoStreamTrack.h"
|
||||
#include "mozilla/media/MediaUtils.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
@ -201,11 +201,17 @@ class MediaRecorder::Session: public PrincipalChangeObserver<MediaStreamTrack>,
|
||||
// Main thread task.
|
||||
// Create a blob event and send back to client.
|
||||
class PushBlobRunnable : public Runnable
|
||||
, public MutableBlobStorageCallback
|
||||
{
|
||||
public:
|
||||
explicit PushBlobRunnable(Session* aSession)
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// aDestroyRunnable can be null. If it's not, it will be dispatched after
|
||||
// the PushBlobRunnable::Run().
|
||||
PushBlobRunnable(Session* aSession, Runnable* aDestroyRunnable)
|
||||
: Runnable("dom::MediaRecorder::Session::PushBlobRunnable")
|
||||
, mSession(aSession)
|
||||
, mDestroyRunnable(aDestroyRunnable)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
@ -213,21 +219,72 @@ class MediaRecorder::Session: public PrincipalChangeObserver<MediaStreamTrack>,
|
||||
LOG(LogLevel::Debug, ("Session.PushBlobRunnable s=(%p)", mSession.get()));
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mSession->GetBlobWhenReady(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
BlobStoreCompleted(MutableBlobStorage* aBlobStorage, Blob* aBlob,
|
||||
nsresult aRv) override
|
||||
{
|
||||
RefPtr<MediaRecorder> recorder = mSession->mRecorder;
|
||||
if (!recorder) {
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = recorder->CreateAndDispatchBlobEvent(mSession->GetEncodedData());
|
||||
if (NS_FAILED(aRv)) {
|
||||
recorder->NotifyError(aRv);
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = recorder->CreateAndDispatchBlobEvent(aBlob);
|
||||
if (NS_FAILED(rv)) {
|
||||
recorder->NotifyError(rv);
|
||||
recorder->NotifyError(aRv);
|
||||
}
|
||||
|
||||
if (mDestroyRunnable &&
|
||||
NS_FAILED(NS_DispatchToMainThread(mDestroyRunnable.forget()))) {
|
||||
MOZ_ASSERT(false, "NS_DispatchToMainThread failed");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
~PushBlobRunnable() = default;
|
||||
|
||||
RefPtr<Session> mSession;
|
||||
|
||||
// The generation of the blob is async. In order to avoid dispatching the
|
||||
// DestroyRunnable before pushing the blob event, we store the runnable
|
||||
// here.
|
||||
RefPtr<Runnable> mDestroyRunnable;
|
||||
};
|
||||
|
||||
class StoreEncodedBufferRunnable final : public Runnable
|
||||
{
|
||||
RefPtr<Session> mSession;
|
||||
nsTArray<nsTArray<uint8_t>> mBuffer;
|
||||
|
||||
public:
|
||||
StoreEncodedBufferRunnable(Session* aSession,
|
||||
nsTArray<nsTArray<uint8_t>>&& aBuffer)
|
||||
: Runnable("StoreEncodedBufferRunnable")
|
||||
, mSession(aSession)
|
||||
, mBuffer(Move(aBuffer))
|
||||
{}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
mSession->MaybeCreateMutableBlobStorage();
|
||||
for (uint32_t i = 0; i < mBuffer.Length(); i++) {
|
||||
if (!mBuffer[i].IsEmpty()) {
|
||||
mSession->mMutableBlobStorage->Append(mBuffer[i].Elements(),
|
||||
mBuffer[i].Length());
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<Session> mSession;
|
||||
};
|
||||
|
||||
// Notify encoder error, run in main thread task. (Bug 1095381)
|
||||
@ -433,9 +490,8 @@ public:
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
uint32_t maxMem = Preferences::GetUint("media.recorder.max_memory",
|
||||
MAX_ALLOW_MEMORY_BUFFER);
|
||||
mEncodedBufferCache = new EncodedBufferCache(maxMem);
|
||||
mMaxMemory = Preferences::GetUint("media.recorder.max_memory",
|
||||
MAX_ALLOW_MEMORY_BUFFER);
|
||||
mLastBlobTimeStamp = TimeStamp::Now();
|
||||
}
|
||||
|
||||
@ -551,7 +607,7 @@ public:
|
||||
LOG(LogLevel::Debug, ("Session.RequestData"));
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
|
||||
if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this, nullptr)))) {
|
||||
MOZ_ASSERT(false, "RequestData NS_DispatchToMainThread failed");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -559,19 +615,35 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIDOMBlob> GetEncodedData()
|
||||
void
|
||||
MaybeCreateMutableBlobStorage()
|
||||
{
|
||||
if (!mMutableBlobStorage) {
|
||||
mMutableBlobStorage =
|
||||
new MutableBlobStorage(MutableBlobStorage::eCouldBeInTemporaryFile,
|
||||
nullptr, mMaxMemory);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GetBlobWhenReady(MutableBlobStorageCallback* aCallback)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mEncodedBufferCache->ExtractBlob(mRecorder->GetParentObject(),
|
||||
mMimeType);
|
||||
|
||||
MaybeCreateMutableBlobStorage();
|
||||
mMutableBlobStorage->GetBlobWhenReady(mRecorder->GetParentObject(),
|
||||
NS_ConvertUTF16toUTF8(mMimeType),
|
||||
aCallback);
|
||||
mMutableBlobStorage = nullptr;
|
||||
}
|
||||
|
||||
RefPtr<SizeOfPromise>
|
||||
SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
size_t encodedBufferSize =
|
||||
mEncodedBufferCache->SizeOfExcludingThis(aMallocSizeOf);
|
||||
size_t encodedBufferSize = mMutableBlobStorage
|
||||
? mMutableBlobStorage->SizeOfCurrentMemoryBuffer()
|
||||
: 0;
|
||||
|
||||
if (!mEncoder) {
|
||||
return SizeOfPromise::CreateAndResolve(encodedBufferSize, __func__);
|
||||
@ -593,11 +665,11 @@ private:
|
||||
MOZ_ASSERT(mShutdownPromise);
|
||||
LOG(LogLevel::Debug, ("Session.~Session (%p)", this));
|
||||
}
|
||||
// Pull encoded media data from MediaEncoder and put into EncodedBufferCache.
|
||||
// Pull encoded media data from MediaEncoder and put into MutableBlobStorage.
|
||||
// Destroy this session object in the end of this function.
|
||||
// If the bool aForceFlush is true, we will force to dispatch a
|
||||
// PushBlobRunnable to main thread.
|
||||
void Extract(bool aForceFlush)
|
||||
void Extract(bool aForceFlush, Runnable* aDestroyRunnable)
|
||||
{
|
||||
MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
|
||||
|
||||
@ -615,11 +687,8 @@ private:
|
||||
}
|
||||
|
||||
// Append pulled data into cache buffer.
|
||||
for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
|
||||
if (!encodedBuf[i].IsEmpty()) {
|
||||
mEncodedBufferCache->AppendBuffer(encodedBuf[i]);
|
||||
}
|
||||
}
|
||||
NS_DispatchToMainThread(new StoreEncodedBufferRunnable(this,
|
||||
Move(encodedBuf)));
|
||||
|
||||
// Whether push encoded data back to onDataAvailable automatically or we
|
||||
// need a flush.
|
||||
@ -630,11 +699,15 @@ private:
|
||||
pushBlob = true;
|
||||
}
|
||||
if (pushBlob) {
|
||||
if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
|
||||
if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this, aDestroyRunnable)))) {
|
||||
MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
|
||||
} else {
|
||||
mLastBlobTimeStamp = TimeStamp::Now();
|
||||
}
|
||||
} else if (aDestroyRunnable) {
|
||||
if (NS_FAILED(NS_DispatchToMainThread(aDestroyRunnable))) {
|
||||
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -894,15 +967,20 @@ private:
|
||||
&MediaRecorder::NotifyError,
|
||||
rv));
|
||||
}
|
||||
|
||||
RefPtr<Runnable> destroyRunnable = new DestroyRunnable(this);
|
||||
|
||||
if (rv != NS_ERROR_DOM_SECURITY_ERR) {
|
||||
// Don't push a blob if there was a security error.
|
||||
if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
|
||||
if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this, destroyRunnable)))) {
|
||||
MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
|
||||
}
|
||||
} else {
|
||||
if (NS_FAILED(NS_DispatchToMainThread(destroyRunnable))) {
|
||||
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
|
||||
}
|
||||
}
|
||||
if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(this)))) {
|
||||
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
|
||||
}
|
||||
|
||||
mNeedSessionEndTask = false;
|
||||
}
|
||||
|
||||
@ -919,11 +997,8 @@ private:
|
||||
}
|
||||
|
||||
// Append pulled data into cache buffer.
|
||||
for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
|
||||
if (!encodedBuf[i].IsEmpty()) {
|
||||
mEncodedBufferCache->AppendBuffer(encodedBuf[i]);
|
||||
}
|
||||
}
|
||||
NS_DispatchToMainThread(new StoreEncodedBufferRunnable(this,
|
||||
Move(encodedBuf)));
|
||||
}
|
||||
|
||||
void MediaEncoderDataAvailable()
|
||||
@ -936,7 +1011,7 @@ private:
|
||||
mIsStartEventFired = true;
|
||||
}
|
||||
|
||||
Extract(false);
|
||||
Extract(false, nullptr);
|
||||
}
|
||||
|
||||
void MediaEncoderError()
|
||||
@ -953,14 +1028,11 @@ private:
|
||||
MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
|
||||
MOZ_ASSERT(mEncoder->IsShutdown());
|
||||
|
||||
// Forces the last blob even if it's not time for it yet.
|
||||
Extract(true);
|
||||
// For the stop event. Let's the creation of the blob to dispatch this runnable.
|
||||
RefPtr<Runnable> destroyRunnable = new DestroyRunnable(this);
|
||||
|
||||
// For the stop event.
|
||||
if (NS_FAILED(NS_DispatchToMainThread(
|
||||
new DestroyRunnable(this)))) {
|
||||
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
|
||||
}
|
||||
// Forces the last blob even if it's not time for it yet.
|
||||
Extract(true, destroyRunnable);
|
||||
|
||||
// Clean up.
|
||||
mEncoderListener->Forget();
|
||||
@ -1065,15 +1137,17 @@ private:
|
||||
// Set in Shutdown() and resolved when shutdown is complete.
|
||||
RefPtr<ShutdownPromise> mShutdownPromise;
|
||||
// A buffer to cache encoded media data.
|
||||
nsAutoPtr<EncodedBufferCache> mEncodedBufferCache;
|
||||
RefPtr<MutableBlobStorage> mMutableBlobStorage;
|
||||
// Max memory to use for the MutableBlobStorage.
|
||||
uint64_t mMaxMemory;
|
||||
// Current session mimeType
|
||||
nsString mMimeType;
|
||||
// Timestamp of the last fired dataavailable event.
|
||||
TimeStamp mLastBlobTimeStamp;
|
||||
// The interval of passing encoded data from EncodedBufferCache to onDataAvailable
|
||||
// handler. "mTimeSlice < 0" means Session object does not push encoded data to
|
||||
// onDataAvailable, instead, it passive wait the client side pull encoded data
|
||||
// by calling requestData API.
|
||||
// The interval of passing encoded data from MutableBlobStorage to
|
||||
// onDataAvailable handler. "mTimeSlice < 0" means Session object does not
|
||||
// push encoded data to onDataAvailable, instead, it passive wait the client
|
||||
// side pull encoded data by calling requestData API.
|
||||
const int32_t mTimeSlice;
|
||||
// Indicate this session's stop has been called.
|
||||
bool mStopIssued;
|
||||
@ -1085,6 +1159,8 @@ private:
|
||||
bool mNeedSessionEndTask;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(MediaRecorder::Session::PushBlobRunnable, Runnable)
|
||||
|
||||
MediaRecorder::~MediaRecorder()
|
||||
{
|
||||
LOG(LogLevel::Debug, ("~MediaRecorder (%p)", this));
|
||||
@ -1448,16 +1524,14 @@ MediaRecorder::IsTypeSupported(const nsAString& aMIMEType)
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaRecorder::CreateAndDispatchBlobEvent(already_AddRefed<nsIDOMBlob>&& aBlob)
|
||||
MediaRecorder::CreateAndDispatchBlobEvent(Blob* aBlob)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
|
||||
|
||||
BlobEventInit init;
|
||||
init.mBubbles = false;
|
||||
init.mCancelable = false;
|
||||
|
||||
nsCOMPtr<nsIDOMBlob> blob = aBlob;
|
||||
init.mData = static_cast<Blob*>(blob.get());
|
||||
init.mData = aBlob;
|
||||
|
||||
RefPtr<BlobEvent> event =
|
||||
BlobEvent::Constructor(this,
|
||||
|
@ -26,16 +26,17 @@ class GlobalObject;
|
||||
namespace dom {
|
||||
|
||||
class AudioNode;
|
||||
class Blob;
|
||||
class DOMException;
|
||||
|
||||
/**
|
||||
* Implementation of https://dvcs.w3.org/hg/dap/raw-file/default/media-stream-capture/MediaRecorder.html
|
||||
* The MediaRecorder accepts a mediaStream as input source passed from UA. When recorder starts,
|
||||
* a MediaEncoder will be created and accept the mediaStream as input source.
|
||||
* Encoder will get the raw data by track data changes, encode it by selected MIME Type, then store the encoded in EncodedBufferCache object.
|
||||
* Encoder will get the raw data by track data changes, encode it by selected MIME Type, then store the encoded in a MutableBlobStorage object.
|
||||
* The encoded data will be extracted on every timeslice passed from Start function call or by RequestData function.
|
||||
* Thread model:
|
||||
* When the recorder starts, it creates a "Media Encoder" thread to read data from MediaEncoder object and store buffer in EncodedBufferCache object.
|
||||
* When the recorder starts, it creates a "Media Encoder" thread to read data from MediaEncoder object and store buffer in MutableBlobStorage object.
|
||||
* Also extract the encoded data and create blobs on every timeslice passed from start function or RequestData function called by UA.
|
||||
*/
|
||||
|
||||
@ -72,7 +73,7 @@ public:
|
||||
void Pause(ErrorResult& aResult);
|
||||
// Resume a paused recording.
|
||||
void Resume(ErrorResult& aResult);
|
||||
// Extract encoded data Blob from EncodedBufferCache.
|
||||
// Extract encoded data Blob from MutableBlobStorage.
|
||||
void RequestData(ErrorResult& aResult);
|
||||
// Return the The DOMMediaStream passed from UA.
|
||||
DOMMediaStream* Stream() const { return mDOMStream; }
|
||||
@ -121,7 +122,7 @@ protected:
|
||||
|
||||
MediaRecorder& operator = (const MediaRecorder& x) = delete;
|
||||
// Create dataavailable event with Blob data and it runs in main thread
|
||||
nsresult CreateAndDispatchBlobEvent(already_AddRefed<nsIDOMBlob>&& aBlob);
|
||||
nsresult CreateAndDispatchBlobEvent(Blob* aBlob);
|
||||
// Creating a simple event to notify UA simple event.
|
||||
void DispatchSimpleEvent(const nsAString & aStr);
|
||||
// Creating a error event with message.
|
||||
|
@ -104,7 +104,6 @@ EXPORTS += [
|
||||
'DecoderDoctorDiagnostics.h',
|
||||
'DecoderTraits.h',
|
||||
'DOMMediaStream.h',
|
||||
'EncodedBufferCache.h',
|
||||
'FileBlockCache.h',
|
||||
'FrameStatistics.h',
|
||||
'Intervals.h',
|
||||
@ -219,7 +218,6 @@ UNIFIED_SOURCES += [
|
||||
'CubebUtils.cpp',
|
||||
'DecoderDoctorDiagnostics.cpp',
|
||||
'DOMMediaStream.cpp',
|
||||
'EncodedBufferCache.cpp',
|
||||
'FileBlockCache.cpp',
|
||||
'FileMediaResource.cpp',
|
||||
'GetUserMediaRequest.cpp',
|
||||
|
@ -71,8 +71,22 @@ PerformanceResourceTiming::~PerformanceResourceTiming()
|
||||
DOMHighResTimeStamp
|
||||
PerformanceResourceTiming::StartTime() const
|
||||
{
|
||||
DOMHighResTimeStamp startTime = mTiming->RedirectStartHighRes();
|
||||
return startTime ? startTime : mTiming->FetchStartHighRes();
|
||||
// Force the start time to be the earliest of:
|
||||
// - RedirectStart
|
||||
// - WorkerStart
|
||||
// - AsyncOpen
|
||||
// Ignore zero values. The RedirectStart and WorkerStart values
|
||||
// can come from earlier redirected channels prior to the AsyncOpen
|
||||
// time being recorded.
|
||||
DOMHighResTimeStamp redirect = mTiming->RedirectStartHighRes();
|
||||
redirect = redirect ? redirect : DBL_MAX;
|
||||
|
||||
DOMHighResTimeStamp worker = mTiming->WorkerStartHighRes();
|
||||
worker = worker ? worker : DBL_MAX;
|
||||
|
||||
DOMHighResTimeStamp asyncOpen = mTiming->AsyncOpenHighRes();
|
||||
|
||||
return std::min(asyncOpen, std::min(redirect, worker));
|
||||
}
|
||||
|
||||
JSObject*
|
||||
|
@ -63,6 +63,12 @@ public:
|
||||
mNextHopProtocol = aNextHopProtocol;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp WorkerStart() const {
|
||||
return mTiming && mTiming->TimingAllowed()
|
||||
? mTiming->WorkerStartHighRes()
|
||||
: 0;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp FetchStart() const {
|
||||
return mTiming
|
||||
? mTiming->FetchStartHighRes()
|
||||
|
@ -71,6 +71,7 @@ PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel)
|
||||
{
|
||||
if (aChannel) {
|
||||
aChannel->GetAsyncOpen(&mAsyncOpen);
|
||||
aChannel->GetDispatchFetchEventStart(&mWorkerStart);
|
||||
aChannel->GetAllRedirectsSameOrigin(&mAllRedirectsSameOrigin);
|
||||
aChannel->GetRedirectCount(&mRedirectCount);
|
||||
aChannel->GetRedirectStart(&mRedirectStart);
|
||||
@ -86,30 +87,38 @@ PerformanceTiming::InitializeTimingInfo(nsITimedChannel* aChannel)
|
||||
aChannel->GetResponseEnd(&mResponseEnd);
|
||||
aChannel->GetCacheReadEnd(&mCacheReadEnd);
|
||||
|
||||
// the performance timing api essentially requires that the event timestamps
|
||||
// are >= asyncOpen().. but in truth the browser engages in a number of
|
||||
// speculative activities that sometimes mean connections and lookups begin
|
||||
// earlier. Workaround that here by just using asyncOpen as the minimum
|
||||
// timestamp for dns and connection info.
|
||||
// The performance timing api essentially requires that the event timestamps
|
||||
// have a strict relation with each other. The truth, however, is the browser
|
||||
// engages in a number of speculative activities that sometimes mean connections
|
||||
// and lookups begin at different times. Workaround that here by clamping
|
||||
// these values to what we expect FetchStart to be. This means the later of
|
||||
// AsyncOpen or WorkerStart times.
|
||||
if (!mAsyncOpen.IsNull()) {
|
||||
if (!mDomainLookupStart.IsNull() && mDomainLookupStart < mAsyncOpen) {
|
||||
mDomainLookupStart = mAsyncOpen;
|
||||
// We want to clamp to the expected FetchStart value. This is later of
|
||||
// the AsyncOpen and WorkerStart values.
|
||||
const TimeStamp* clampTime = &mAsyncOpen;
|
||||
if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) {
|
||||
clampTime = &mWorkerStart;
|
||||
}
|
||||
|
||||
if (!mDomainLookupEnd.IsNull() && mDomainLookupEnd < mAsyncOpen) {
|
||||
mDomainLookupEnd = mAsyncOpen;
|
||||
if (!mDomainLookupStart.IsNull() && mDomainLookupStart < *clampTime) {
|
||||
mDomainLookupStart = *clampTime;
|
||||
}
|
||||
|
||||
if (!mConnectStart.IsNull() && mConnectStart < mAsyncOpen) {
|
||||
mConnectStart = mAsyncOpen;
|
||||
if (!mDomainLookupEnd.IsNull() && mDomainLookupEnd < *clampTime) {
|
||||
mDomainLookupEnd = *clampTime;
|
||||
}
|
||||
|
||||
if (!mSecureConnectionStart.IsNull() && mSecureConnectionStart < mAsyncOpen) {
|
||||
mSecureConnectionStart = mAsyncOpen;
|
||||
if (!mConnectStart.IsNull() && mConnectStart < *clampTime) {
|
||||
mConnectStart = *clampTime;
|
||||
}
|
||||
|
||||
if (!mConnectEnd.IsNull() && mConnectEnd < mAsyncOpen) {
|
||||
mConnectEnd = mAsyncOpen;
|
||||
if (!mSecureConnectionStart.IsNull() && mSecureConnectionStart < *clampTime) {
|
||||
mSecureConnectionStart = *clampTime;
|
||||
}
|
||||
|
||||
if (!mConnectEnd.IsNull() && mConnectEnd < *clampTime) {
|
||||
mConnectEnd = *clampTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -129,9 +138,13 @@ PerformanceTiming::FetchStartHighRes()
|
||||
}
|
||||
MOZ_ASSERT(!mAsyncOpen.IsNull(), "The fetch start time stamp should always be "
|
||||
"valid if the performance timing is enabled");
|
||||
mFetchStart = (!mAsyncOpen.IsNull())
|
||||
? TimeStampToDOMHighRes(mAsyncOpen)
|
||||
: 0.0;
|
||||
if (!mAsyncOpen.IsNull()) {
|
||||
if (!mWorkerStart.IsNull() && mWorkerStart > mAsyncOpen) {
|
||||
mFetchStart = TimeStampToDOMHighRes(mWorkerStart);
|
||||
} else {
|
||||
mFetchStart = TimeStampToDOMHighRes(mAsyncOpen);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mFetchStart;
|
||||
}
|
||||
@ -178,7 +191,7 @@ PerformanceTiming::TimingAllowed() const
|
||||
return mTimingAllowed;
|
||||
}
|
||||
|
||||
uint16_t
|
||||
uint8_t
|
||||
PerformanceTiming::GetRedirectCount() const
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
|
||||
@ -205,6 +218,26 @@ PerformanceTiming::ShouldReportCrossOriginRedirect() const
|
||||
return (mRedirectCount != 0) && mReportCrossOriginRedirect;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceTiming::AsyncOpenHighRes()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
|
||||
nsContentUtils::ShouldResistFingerprinting() || mAsyncOpen.IsNull()) {
|
||||
return mZeroTime;
|
||||
}
|
||||
return TimeStampToDOMHighRes(mAsyncOpen);
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp
|
||||
PerformanceTiming::WorkerStartHighRes()
|
||||
{
|
||||
if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized() ||
|
||||
nsContentUtils::ShouldResistFingerprinting() || mWorkerStart.IsNull()) {
|
||||
return mZeroTime;
|
||||
}
|
||||
return TimeStampToDOMHighRes(mWorkerStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* RedirectStartHighRes() is used by both the navigation timing and the
|
||||
* resource timing. Since, navigation timing and resource timing check and
|
||||
|
@ -141,7 +141,7 @@ public:
|
||||
return GetDOMTiming()->GetUnloadEventEnd();
|
||||
}
|
||||
|
||||
uint16_t GetRedirectCount() const;
|
||||
uint8_t GetRedirectCount() const;
|
||||
|
||||
// Checks if the resource is either same origin as the page that started
|
||||
// the load, or if the response contains the Timing-Allow-Origin header
|
||||
@ -157,7 +157,12 @@ public:
|
||||
// the timing-allow-origin check in HttpBaseChannel::TimingAllowCheck
|
||||
bool ShouldReportCrossOriginRedirect() const;
|
||||
|
||||
// The last channel's AsyncOpen time. This may occur before the FetchStart
|
||||
// in some cases.
|
||||
DOMHighResTimeStamp AsyncOpenHighRes();
|
||||
|
||||
// High resolution (used by resource timing)
|
||||
DOMHighResTimeStamp WorkerStartHighRes();
|
||||
DOMHighResTimeStamp FetchStartHighRes();
|
||||
DOMHighResTimeStamp RedirectStartHighRes();
|
||||
DOMHighResTimeStamp RedirectEndHighRes();
|
||||
@ -273,6 +278,7 @@ private:
|
||||
DOMHighResTimeStamp mZeroTime;
|
||||
|
||||
TimeStamp mAsyncOpen;
|
||||
TimeStamp mWorkerStart;
|
||||
TimeStamp mRedirectStart;
|
||||
TimeStamp mRedirectEnd;
|
||||
TimeStamp mDomainLookupStart;
|
||||
@ -285,7 +291,7 @@ private:
|
||||
TimeStamp mCacheReadStart;
|
||||
TimeStamp mResponseEnd;
|
||||
TimeStamp mCacheReadEnd;
|
||||
uint16_t mRedirectCount;
|
||||
uint8_t mRedirectCount;
|
||||
bool mTimingAllowed;
|
||||
bool mAllRedirectsSameOrigin;
|
||||
bool mInitialized;
|
||||
|
@ -38,6 +38,7 @@ class nsSMILTimeValueSpec
|
||||
{
|
||||
public:
|
||||
typedef mozilla::dom::Element Element;
|
||||
typedef mozilla::dom::IDTracker IDTracker;
|
||||
|
||||
nsSMILTimeValueSpec(nsSMILTimedElement& aOwner, bool aIsBegin);
|
||||
~nsSMILTimeValueSpec();
|
||||
@ -86,10 +87,22 @@ protected:
|
||||
// the target.
|
||||
nsSMILTimeValueSpecParams mParams;
|
||||
|
||||
class TimeReferenceElement : public mozilla::dom::IDTracker
|
||||
/**
|
||||
* If our nsSMILTimeValueSpec exists for a 'begin' or 'end' attribute with a
|
||||
* value that specifies a time that is relative to the animation of some
|
||||
* other element, it will create an instance of this class to reference and
|
||||
* track that other element. For example, if the nsSMILTimeValueSpec is for
|
||||
* end='a.end+2s', an instance of this class will be created to track the
|
||||
* element associated with the element ID "a". This class will notify the
|
||||
* nsSMILTimeValueSpec if the element that that ID identifies changes to a
|
||||
* different element (or none).
|
||||
*/
|
||||
class TimeReferenceTracker final : public IDTracker
|
||||
{
|
||||
public:
|
||||
explicit TimeReferenceElement(nsSMILTimeValueSpec* aOwner) : mSpec(aOwner) { }
|
||||
explicit TimeReferenceTracker(nsSMILTimeValueSpec* aOwner)
|
||||
: mSpec(aOwner)
|
||||
{}
|
||||
void ResetWithElement(Element* aTo) {
|
||||
RefPtr<Element> from = get();
|
||||
Unlink();
|
||||
@ -107,7 +120,7 @@ protected:
|
||||
nsSMILTimeValueSpec* mSpec;
|
||||
};
|
||||
|
||||
TimeReferenceElement mReferencedElement;
|
||||
TimeReferenceTracker mReferencedElement;
|
||||
|
||||
class EventListener final : public nsIDOMEventListener
|
||||
{
|
||||
|
@ -99,10 +99,19 @@ public:
|
||||
const nsAString& aHrefStr);
|
||||
void AnimationTargetChanged();
|
||||
|
||||
class TargetReference : public mozilla::dom::IDTracker {
|
||||
/**
|
||||
* Helper that provides a reference to the element with the ID that is
|
||||
* referenced by the animation element's 'href' attribute, if any, and that
|
||||
* will notify the animation element if the element that that ID identifies
|
||||
* changes to a different element (or none). (If the 'href' attribute is not
|
||||
* specified, then the animation target is the parent element and this helper
|
||||
* is not used.)
|
||||
*/
|
||||
class HrefTargetTracker final : public IDTracker {
|
||||
public:
|
||||
explicit TargetReference(SVGAnimationElement* aAnimationElement) :
|
||||
mAnimationElement(aAnimationElement) {}
|
||||
explicit HrefTargetTracker(SVGAnimationElement* aAnimationElement)
|
||||
: mAnimationElement(aAnimationElement)
|
||||
{}
|
||||
protected:
|
||||
// We need to be notified when target changes, in order to request a
|
||||
// sample (which will clear animation effects from old target and apply
|
||||
@ -119,7 +128,7 @@ public:
|
||||
SVGAnimationElement* const mAnimationElement;
|
||||
};
|
||||
|
||||
TargetReference mHrefTarget;
|
||||
HrefTargetTracker mHrefTarget;
|
||||
nsSMILTimedElement mTimedElement;
|
||||
};
|
||||
|
||||
|
@ -41,7 +41,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGMPathElement,
|
||||
SVGMPathElementBase)
|
||||
tmp->mHrefTarget.Traverse(&cb);
|
||||
tmp->mPathTracker.Traverse(&cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@ -56,8 +56,8 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(SVGMPathElement,
|
||||
|
||||
// Constructor
|
||||
SVGMPathElement::SVGMPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
||||
: SVGMPathElementBase(aNodeInfo),
|
||||
mHrefTarget(this)
|
||||
: SVGMPathElementBase(aNodeInfo)
|
||||
, mPathTracker(this)
|
||||
{
|
||||
}
|
||||
|
||||
@ -88,7 +88,7 @@ SVGMPathElement::BindToTree(nsIDocument* aDocument,
|
||||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers)
|
||||
{
|
||||
MOZ_ASSERT(!mHrefTarget.get(),
|
||||
MOZ_ASSERT(!mPathTracker.get(),
|
||||
"Shouldn't have href-target yet (or it should've been cleared)");
|
||||
nsresult rv = SVGMPathElementBase::BindToTree(aDocument, aParent,
|
||||
aBindingParent,
|
||||
@ -205,13 +205,13 @@ SVGMPathElement::GetReferencedPath()
|
||||
{
|
||||
if (!HasAttr(kNameSpaceID_XLink, nsGkAtoms::href) &&
|
||||
!HasAttr(kNameSpaceID_None, nsGkAtoms::href)) {
|
||||
MOZ_ASSERT(!mHrefTarget.get(),
|
||||
MOZ_ASSERT(!mPathTracker.get(),
|
||||
"We shouldn't have a href target "
|
||||
"if we don't have an xlink:href or href attribute");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent* genericTarget = mHrefTarget.get();
|
||||
nsIContent* genericTarget = mPathTracker.get();
|
||||
if (genericTarget && genericTarget->IsSVGElement(nsGkAtoms::path)) {
|
||||
return static_cast<SVGPathElement*>(genericTarget);
|
||||
}
|
||||
@ -231,24 +231,24 @@ SVGMPathElement::UpdateHrefTarget(nsIContent* aParent,
|
||||
aHrefStr, OwnerDoc(), baseURI);
|
||||
|
||||
// Stop observing old target (if any)
|
||||
if (mHrefTarget.get()) {
|
||||
mHrefTarget.get()->RemoveMutationObserver(this);
|
||||
if (mPathTracker.get()) {
|
||||
mPathTracker.get()->RemoveMutationObserver(this);
|
||||
}
|
||||
|
||||
if (aParent) {
|
||||
// Pass in |aParent| instead of |this| -- first argument is only used
|
||||
// for a call to GetComposedDoc(), and |this| might not have a current
|
||||
// document yet (if our caller is BindToTree).
|
||||
mHrefTarget.Reset(aParent, targetURI);
|
||||
mPathTracker.Reset(aParent, targetURI);
|
||||
} else {
|
||||
// if we don't have a parent, then there's no animateMotion element
|
||||
// depending on our target, so there's no point tracking it right now.
|
||||
mHrefTarget.Unlink();
|
||||
mPathTracker.Unlink();
|
||||
}
|
||||
|
||||
// Start observing new target (if any)
|
||||
if (mHrefTarget.get()) {
|
||||
mHrefTarget.get()->AddMutationObserver(this);
|
||||
if (mPathTracker.get()) {
|
||||
mPathTracker.get()->AddMutationObserver(this);
|
||||
}
|
||||
|
||||
NotifyParentOfMpathChange(aParent);
|
||||
@ -258,10 +258,10 @@ void
|
||||
SVGMPathElement::UnlinkHrefTarget(bool aNotifyParent)
|
||||
{
|
||||
// Stop observing old target (if any)
|
||||
if (mHrefTarget.get()) {
|
||||
mHrefTarget.get()->RemoveMutationObserver(this);
|
||||
if (mPathTracker.get()) {
|
||||
mPathTracker.get()->RemoveMutationObserver(this);
|
||||
}
|
||||
mHrefTarget.Unlink();
|
||||
mPathTracker.Unlink();
|
||||
|
||||
if (aNotifyParent) {
|
||||
NotifyParentOfMpathChange(GetParent());
|
||||
|
@ -66,10 +66,18 @@ public:
|
||||
already_AddRefed<SVGAnimatedString> Href();
|
||||
|
||||
protected:
|
||||
class PathReference : public mozilla::dom::IDTracker {
|
||||
/**
|
||||
* Helper that provides a reference to the 'path' element with the ID that is
|
||||
* referenced by the 'mpath' element's 'href' attribute, and that will
|
||||
* invalidate the parent of the 'mpath' and update mutation observers to the
|
||||
* new path element if the element that that ID identifies changes to a
|
||||
* different element (or none).
|
||||
*/
|
||||
class PathElementTracker final : public IDTracker {
|
||||
public:
|
||||
explicit PathReference(SVGMPathElement* aMpathElement) :
|
||||
mMpathElement(aMpathElement) {}
|
||||
explicit PathElementTracker(SVGMPathElement* aMpathElement)
|
||||
: mMpathElement(aMpathElement)
|
||||
{}
|
||||
protected:
|
||||
// We need to be notified when target changes, in order to request a sample
|
||||
// (which will clear animation effects that used the old target-path
|
||||
@ -101,7 +109,7 @@ protected:
|
||||
enum { HREF, XLINK_HREF };
|
||||
nsSVGString mStringAttributes[2];
|
||||
static StringInfo sStringInfo[2];
|
||||
PathReference mHrefTarget;
|
||||
PathElementTracker mPathTracker;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -62,7 +62,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGUseElement,
|
||||
SVGUseElementBase)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginal)
|
||||
tmp->mSource.Traverse(&cb);
|
||||
tmp->mReferencedElementTracker.Traverse(&cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(SVGUseElement,
|
||||
@ -73,7 +73,8 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(SVGUseElement,
|
||||
// Implementation
|
||||
|
||||
SVGUseElement::SVGUseElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
||||
: SVGUseElementBase(aNodeInfo), mSource(this)
|
||||
: SVGUseElementBase(aNodeInfo)
|
||||
, mReferencedElementTracker(this)
|
||||
{
|
||||
}
|
||||
|
||||
@ -210,12 +211,12 @@ SVGUseElement::NodeWillBeDestroyed(const nsINode *aNode)
|
||||
already_AddRefed<nsIContent>
|
||||
SVGUseElement::CreateAnonymousContent()
|
||||
{
|
||||
if (mSource.get()) {
|
||||
mSource.get()->RemoveMutationObserver(this);
|
||||
if (mReferencedElementTracker.get()) {
|
||||
mReferencedElementTracker.get()->RemoveMutationObserver(this);
|
||||
}
|
||||
|
||||
LookupHref();
|
||||
nsIContent* targetContent = mSource.get();
|
||||
nsIContent* targetContent = mReferencedElementTracker.get();
|
||||
if (!targetContent)
|
||||
return nullptr;
|
||||
|
||||
@ -300,7 +301,7 @@ SVGUseElement::CreateAnonymousContent()
|
||||
nsIURI*
|
||||
SVGUseElement::GetSourceDocURI()
|
||||
{
|
||||
nsIContent* targetContent = mSource.get();
|
||||
nsIContent* targetContent = mReferencedElementTracker.get();
|
||||
if (!targetContent)
|
||||
return nullptr;
|
||||
|
||||
@ -378,7 +379,7 @@ SVGUseElement::LookupHref()
|
||||
nsCOMPtr<nsIURI> targetURI;
|
||||
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
|
||||
GetComposedDoc(), baseURI);
|
||||
mSource.Reset(this, targetURI);
|
||||
mReferencedElementTracker.Reset(this, targetURI);
|
||||
}
|
||||
|
||||
void
|
||||
@ -396,10 +397,10 @@ SVGUseElement::TriggerReclone()
|
||||
void
|
||||
SVGUseElement::UnlinkSource()
|
||||
{
|
||||
if (mSource.get()) {
|
||||
mSource.get()->RemoveMutationObserver(this);
|
||||
if (mReferencedElementTracker.get()) {
|
||||
mReferencedElementTracker.get()->RemoveMutationObserver(this);
|
||||
}
|
||||
mSource.Unlink();
|
||||
mReferencedElementTracker.Unlink();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -81,19 +81,27 @@ public:
|
||||
URLExtraData* GetContentURLData() const { return mContentURLData; }
|
||||
|
||||
protected:
|
||||
class SourceReference : public mozilla::dom::IDTracker {
|
||||
/**
|
||||
* Helper that provides a reference to the element with the ID that is
|
||||
* referenced by the 'use' element's 'href' attribute, and that will update
|
||||
* the 'use' element if the element that that ID identifies changes to a
|
||||
* different element (or none).
|
||||
*/
|
||||
class ElementTracker final : public IDTracker {
|
||||
public:
|
||||
explicit SourceReference(SVGUseElement* aContainer) : mContainer(aContainer) {}
|
||||
explicit ElementTracker(SVGUseElement* aOwningUseElement)
|
||||
: mOwningUseElement(aOwningUseElement)
|
||||
{}
|
||||
protected:
|
||||
virtual void ElementChanged(Element* aFrom, Element* aTo) override {
|
||||
IDTracker::ElementChanged(aFrom, aTo);
|
||||
if (aFrom) {
|
||||
aFrom->RemoveMutationObserver(mContainer);
|
||||
aFrom->RemoveMutationObserver(mOwningUseElement);
|
||||
}
|
||||
mContainer->TriggerReclone();
|
||||
mOwningUseElement->TriggerReclone();
|
||||
}
|
||||
private:
|
||||
SVGUseElement* mContainer;
|
||||
SVGUseElement* mOwningUseElement;
|
||||
};
|
||||
|
||||
nsSVGUseFrame* GetFrame() const;
|
||||
@ -122,7 +130,7 @@ protected:
|
||||
|
||||
nsCOMPtr<nsIContent> mOriginal; // if we've been cloned, our "real" copy
|
||||
nsCOMPtr<nsIContent> mClone; // cloned tree
|
||||
SourceReference mSource; // observed element
|
||||
ElementTracker mReferencedElementTracker;
|
||||
RefPtr<URLExtraData> mContentURLData; // URL data for its anonymous content
|
||||
};
|
||||
|
||||
|
@ -247,14 +247,8 @@ U2F::Register(const nsAString& aAppId,
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
RefPtr<U2FManager> mgr = U2FManager::GetOrCreate();
|
||||
MOZ_ASSERT(mgr);
|
||||
if (!mgr || mRegisterCallback.isSome()) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
Cancel();
|
||||
|
||||
MOZ_ASSERT(mRegisterCallback.isNothing());
|
||||
mRegisterCallback = Some(nsMainThreadPtrHandle<U2FRegisterCallback>(
|
||||
new nsMainThreadPtrHolder<U2FRegisterCallback>(
|
||||
@ -307,6 +301,7 @@ U2F::Register(const nsAString& aAppId,
|
||||
|
||||
auto& localReqHolder = mPromiseHolder;
|
||||
auto& localCb = mRegisterCallback;
|
||||
RefPtr<U2FManager> mgr = U2FManager::GetOrCreate();
|
||||
RefPtr<U2FPromise> p = mgr->Register(mParent, cAppId,
|
||||
NS_ConvertUTF16toUTF8(clientDataJSON),
|
||||
adjustedTimeoutMillis, excludeList);
|
||||
@ -344,14 +339,8 @@ U2F::Sign(const nsAString& aAppId,
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
RefPtr<U2FManager> mgr = U2FManager::GetOrCreate();
|
||||
MOZ_ASSERT(mgr);
|
||||
if (!mgr || mSignCallback.isSome()) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
Cancel();
|
||||
|
||||
MOZ_ASSERT(mSignCallback.isNothing());
|
||||
mSignCallback = Some(nsMainThreadPtrHandle<U2FSignCallback>(
|
||||
new nsMainThreadPtrHolder<U2FSignCallback>(
|
||||
@ -389,6 +378,7 @@ U2F::Sign(const nsAString& aAppId,
|
||||
permittedList);
|
||||
auto& localReqHolder = mPromiseHolder;
|
||||
auto& localCb = mSignCallback;
|
||||
RefPtr<U2FManager> mgr = U2FManager::GetOrCreate();
|
||||
RefPtr<U2FPromise> p = mgr->Sign(mParent, cAppId,
|
||||
NS_ConvertUTF16toUTF8(clientDataJSON),
|
||||
adjustedTimeoutMillis, permittedList);
|
||||
|
@ -4,7 +4,7 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* http://w3c-test.org/webperf/specs/ResourceTiming/#performanceresourcetiming
|
||||
* https://w3c.github.io/resource-timing/#performanceresourcetiming
|
||||
*
|
||||
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
|
||||
* liability, trademark and document use rules apply.
|
||||
@ -12,14 +12,10 @@
|
||||
|
||||
interface PerformanceResourceTiming : PerformanceEntry
|
||||
{
|
||||
// A string with the name of that element that initiated the load.
|
||||
// If the initiator is a CSS resource, the initiatorType attribute must return
|
||||
// the string "css".
|
||||
// If the initiator is an XMLHttpRequest object, the initiatorType attribute
|
||||
// must return the string "xmlhttprequest".
|
||||
readonly attribute DOMString initiatorType;
|
||||
readonly attribute DOMString nextHopProtocol;
|
||||
|
||||
readonly attribute DOMHighResTimeStamp workerStart;
|
||||
readonly attribute DOMHighResTimeStamp redirectStart;
|
||||
readonly attribute DOMHighResTimeStamp redirectEnd;
|
||||
readonly attribute DOMHighResTimeStamp fetchStart;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user