Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ed4888c06 | ||
|
|
a6166027fa | ||
|
|
cc40df4933 | ||
|
|
7fe7fa64da | ||
|
|
c6c5bcb1ee | ||
|
|
028d533884 | ||
|
|
1decb66e13 | ||
|
|
73d9a067bd |
No files matched your search
@@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Clobber for Idle GC backout
|
||||
Clobber for Python 3 migration
|
||||
@@ -1,8 +0,0 @@
|
||||
TRADEMARK NOTICE
|
||||
================
|
||||
|
||||
The name "Goanna" and visual representations (logo) have not been
|
||||
registered by the Copyright owners as a trademark in any jurisdiction.
|
||||
However, because Goanna has been distributed and maintained world-wide,
|
||||
continually since 2015, the Copyright owners claim "common-law trademark
|
||||
protection" in any jurisdiction where common-law trademark is recognized.
|
||||
@@ -152,7 +152,6 @@ def old_configure_options(*options):
|
||||
'--enable-jitspew',
|
||||
'--enable-js-lto',
|
||||
'--enable-leak-sanitizer',
|
||||
'--enable-libaom',
|
||||
'--enable-libjpeg-turbo',
|
||||
'--enable-libproxy',
|
||||
'--enable-llvm-hacks',
|
||||
|
||||
@@ -61,44 +61,6 @@ set_config('HAVE_YASM', have_yasm)
|
||||
# Until the YASM variable is not necessary in old-configure.
|
||||
add_old_configure_assignment('YASM', have_yasm)
|
||||
|
||||
# nasm detection
|
||||
# ==============================================================
|
||||
nasm = check_prog('NASM', ['nasm'], allow_missing=True)
|
||||
|
||||
@depends_if(nasm)
|
||||
@checking('nasm version')
|
||||
def nasm_version(nasm):
|
||||
version = check_cmd_output(
|
||||
nasm, '-v',
|
||||
onerror=lambda: die('Failed to get nasm version.')
|
||||
).splitlines()[0].split()[2]
|
||||
return Version(version)
|
||||
|
||||
@depends(nasm, nasm_version)
|
||||
def have_nasm(nasm, version):
|
||||
if nasm and version >= Version('2.14'):
|
||||
return True
|
||||
|
||||
set_config('HAVE_NASM', have_nasm)
|
||||
|
||||
@depends(have_nasm, target)
|
||||
def nasm_asflags(have_nasm, target):
|
||||
if have_nasm:
|
||||
asflags = {
|
||||
('OSX', 'x86'): '-f macho32',
|
||||
('OSX', 'x86_64'): '-f macho64',
|
||||
('WINNT', 'x86'): '-f win32',
|
||||
('WINNT', 'x86_64'): '-f win64',
|
||||
}.get((target.os, target.cpu), None)
|
||||
if asflags is None:
|
||||
if target.cpu == 'x86':
|
||||
asflags = '-f elf32'
|
||||
elif target.cpu == 'x86_64':
|
||||
asflags = '-f elf64'
|
||||
return asflags
|
||||
|
||||
set_config('NASM_ASFLAGS', nasm_asflags)
|
||||
|
||||
# MacOS deployment target version
|
||||
# ==============================================================
|
||||
# This needs to happen before any compilation test is done.
|
||||
|
||||
Vendored
+1
-4
@@ -29,10 +29,7 @@ if CONFIG['MOZ_WEBM_ENCODER']:
|
||||
external_dirs += ['media/libvpx']
|
||||
|
||||
if CONFIG['MOZ_AV1']:
|
||||
if CONFIG['AV1_USE_AOM']:
|
||||
external_dirs += ['media/libaom']
|
||||
else:
|
||||
external_dirs += ['media/libdav1d']
|
||||
external_dirs += ['media/libaom']
|
||||
|
||||
external_dirs += ['media/libpng']
|
||||
|
||||
|
||||
@@ -210,10 +210,10 @@ function initialHTML(doc) {
|
||||
let style = doc.createElement("link");
|
||||
style.rel = "stylesheet";
|
||||
style.type = "text/css";
|
||||
style.href = base.href +"css/main.css";
|
||||
style.href = "css/main.css";
|
||||
|
||||
let script = doc.createElement("script");
|
||||
script.src = base.href + "lib/require.js";
|
||||
script.src = "lib/require.js";
|
||||
script.dataset.main = "viewer-config";
|
||||
script.defer = true;
|
||||
|
||||
|
||||
@@ -103,6 +103,12 @@ function StyleSheetEditor(styleSheet, win, file, isNew, walker, highlighter) {
|
||||
}
|
||||
};
|
||||
|
||||
this._styleSheetFilePath = null;
|
||||
if (styleSheet.href &&
|
||||
Services.io.extractScheme(this.styleSheet.href) == "file") {
|
||||
this._styleSheetFilePath = this.styleSheet.href;
|
||||
}
|
||||
|
||||
this._onPropertyChange = this._onPropertyChange.bind(this);
|
||||
this._onError = this._onError.bind(this);
|
||||
this._onMediaRuleMatchesChange = this._onMediaRuleMatchesChange.bind(this);
|
||||
@@ -658,7 +664,8 @@ StyleSheetEditor.prototype = {
|
||||
if (this._friendlyName) {
|
||||
defaultName = OS.Path.basename(this._friendlyName);
|
||||
}
|
||||
showFilePicker(file, true, this._window, onFile, defaultName);
|
||||
showFilePicker(file || this._styleSheetFilePath, true, this._window,
|
||||
onFile, defaultName);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,112 +1,98 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test that 'Save' function works.
|
||||
|
||||
const TESTCASE_URI_HTML = TEST_BASE_HTTP + "simple.html";
|
||||
const TESTCASE_URI_CSS = TEST_BASE_HTTP + "simple.css";
|
||||
|
||||
var tempScope = {};
|
||||
Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope);
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm", tempScope);
|
||||
var FileUtils = tempScope.FileUtils;
|
||||
var NetUtil = tempScope.NetUtil;
|
||||
|
||||
add_task(function* () {
|
||||
let htmlFile = yield copy(TESTCASE_URI_HTML, "simple.html");
|
||||
const cssFile = await copy(TESTCASE_URI_CSS, "simple.css");
|
||||
let uri = Services.io.newFileURI(htmlFile);
|
||||
let filePath = uri.resolve("");
|
||||
|
||||
let { ui } = yield openStyleEditorForURL(filePath);
|
||||
|
||||
let editor = ui.editors[0];
|
||||
yield editor.getSourceEditor();
|
||||
|
||||
is(
|
||||
editor.savedFile,
|
||||
null,
|
||||
"savedFile should not be pre-populated from the source file"
|
||||
);
|
||||
|
||||
info("Editing the style sheet.");
|
||||
let dirty = editor.sourceEditor.once("dirty-change");
|
||||
let beginCursor = {line: 0, ch: 0};
|
||||
editor.sourceEditor.replaceText("DIRTY TEXT", beginCursor, beginCursor);
|
||||
|
||||
yield dirty;
|
||||
|
||||
is(editor.sourceEditor.isClean(), false, "Editor is dirty.");
|
||||
ok(editor.summary.classList.contains("unsaved"),
|
||||
"Star icon is present in the corresponding summary.");
|
||||
|
||||
info(
|
||||
"Saving the changes with an explicit file (simulating a user-chosen save location)."
|
||||
);
|
||||
dirty = editor.sourceEditor.once("dirty-change");
|
||||
|
||||
editor.saveToFile(cssFile, function (file) {
|
||||
ok(file, "file should get saved when explicitly passing a file");
|
||||
});
|
||||
|
||||
yield dirty;
|
||||
|
||||
is(editor.sourceEditor.isClean(), true, "Editor is clean.");
|
||||
ok(!editor.summary.classList.contains("unsaved"),
|
||||
"Star icon is not present in the corresponding summary.");
|
||||
|
||||
is(
|
||||
editor.savedFile?.path,
|
||||
cssFile.path,
|
||||
"savedFile should now be set on the editor"
|
||||
);
|
||||
});
|
||||
|
||||
function copy(srcChromeURL, destFileName) {
|
||||
let deferred = defer();
|
||||
let destFile = FileUtils.getFile("ProfD", [destFileName]);
|
||||
write(read(srcChromeURL), destFile, deferred.resolve);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function read(srcChromeURL) {
|
||||
let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||
.getService(Ci.nsIScriptableInputStream);
|
||||
|
||||
let channel = NetUtil.newChannel({
|
||||
uri: srcChromeURL,
|
||||
loadUsingSystemPrincipal: true
|
||||
});
|
||||
let input = channel.open2();
|
||||
scriptableStream.init(input);
|
||||
|
||||
let data = "";
|
||||
while (input.available()) {
|
||||
data = data.concat(scriptableStream.read(input.available()));
|
||||
}
|
||||
scriptableStream.close();
|
||||
input.close();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function write(data, file, callback) {
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
|
||||
converter.charset = "UTF-8";
|
||||
|
||||
let istream = converter.convertToInputStream(data);
|
||||
let ostream = FileUtils.openSafeFileOutputStream(file);
|
||||
|
||||
NetUtil.asyncCopy(istream, ostream, function (status) {
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
info("Couldn't write to " + file.path);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(file);
|
||||
});
|
||||
}
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test that 'Save' function works.
|
||||
|
||||
const TESTCASE_URI_HTML = TEST_BASE_HTTP + "simple.html";
|
||||
const TESTCASE_URI_CSS = TEST_BASE_HTTP + "simple.css";
|
||||
|
||||
var tempScope = {};
|
||||
Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope);
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm", tempScope);
|
||||
var FileUtils = tempScope.FileUtils;
|
||||
var NetUtil = tempScope.NetUtil;
|
||||
|
||||
add_task(function* () {
|
||||
let htmlFile = yield copy(TESTCASE_URI_HTML, "simple.html");
|
||||
yield copy(TESTCASE_URI_CSS, "simple.css");
|
||||
let uri = Services.io.newFileURI(htmlFile);
|
||||
let filePath = uri.resolve("");
|
||||
|
||||
let { ui } = yield openStyleEditorForURL(filePath);
|
||||
|
||||
let editor = ui.editors[0];
|
||||
yield editor.getSourceEditor();
|
||||
|
||||
info("Editing the style sheet.");
|
||||
let dirty = editor.sourceEditor.once("dirty-change");
|
||||
let beginCursor = {line: 0, ch: 0};
|
||||
editor.sourceEditor.replaceText("DIRTY TEXT", beginCursor, beginCursor);
|
||||
|
||||
yield dirty;
|
||||
|
||||
is(editor.sourceEditor.isClean(), false, "Editor is dirty.");
|
||||
ok(editor.summary.classList.contains("unsaved"),
|
||||
"Star icon is present in the corresponding summary.");
|
||||
|
||||
info("Saving the changes.");
|
||||
dirty = editor.sourceEditor.once("dirty-change");
|
||||
|
||||
editor.saveToFile(null, function (file) {
|
||||
ok(file, "file should get saved directly when using a file:// URI");
|
||||
});
|
||||
|
||||
yield dirty;
|
||||
|
||||
is(editor.sourceEditor.isClean(), true, "Editor is clean.");
|
||||
ok(!editor.summary.classList.contains("unsaved"),
|
||||
"Star icon is not present in the corresponding summary.");
|
||||
});
|
||||
|
||||
function copy(srcChromeURL, destFileName) {
|
||||
let deferred = defer();
|
||||
let destFile = FileUtils.getFile("ProfD", [destFileName]);
|
||||
write(read(srcChromeURL), destFile, deferred.resolve);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function read(srcChromeURL) {
|
||||
let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||
.getService(Ci.nsIScriptableInputStream);
|
||||
|
||||
let channel = NetUtil.newChannel({
|
||||
uri: srcChromeURL,
|
||||
loadUsingSystemPrincipal: true
|
||||
});
|
||||
let input = channel.open2();
|
||||
scriptableStream.init(input);
|
||||
|
||||
let data = "";
|
||||
while (input.available()) {
|
||||
data = data.concat(scriptableStream.read(input.available()));
|
||||
}
|
||||
scriptableStream.close();
|
||||
input.close();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function write(data, file, callback) {
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
|
||||
converter.charset = "UTF-8";
|
||||
|
||||
let istream = converter.convertToInputStream(data);
|
||||
let ostream = FileUtils.openSafeFileOutputStream(file);
|
||||
|
||||
NetUtil.asyncCopy(istream, ostream, function (status) {
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
info("Couldn't write to " + file.path);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(file);
|
||||
});
|
||||
}
|
||||
@@ -28,10 +28,7 @@ add_task(function* () {
|
||||
let HTMLFile = yield copy(TESTCASE_URI_HTML, ["sourcemaps.html"]);
|
||||
let CSSFile = yield copy(TESTCASE_URI_CSS,
|
||||
["sourcemap-css", "sourcemaps.css"]);
|
||||
const SCSSFile = await copy(TESTCASE_URI_SCSS, [
|
||||
"sourcemap-sass",
|
||||
"sourcemaps.scss",
|
||||
]);
|
||||
yield copy(TESTCASE_URI_SCSS, ["sourcemap-sass", "sourcemaps.scss"]);
|
||||
yield copy(TESTCASE_URI_MAP, ["sourcemap-css", "sourcemaps.css.map"]);
|
||||
yield copy(TESTCASE_URI_REG_CSS, ["simple.css"]);
|
||||
|
||||
@@ -62,7 +59,7 @@ add_task(function* () {
|
||||
|
||||
// Edit and save Sass in the editor. This will start off a file-watching
|
||||
// process waiting for the CSS file to change.
|
||||
yield editSCSS(SCSSFile, editor);
|
||||
yield editSCSS(editor);
|
||||
|
||||
// We can't run Sass or another compiler, so we fake it by just
|
||||
// directly changing the CSS file.
|
||||
@@ -76,12 +73,12 @@ add_task(function* () {
|
||||
is(color, "rgb(0, 0, 255)", "div is blue after saving file");
|
||||
});
|
||||
|
||||
function editSCSS(SCSSFile, editor) {
|
||||
function editSCSS(editor) {
|
||||
let deferred = defer();
|
||||
|
||||
editor.sourceEditor.setText(CSS_TEXT);
|
||||
|
||||
editor.saveToFile(SCSSFile, function (file) {
|
||||
editor.saveToFile(null, function (file) {
|
||||
ok(file, "Scss file should be saved");
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
@@ -196,7 +196,7 @@ public:
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
int32_t written = mLength < INT32_MAX ? PR_Write(mFD, mData, mLength) : -1;
|
||||
int32_t written = PR_Write(mFD, mData, mLength);
|
||||
if (NS_WARN_IF(written < 0 || uint32_t(written) != mLength)) {
|
||||
return NS_DispatchToMainThread(
|
||||
new ErrorPropagationRunnable(mBlobStorage, NS_ERROR_FAILURE));
|
||||
|
||||
@@ -1004,7 +1004,6 @@ GK_ATOM(parameter, "parameter")
|
||||
GK_ATOM(parent, "parent")
|
||||
GK_ATOM(parentfocused, "parentfocused")
|
||||
GK_ATOM(parsetype, "parsetype")
|
||||
GK_ATOM(part, "part")
|
||||
GK_ATOM(password, "password")
|
||||
GK_ATOM(pattern, "pattern")
|
||||
GK_ATOM(patternSeparator, "pattern-separator")
|
||||
|
||||
@@ -97,7 +97,7 @@ ToJSValue(JSContext* aCx,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Accept floating point types
|
||||
// accept floating point types
|
||||
inline bool
|
||||
ToJSValue(JSContext* aCx,
|
||||
float aArgument,
|
||||
@@ -106,7 +106,7 @@ ToJSValue(JSContext* aCx,
|
||||
// Make sure we're called in a compartment
|
||||
MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
|
||||
|
||||
aValue.set(JS_NumberValue(double(aArgument)));
|
||||
aValue.setNumber(aArgument);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,44 +39,6 @@ WebGLTransformFeedback::Delete()
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
// Validation helper for beginning/resuming the TF and computing the
|
||||
// vertex capacity.
|
||||
bool
|
||||
WebGLTransformFeedback::PrepareTransformFeedback() {
|
||||
const auto& prog = mContext->mCurrentProgram;
|
||||
if (!prog ||
|
||||
!prog->IsLinked() ||
|
||||
!prog->LinkInfo()->componentsPerTFVert.size()) {
|
||||
mContext->ErrorInvalidOperation("Current program not valid for transform feedback.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& linkInfo = prog->LinkInfo();
|
||||
const auto& componentsPerTFVert = linkInfo->componentsPerTFVert;
|
||||
|
||||
mActive_VertCapacity = 0;
|
||||
|
||||
size_t minVertCapacity = SIZE_MAX;
|
||||
for (size_t i = 0; i < componentsPerTFVert.size(); i++) {
|
||||
const auto& indexedBinding = mIndexedBindings[i];
|
||||
const auto& componentsPerVert = componentsPerTFVert[i];
|
||||
|
||||
const auto& buffer = indexedBinding.mBufferBinding;
|
||||
if (!buffer) {
|
||||
mContext->ErrorInvalidOperation("No buffer attached to required transform feedback index %u.",
|
||||
(uint32_t)i);
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t vertCapacity = buffer->ByteLength() / 4 / componentsPerVert;
|
||||
minVertCapacity = std::min(minVertCapacity, vertCapacity);
|
||||
}
|
||||
|
||||
mActive_VertCapacity = minVertCapacity;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLTransformFeedback::BeginTransformFeedback(GLenum primMode)
|
||||
{
|
||||
@@ -97,10 +59,37 @@ WebGLTransformFeedback::BeginTransformFeedback(GLenum primMode)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PrepareTransformFeedback()) {
|
||||
const auto& prog = mContext->mCurrentProgram;
|
||||
if (!prog ||
|
||||
!prog->IsLinked() ||
|
||||
!prog->LinkInfo()->componentsPerTFVert.size())
|
||||
{
|
||||
mContext->ErrorInvalidOperation("%s: Current program not valid for transform"
|
||||
" feedback.",
|
||||
funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& linkInfo = prog->LinkInfo();
|
||||
const auto& componentsPerTFVert = linkInfo->componentsPerTFVert;
|
||||
|
||||
size_t minVertCapacity = SIZE_MAX;
|
||||
for (size_t i = 0; i < componentsPerTFVert.size(); i++) {
|
||||
const auto& indexedBinding = mIndexedBindings[i];
|
||||
const auto& componentsPerVert = componentsPerTFVert[i];
|
||||
|
||||
const auto& buffer = indexedBinding.mBufferBinding;
|
||||
if (!buffer) {
|
||||
mContext->ErrorInvalidOperation("%s: No buffer attached to required transform"
|
||||
" feedback index %u.",
|
||||
funcName, (uint32_t)i);
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t vertCapacity = buffer->ByteLength() / 4 / componentsPerVert;
|
||||
minVertCapacity = std::min(minVertCapacity, vertCapacity);
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
const auto& gl = mContext->gl;
|
||||
@@ -112,9 +101,10 @@ WebGLTransformFeedback::BeginTransformFeedback(GLenum primMode)
|
||||
mIsActive = true;
|
||||
MOZ_ASSERT(!mIsPaused);
|
||||
|
||||
mActive_Program = mContext->mCurrentProgram;
|
||||
mActive_Program = prog;
|
||||
mActive_PrimMode = primMode;
|
||||
mActive_VertPosition = 0;
|
||||
mActive_VertCapacity = minVertCapacity;
|
||||
|
||||
////
|
||||
|
||||
@@ -183,12 +173,6 @@ WebGLTransformFeedback::ResumeTransformFeedback()
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-run prepare in case some of the buffers have been modified
|
||||
// while the TF was paused.
|
||||
if (!PrepareTransformFeedback()) {
|
||||
return;
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
const auto& gl = mContext->gl;
|
||||
|
||||
@@ -44,7 +44,6 @@ public:
|
||||
WebGLTransformFeedback(WebGLContext* webgl, GLuint tf);
|
||||
private:
|
||||
~WebGLTransformFeedback();
|
||||
bool PrepareTransformFeedback();
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTransformFeedback)
|
||||
|
||||
@@ -26,6 +26,7 @@ support-files =
|
||||
image_yellow75.png
|
||||
imagebitmap_bug1239300.js
|
||||
imagebitmap_bug1239752.js
|
||||
imagebitmap_extensions.html
|
||||
imagebitmap_on_worker.js
|
||||
imagebitmap_structuredclone.js
|
||||
imagebitmap_structuredclone_iframe.html
|
||||
|
||||
@@ -63,7 +63,6 @@ EncodingUtils::IsAsciiCompatible(const nsACString& aPreferredName)
|
||||
aPreferredName.LowerCaseEqualsLiteral("utf-16be") ||
|
||||
aPreferredName.LowerCaseEqualsLiteral("utf-16le") ||
|
||||
aPreferredName.LowerCaseEqualsLiteral("replacement") ||
|
||||
aPreferredName.LowerCaseEqualsLiteral("iso-2022-jp") ||
|
||||
aPreferredName.LowerCaseEqualsLiteral("hz-gb-2312") ||
|
||||
aPreferredName.LowerCaseEqualsLiteral("utf-7") ||
|
||||
aPreferredName.LowerCaseEqualsLiteral("x-imap4-modified-utf7"));
|
||||
|
||||
@@ -652,7 +652,7 @@ nsresult nsGeolocationService::Init()
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)
|
||||
#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
|
||||
if (Preferences::GetBool("geo.provider.use_corelocation", true)) {
|
||||
mProvider = new CoreLocationLocationProvider();
|
||||
}
|
||||
|
||||
@@ -876,12 +876,20 @@ HTMLImageElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
return HTMLImageElementBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
nsIDOMHTMLFormElement*
|
||||
HTMLImageElement::GetForm() const
|
||||
{
|
||||
return mForm;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
HTMLImageElement::SetForm(nsIDOMHTMLFormElement* aForm)
|
||||
{
|
||||
NS_PRECONDITION(aForm, "Don't pass null here");
|
||||
MOZ_ASSERT(!mForm && !HasFlag(ADDED_TO_FORM),
|
||||
"We don't support switching from one non-null form to another.");
|
||||
NS_ASSERTION(!mForm,
|
||||
"We don't support switching from one non-null form to another.");
|
||||
|
||||
mForm = static_cast<HTMLFormElement*>(aForm);
|
||||
}
|
||||
|
||||
@@ -216,8 +216,9 @@ public:
|
||||
SetHTMLAttr(nsGkAtoms::lowsrc, aLowsrc, aError);
|
||||
}
|
||||
|
||||
HTMLFormElement* GetForm() const { return mForm; };
|
||||
|
||||
#ifdef DEBUG
|
||||
nsIDOMHTMLFormElement* GetForm() const;
|
||||
#endif
|
||||
void SetForm(nsIDOMHTMLFormElement* aForm);
|
||||
void ClearForm(bool aRemoveFromForm);
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIProtocolHandler.h"
|
||||
|
||||
NS_IMPL_NS_NEW_HTML_ELEMENT(Shared)
|
||||
|
||||
@@ -188,8 +186,6 @@ SetBaseURIUsingFirstBaseWithHref(nsIDocument* aDocument, nsIContent* aMustMatch)
|
||||
newBaseURI = nullptr;
|
||||
}
|
||||
if (csp && newBaseURI) {
|
||||
bool isLocalResource = false;
|
||||
rv = NS_URIChainHasFlags(newBaseURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, &isLocalResource);
|
||||
// base-uri is only enforced if explicitly defined in the
|
||||
// policy - do *not* consult default-src, see:
|
||||
// http://www.w3.org/TR/CSP2/#directive-default-src
|
||||
@@ -197,7 +193,7 @@ SetBaseURIUsingFirstBaseWithHref(nsIDocument* aDocument, nsIContent* aMustMatch)
|
||||
rv = csp->Permits(newBaseURI, nsIContentSecurityPolicy::BASE_URI_DIRECTIVE,
|
||||
true /* aSpecific */, true /* aSendViolationReports */,
|
||||
&cspPermitsBaseURI);
|
||||
if ((NS_FAILED(rv) || !cspPermitsBaseURI) && !isLocalResource) {
|
||||
if (NS_FAILED(rv) || !cspPermitsBaseURI) {
|
||||
newBaseURI = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1824,8 +1824,8 @@ void
|
||||
nsGenericHTMLFormElement::SetForm(nsIDOMHTMLFormElement* aForm)
|
||||
{
|
||||
NS_PRECONDITION(aForm, "Don't pass null here");
|
||||
MOZ_ASSERT(!mForm && !HasFlag(ADDED_TO_FORM),
|
||||
"We don't support switching from one non-null form to another.");
|
||||
NS_ASSERTION(!mForm,
|
||||
"We don't support switching from one non-null form to another.");
|
||||
|
||||
// keep a *weak* ref to the form here
|
||||
mForm = static_cast<HTMLFormElement*>(aForm);
|
||||
|
||||
@@ -105,6 +105,8 @@ gDownMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] =
|
||||
* Given an array of input channels, downmix to aOutputChannelCount, and copy
|
||||
* the results to the channel buffers in aOutputChannels. Don't call this with
|
||||
* input count <= output count.
|
||||
* Each of aOutputChannels must be non-null.
|
||||
* aChannelArray elements may be null.
|
||||
*/
|
||||
template<typename T>
|
||||
void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray,
|
||||
@@ -119,7 +121,11 @@ void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray,
|
||||
if (inputChannelCount > 6) {
|
||||
// Just drop the unknown channels.
|
||||
for (uint32_t o = 0; o < aOutputChannelCount; ++o) {
|
||||
PodCopy(aOutputChannels[o], inputChannels[o], aDuration);
|
||||
if (inputChannels[o]) {
|
||||
PodCopy(aOutputChannels[o], inputChannels[o], aDuration);
|
||||
} else {
|
||||
std::fill_n(aOutputChannels[o], aDuration, static_cast<T>(0));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -138,13 +144,19 @@ void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray,
|
||||
// want an input channel to contribute to nothing
|
||||
T outputChannels[CUSTOM_CHANNEL_LAYOUTS + 1] = {0};
|
||||
for (uint32_t c = 0; c < inputChannelCount; ++c) {
|
||||
outputChannels[m.mInputDestination[c]] +=
|
||||
m.mInputCoefficient[c]*(static_cast<const T*>(inputChannels[c]))[s];
|
||||
if (inputChannels[c]) {
|
||||
outputChannels[m.mInputDestination[c]] +=
|
||||
m.mInputCoefficient[c] *
|
||||
(static_cast<const T*>(inputChannels[c]))[s];
|
||||
}
|
||||
}
|
||||
// Utilize the fact that in every layout, C is the third channel.
|
||||
if (m.mCExtraDestination != IGNORE) {
|
||||
outputChannels[m.mCExtraDestination] +=
|
||||
m.mInputCoefficient[SURROUND_C]*(static_cast<const T*>(inputChannels[SURROUND_C]))[s];
|
||||
if (inputChannels[SURROUND_C]) {
|
||||
outputChannels[m.mCExtraDestination] +=
|
||||
m.mInputCoefficient[SURROUND_C] *
|
||||
(static_cast<const T*>(inputChannels[SURROUND_C]))[s];
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t c = 0; c < aOutputChannelCount; ++c) {
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <speex/speex_resampler.h>
|
||||
#include <string.h>
|
||||
#include <cmath>
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
/*
|
||||
* Parts derived from MythTV AudioConvert Class
|
||||
@@ -263,15 +262,8 @@ AudioConverter::ResampleAudio(void* aOut, const void* aIn, size_t aFrames)
|
||||
if (!mResampler) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t outframes;
|
||||
if (!ResampleRecipientFrames(aFrames, &outframes)) {
|
||||
return 0;
|
||||
}
|
||||
CheckedUint32 inframesChecked(aFrames);
|
||||
if (!inframesChecked.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t inframes = inframesChecked.value();
|
||||
uint32_t outframes = ResampleRecipientFrames(aFrames);
|
||||
uint32_t inframes = aFrames;
|
||||
|
||||
int error;
|
||||
if (mOut.Format() == AudioConfig::FORMAT_FLT) {
|
||||
@@ -296,7 +288,7 @@ AudioConverter::ResampleAudio(void* aOut, const void* aIn, size_t aFrames)
|
||||
mResampler = nullptr;
|
||||
return 0;
|
||||
}
|
||||
MOZ_ASSERT(static_cast<size_t>(inframes) == aFrames, "Some frames will be dropped");
|
||||
MOZ_ASSERT(inframes == aFrames, "Some frames will be dropped");
|
||||
return outframes;
|
||||
}
|
||||
|
||||
@@ -381,27 +373,17 @@ AudioConverter::UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const
|
||||
return aFrames;
|
||||
}
|
||||
|
||||
bool AudioConverter::ResampleRecipientFrames(size_t aFrames,
|
||||
uint32_t* aOutFrames) const {
|
||||
size_t
|
||||
AudioConverter::ResampleRecipientFrames(size_t aFrames) const
|
||||
{
|
||||
if (!aFrames && mIn.Rate() != mOut.Rate()) {
|
||||
if (!mResampler) {
|
||||
*aOutFrames = 0;
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
// We drain by pushing in get_input_latency() samples of 0
|
||||
aFrames = speex_resampler_get_input_latency(mResampler);
|
||||
}
|
||||
CheckedInt<uint64_t> numerator = CheckedInt<uint64_t>(aFrames) * mOut.Rate();
|
||||
if (!numerator.isValid()) {
|
||||
return false;
|
||||
}
|
||||
CheckedUint32 outFrames(numerator.value() / mIn.Rate());
|
||||
outFrames += 1u;
|
||||
if (!outFrames.isValid()) {
|
||||
return false;
|
||||
}
|
||||
*aOutFrames = outFrames.value();
|
||||
return true;
|
||||
return (uint64_t)aFrames * mOut.Rate() / mIn.Rate() + 1;
|
||||
}
|
||||
|
||||
size_t
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#define AudioConverter_h
|
||||
|
||||
#include "MediaInfo.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
// Forward declaration
|
||||
typedef struct SpeexResamplerState_ SpeexResamplerState;
|
||||
@@ -164,15 +163,9 @@ public:
|
||||
AlignedBuffer<Value>* outputBuffer = &temp1;
|
||||
AlignedBuffer<Value> temp2;
|
||||
if (!frames || mOut.Rate() > mIn.Rate()) {
|
||||
uint32_t resampledFrames;
|
||||
// We are upsampling or about to drain, we can't work in place.
|
||||
// Allocate another temporary buffer where the upsampling will occur.
|
||||
if (!ResampleRecipientFrames(frames, &resampledFrames)) {
|
||||
return AudioDataBuffer<Format, Value>(std::move(temp2));
|
||||
}
|
||||
CheckedInt<size_t> outputSamples =
|
||||
CheckedInt<size_t>(resampledFrames) * mOut.Channels();
|
||||
if (!outputSamples.isValid() || !temp2.SetLength(outputSamples.value())) {
|
||||
if (!temp2.SetLength(FramesOutToSamples(ResampleRecipientFrames(frames)))) {
|
||||
return AudioDataBuffer<Format, Value>(Move(temp2));
|
||||
}
|
||||
outputBuffer = &temp2;
|
||||
@@ -236,7 +229,7 @@ private:
|
||||
// Resampler context.
|
||||
SpeexResamplerState* mResampler;
|
||||
size_t ResampleAudio(void* aOut, const void* aIn, size_t aFrames);
|
||||
bool ResampleRecipientFrames(size_t aFrames, uint32_t* aOutFrames) const;
|
||||
size_t ResampleRecipientFrames(size_t aFrames) const;
|
||||
void RecreateResampler();
|
||||
size_t DrainResampler(void* aOut);
|
||||
};
|
||||
|
||||
@@ -121,13 +121,19 @@ AudioSegment::Mix(AudioMixer& aMixer, uint32_t aOutputChannels,
|
||||
}
|
||||
if (channelData.Length() < aOutputChannels) {
|
||||
// Up-mix.
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, SilentChannel::ZeroChannel<AudioDataValue>());
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, static_cast<const AudioDataValue*>(nullptr));
|
||||
for (uint32_t channel = 0; channel < aOutputChannels; channel++) {
|
||||
AudioDataValue* ptr =
|
||||
PointerForOffsetInChannel(buf.Elements(), outBufferLength,
|
||||
aOutputChannels, channel, offsetSamples);
|
||||
PodCopy(ptr, reinterpret_cast<const AudioDataValue*>(channelData[channel]),
|
||||
frames);
|
||||
if (channelData[channel]) {
|
||||
PodCopy(
|
||||
ptr,
|
||||
reinterpret_cast<const AudioDataValue*>(channelData[channel]),
|
||||
frames);
|
||||
} else {
|
||||
PodZero(ptr, frames);
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(channelData.Length() == aOutputChannels);
|
||||
} else if (channelData.Length() > aOutputChannels) {
|
||||
|
||||
@@ -61,15 +61,22 @@ template <typename SrcT, typename DestT>
|
||||
static void
|
||||
InterleaveAndConvertBuffer(const SrcT* const* aSourceChannels,
|
||||
uint32_t aLength, float aVolume,
|
||||
uint32_t aChannels,
|
||||
uint32_t aChannelCount,
|
||||
DestT* aOutput)
|
||||
{
|
||||
DestT* output = aOutput;
|
||||
for (size_t i = 0; i < aLength; ++i) {
|
||||
for (size_t channel = 0; channel < aChannels; ++channel) {
|
||||
float v = AudioSampleToFloat(aSourceChannels[channel][i])*aVolume;
|
||||
*output = FloatToAudioSample<DestT>(v);
|
||||
++output;
|
||||
for (size_t channel = 0; channel < aChannelCount; ++channel) {
|
||||
DestT* output = aOutput;
|
||||
if (aSourceChannels[channel]) {
|
||||
for (size_t i = 0; i < aLength; ++i) {
|
||||
float v = AudioSampleToFloat(aSourceChannels[channel][i])*aVolume;
|
||||
*output = FloatToAudioSample<DestT>(v);
|
||||
output += aChannelCount;
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < aLength; ++i) {
|
||||
*output = static_cast<DestT>(0);
|
||||
output += aChannelCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,7 +100,7 @@ DeinterleaveAndConvertBuffer(const SrcT* aSourceBuffer,
|
||||
class SilentChannel
|
||||
{
|
||||
public:
|
||||
static const int AUDIO_PROCESSING_FRAMES = 4096; /* > 10ms of 48KHz audio */
|
||||
static const int AUDIO_PROCESSING_FRAMES = 640; /* > 10ms of 48KHz audio */
|
||||
static const uint8_t gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*AUDIO_PROCESSING_FRAMES];
|
||||
// We take advantage of the fact that zero in float and zero in int have the
|
||||
// same all-zeros bit layout.
|
||||
@@ -408,7 +415,7 @@ void WriteChunk(AudioChunk& aChunk,
|
||||
if (channelData.Length() < aOutputChannels) {
|
||||
// Up-mix. Note that this might actually make channelData have more
|
||||
// than aOutputChannels temporarily.
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, SilentChannel::ZeroChannel<SrcT>());
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, static_cast<const SrcT*>(nullptr));
|
||||
}
|
||||
if (channelData.Length() > aOutputChannels) {
|
||||
// Down-mix.
|
||||
|
||||
@@ -367,13 +367,12 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static const uint32_t MIN_RATE = 8000;
|
||||
static const uint32_t MAX_RATE = 640000;
|
||||
|
||||
bool IsValid() const override
|
||||
{
|
||||
return mChannels > 0 && mChannels <= MAX_AUDIO_CHANNELS
|
||||
&& mRate >= MIN_RATE && mRate <= MAX_RATE;
|
||||
&& mRate > 0 && mRate <= MAX_RATE;
|
||||
}
|
||||
|
||||
AudioInfo* GetAsAudioInfo() override
|
||||
|
||||
@@ -111,7 +111,7 @@ private:
|
||||
DECL_MEDIA_PREF("media.ffvpx.enabled", PDMFFVPXEnabled, bool, true);
|
||||
#endif
|
||||
#ifdef MOZ_AV1
|
||||
DECL_MEDIA_PREF("media.av1.enabled", AV1Enabled, bool, true);
|
||||
DECL_MEDIA_PREF("media.av1.enabled", AV1Enabled, bool, false);
|
||||
#endif
|
||||
#ifdef XP_WIN
|
||||
DECL_MEDIA_PREF("media.wmf.enabled", PDMWMFEnabled, bool, true);
|
||||
|
||||
@@ -179,18 +179,12 @@ IsValidVideoRegion(const nsIntSize& aFrame, const nsIntRect& aPicture,
|
||||
aFrame.height <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aFrame.width * aFrame.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
|
||||
aFrame.width * aFrame.height != 0 &&
|
||||
aPicture.width > 0 &&
|
||||
aPicture.width <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.x >= 0 &&
|
||||
aPicture.x < PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.x + aPicture.width < PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.XMost() <= aFrame.width &&
|
||||
aPicture.height > 0 &&
|
||||
aPicture.height <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.y >= 0 &&
|
||||
aPicture.y < PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.y + aPicture.height < PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.YMost() <= aFrame.height &&
|
||||
aPicture.width * aPicture.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
|
||||
aPicture.width * aPicture.height != 0 &&
|
||||
aDisplay.width <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
|
||||
@@ -439,7 +439,7 @@ private:
|
||||
Maybe<size_t> mInitOffset;
|
||||
Maybe<size_t> mMediaOffset;
|
||||
bool mValid = true;
|
||||
char mLastInvalidBox[5]{};
|
||||
char mLastInvalidBox[5];
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
@@ -13,11 +13,7 @@
|
||||
#include "TheoraDecoder.h"
|
||||
|
||||
#ifdef MOZ_AV1
|
||||
#ifdef AV1_USE_AOM
|
||||
#include "AOMDecoder.h"
|
||||
#else
|
||||
#include "Dav1dDecoder.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
@@ -34,11 +30,7 @@ AgnosticDecoderModule::SupportsMimeType(const nsACString& aMimeType,
|
||||
TheoraDecoder::IsTheora(aMimeType);
|
||||
#ifdef MOZ_AV1
|
||||
if (MediaPrefs::AV1Enabled()) {
|
||||
#ifdef AV1_USE_AOM
|
||||
supports |= AOMDecoder::IsAV1(aMimeType);
|
||||
#else
|
||||
supports |= Dav1dDecoder::IsAV1(aMimeType);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
MOZ_LOG(sPDMLog, LogLevel::Debug, ("Agnostic decoder %s requested type",
|
||||
@@ -55,17 +47,10 @@ AgnosticDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
|
||||
m = new VPXDecoder(aParams);
|
||||
}
|
||||
#ifdef MOZ_AV1
|
||||
#ifdef AV1_USE_AOM
|
||||
else if (AOMDecoder::IsAV1(aParams.mConfig.mMimeType) &&
|
||||
MediaPrefs::AV1Enabled()) {
|
||||
m = new AOMDecoder(aParams);
|
||||
}
|
||||
#else
|
||||
else if (Dav1dDecoder::IsAV1(aParams.mConfig.mMimeType) &&
|
||||
MediaPrefs::AV1Enabled()) {
|
||||
m = new Dav1dDecoder(aParams);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
else if (TheoraDecoder::IsTheora(aParams.mConfig.mMimeType)) {
|
||||
m = new TheoraDecoder(aParams);
|
||||
|
||||
@@ -1,376 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Dav1dDecoder.h"
|
||||
#include "MediaResult.h"
|
||||
#include "TimeUnits.h"
|
||||
#include "dav1d/dav1d.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/UniquePtrExtensions.h"
|
||||
#include "nsError.h"
|
||||
#include "prsystem.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#undef LOG
|
||||
#define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("Dav1dDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace gfx;
|
||||
using namespace layers;
|
||||
|
||||
Dav1dDecoder::Dav1dDecoder(const CreateDecoderParams& aParams)
|
||||
: mImageContainer(aParams.mImageContainer)
|
||||
, mTaskQueue(aParams.mTaskQueue)
|
||||
, mCallback(aParams.mCallback)
|
||||
, mIsFlushing(false)
|
||||
, mDecoder(nullptr)
|
||||
, mInfo(aParams.VideoConfig())
|
||||
{
|
||||
}
|
||||
|
||||
Dav1dDecoder::~Dav1dDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Dav1dDecoder::Shutdown()
|
||||
{
|
||||
if (mDecoder) {
|
||||
dav1d_close(&mDecoder);
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::InitPromise>
|
||||
Dav1dDecoder::Init()
|
||||
{
|
||||
int decodeThreads = 2;
|
||||
if (mInfo.mDisplay.width >= 2048) {
|
||||
decodeThreads = 8;
|
||||
} else if (mInfo.mDisplay.width >= 1024) {
|
||||
decodeThreads = 4;
|
||||
}
|
||||
decodeThreads = std::min(decodeThreads, PR_GetNumberOfProcessors());
|
||||
|
||||
Dav1dSettings settings;
|
||||
dav1d_default_settings(&settings);
|
||||
settings.n_threads = decodeThreads;
|
||||
settings.logger.callback = nullptr;
|
||||
|
||||
if (dav1d_open(&mDecoder, &settings) < 0) {
|
||||
return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
|
||||
}
|
||||
return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
Dav1dDecoder::Flush()
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
mIsFlushing = true;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([this] () {
|
||||
if (mDecoder) {
|
||||
dav1d_flush(mDecoder);
|
||||
}
|
||||
});
|
||||
SyncRunnable::DispatchToThread(mTaskQueue, r);
|
||||
mIsFlushing = false;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetPlaneSize(int aWidth, int aHeight, size_t* aSize)
|
||||
{
|
||||
CheckedInt<size_t> size = aWidth;
|
||||
size *= aHeight;
|
||||
if (!size.isValid()) {
|
||||
return false;
|
||||
}
|
||||
*aSize = size.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
DownshiftPlane(uint8_t* aDst, int aDstStride, const uint8_t* aSrc,
|
||||
ptrdiff_t aSrcStride, int aWidth, int aHeight, int aShift)
|
||||
{
|
||||
for (int y = 0; y < aHeight; y++) {
|
||||
const uint16_t* src =
|
||||
reinterpret_cast<const uint16_t*>(aSrc + y * aSrcStride);
|
||||
uint8_t* dst = aDst + y * aDstStride;
|
||||
for (int x = 0; x < aWidth; x++) {
|
||||
dst[x] = (src[x] >> aShift) & 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MediaResult
|
||||
Dav1dDecoder::OutputPicture(const Dav1dPicture& aPicture)
|
||||
{
|
||||
const int width = aPicture.p.w;
|
||||
const int height = aPicture.p.h;
|
||||
if (width <= 0 || height <= 0) {
|
||||
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
RESULT_DETAIL("dav1d returned invalid AV1 picture size"));
|
||||
}
|
||||
|
||||
int chromaWidth = width;
|
||||
int chromaHeight = height;
|
||||
switch (aPicture.p.layout) {
|
||||
case DAV1D_PIXEL_LAYOUT_I420:
|
||||
chromaWidth = (width + 1) >> 1;
|
||||
chromaHeight = (height + 1) >> 1;
|
||||
break;
|
||||
case DAV1D_PIXEL_LAYOUT_I422:
|
||||
chromaWidth = (width + 1) >> 1;
|
||||
chromaHeight = height;
|
||||
break;
|
||||
case DAV1D_PIXEL_LAYOUT_I444:
|
||||
break;
|
||||
default:
|
||||
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
RESULT_DETAIL("dav1d returned unsupported AV1 pixel layout: %d",
|
||||
int(aPicture.p.layout)));
|
||||
}
|
||||
|
||||
const int planeWidths[3] = { width, chromaWidth, chromaWidth };
|
||||
const int planeHeights[3] = { height, chromaHeight, chromaHeight };
|
||||
const ptrdiff_t srcStrides[3] = {
|
||||
aPicture.stride[0],
|
||||
aPicture.stride[1],
|
||||
aPicture.stride[1]
|
||||
};
|
||||
uint8_t* planeData[3] = {
|
||||
static_cast<uint8_t*>(aPicture.data[0]),
|
||||
static_cast<uint8_t*>(aPicture.data[1]),
|
||||
static_cast<uint8_t*>(aPicture.data[2])
|
||||
};
|
||||
uint32_t planeStrides[3] = { 0, 0, 0 };
|
||||
UniquePtr<uint8_t[]> downshifted[3];
|
||||
|
||||
if (aPicture.p.bpc > 8) {
|
||||
const int shift = aPicture.p.bpc - 8;
|
||||
for (int plane = 0; plane < 3; plane++) {
|
||||
size_t planeSize;
|
||||
if (!GetPlaneSize(planeWidths[plane], planeHeights[plane], &planeSize)) {
|
||||
return MediaResult(NS_ERROR_OUT_OF_MEMORY,
|
||||
RESULT_DETAIL("AV1 downshift plane size overflow"));
|
||||
}
|
||||
downshifted[plane] = MakeUniqueFallible<uint8_t[]>(planeSize);
|
||||
if (!downshifted[plane]) {
|
||||
return MediaResult(NS_ERROR_OUT_OF_MEMORY,
|
||||
RESULT_DETAIL("Couldn't allocate AV1 conversion buffer"));
|
||||
}
|
||||
DownshiftPlane(downshifted[plane].get(), planeWidths[plane],
|
||||
planeData[plane], srcStrides[plane],
|
||||
planeWidths[plane], planeHeights[plane], shift);
|
||||
planeData[plane] = downshifted[plane].get();
|
||||
planeStrides[plane] = planeWidths[plane];
|
||||
}
|
||||
} else {
|
||||
for (int plane = 0; plane < 3; plane++) {
|
||||
CheckedInt<uint32_t> stride(srcStrides[plane]);
|
||||
if (srcStrides[plane] < 0 || !stride.isValid()) {
|
||||
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
RESULT_DETAIL("dav1d returned invalid AV1 stride"));
|
||||
}
|
||||
planeStrides[plane] = stride.value();
|
||||
}
|
||||
}
|
||||
|
||||
VideoData::YCbCrBuffer b;
|
||||
for (int plane = 0; plane < 3; plane++) {
|
||||
b.mPlanes[plane].mData = planeData[plane];
|
||||
b.mPlanes[plane].mStride = planeStrides[plane];
|
||||
b.mPlanes[plane].mHeight = planeHeights[plane];
|
||||
b.mPlanes[plane].mWidth = planeWidths[plane];
|
||||
b.mPlanes[plane].mOffset = 0;
|
||||
b.mPlanes[plane].mSkip = 0;
|
||||
}
|
||||
|
||||
if (aPicture.seq_hdr) {
|
||||
switch (aPicture.seq_hdr->mtrx) {
|
||||
case DAV1D_MC_BT601:
|
||||
case DAV1D_MC_BT470BG:
|
||||
b.mYUVColorSpace = YUVColorSpace::BT601;
|
||||
break;
|
||||
case DAV1D_MC_BT709:
|
||||
b.mYUVColorSpace = YUVColorSpace::BT709;
|
||||
break;
|
||||
case DAV1D_MC_IDENTITY:
|
||||
b.mYUVColorSpace = YUVColorSpace::IDENTITY;
|
||||
break;
|
||||
default:
|
||||
LOG("Unhandled colorspace %d", aPicture.seq_hdr->mtrx);
|
||||
break;
|
||||
}
|
||||
b.mColorRange = aPicture.seq_hdr->color_range ? ColorRange::FULL
|
||||
: ColorRange::LIMITED;
|
||||
}
|
||||
|
||||
const int64_t time =
|
||||
aPicture.m.timestamp == INT64_MIN ? 0 : aPicture.m.timestamp;
|
||||
const int64_t duration = aPicture.m.duration;
|
||||
const int64_t offset = aPicture.m.offset;
|
||||
const bool keyframe =
|
||||
aPicture.frame_hdr &&
|
||||
aPicture.frame_hdr->frame_type == DAV1D_FRAME_TYPE_KEY;
|
||||
|
||||
RefPtr<VideoData> v =
|
||||
VideoData::CreateAndCopyData(mInfo,
|
||||
mImageContainer,
|
||||
offset,
|
||||
time,
|
||||
duration,
|
||||
b,
|
||||
keyframe,
|
||||
time,
|
||||
mInfo.ScaledImageRect(width, height));
|
||||
|
||||
if (!v) {
|
||||
LOG("Image allocation error source %dx%d display %ux%u picture %ux%u",
|
||||
width, height, mInfo.mDisplay.width, mInfo.mDisplay.height,
|
||||
mInfo.mImage.width, mInfo.mImage.height);
|
||||
return MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__);
|
||||
}
|
||||
mCallback->Output(v);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MediaResult
|
||||
Dav1dDecoder::DrainOutput()
|
||||
{
|
||||
while (true) {
|
||||
Dav1dPicture picture;
|
||||
PodZero(&picture);
|
||||
const int res = dav1d_get_picture(mDecoder, &picture);
|
||||
if (res == DAV1D_ERR(EAGAIN)) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (res < 0) {
|
||||
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
RESULT_DETAIL("dav1d error getting AV1 picture: %d",
|
||||
res));
|
||||
}
|
||||
|
||||
MediaResult rv = OutputPicture(picture);
|
||||
dav1d_picture_unref(&picture);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MediaResult
|
||||
Dav1dDecoder::DoDecode(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
|
||||
Dav1dData data;
|
||||
PodZero(&data);
|
||||
uint8_t* dst = dav1d_data_create(&data, aSample->Size());
|
||||
if (!dst) {
|
||||
return MediaResult(NS_ERROR_OUT_OF_MEMORY,
|
||||
RESULT_DETAIL("Couldn't allocate dav1d AV1 input buffer"));
|
||||
}
|
||||
memcpy(dst, aSample->Data(), aSample->Size());
|
||||
data.m.timestamp = aSample->mTime;
|
||||
data.m.duration = aSample->mDuration;
|
||||
data.m.offset = aSample->mOffset;
|
||||
data.m.size = aSample->Size();
|
||||
|
||||
while (data.sz) {
|
||||
const int res = dav1d_send_data(mDecoder, &data);
|
||||
if (res == DAV1D_ERR(EAGAIN)) {
|
||||
MediaResult rv = DrainOutput();
|
||||
if (NS_FAILED(rv)) {
|
||||
dav1d_data_unref(&data);
|
||||
return rv;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (res < 0) {
|
||||
dav1d_data_unref(&data);
|
||||
return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
RESULT_DETAIL("dav1d error decoding AV1 sample: %d",
|
||||
res));
|
||||
}
|
||||
}
|
||||
|
||||
dav1d_data_unref(&data);
|
||||
return DrainOutput();
|
||||
}
|
||||
|
||||
void
|
||||
Dav1dDecoder::ProcessDecode(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
if (mIsFlushing) {
|
||||
return;
|
||||
}
|
||||
MediaResult rv = DoDecode(aSample);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCallback->Error(rv);
|
||||
} else {
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Dav1dDecoder::Input(MediaRawData* aSample)
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>(
|
||||
this, &Dav1dDecoder::ProcessDecode, aSample));
|
||||
}
|
||||
|
||||
void
|
||||
Dav1dDecoder::ProcessDrain()
|
||||
{
|
||||
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||
MediaResult rv = DrainOutput();
|
||||
if (NS_FAILED(rv)) {
|
||||
mCallback->Error(rv);
|
||||
} else {
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Dav1dDecoder::Drain()
|
||||
{
|
||||
MOZ_ASSERT(mCallback->OnReaderTaskQueue());
|
||||
mTaskQueue->Dispatch(NewRunnableMethod(this, &Dav1dDecoder::ProcessDrain));
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
Dav1dDecoder::IsAV1(const nsACString& aMimeType)
|
||||
{
|
||||
return aMimeType.EqualsLiteral("video/webm; codecs=av1") ||
|
||||
aMimeType.EqualsLiteral("video/av1");
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsIntSize
|
||||
Dav1dDecoder::GetFrameSize(Span<const uint8_t> aBuffer)
|
||||
{
|
||||
Dav1dSequenceHeader seqHdr;
|
||||
PodZero(&seqHdr);
|
||||
if (dav1d_parse_sequence_header(&seqHdr,
|
||||
aBuffer.Elements(),
|
||||
aBuffer.Length()) < 0) {
|
||||
return nsIntSize(0, 0);
|
||||
}
|
||||
return nsIntSize(seqHdr.max_width, seqHdr.max_height);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
#undef LOG
|
||||
@@ -1,61 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#if !defined(Dav1dDecoder_h_)
|
||||
#define Dav1dDecoder_h_
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mozilla/Span.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct Dav1dContext Dav1dContext;
|
||||
typedef struct Dav1dPicture Dav1dPicture;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class Dav1dDecoder : public MediaDataDecoder
|
||||
{
|
||||
public:
|
||||
explicit Dav1dDecoder(const CreateDecoderParams& aParams);
|
||||
|
||||
RefPtr<InitPromise> Init() override;
|
||||
void Input(MediaRawData* aSample) override;
|
||||
void Flush() override;
|
||||
void Drain() override;
|
||||
void Shutdown() override;
|
||||
const char* GetDescriptionName() const override
|
||||
{
|
||||
return "dav1d (AV1) video decoder";
|
||||
}
|
||||
|
||||
// Return true if aMimeType is a one of the strings used
|
||||
// by our demuxers to identify AV1 streams.
|
||||
static bool IsAV1(const nsACString& aMimeType);
|
||||
|
||||
// Return the frame dimensions from a sequence header, when one is present.
|
||||
static nsIntSize GetFrameSize(Span<const uint8_t> aBuffer);
|
||||
|
||||
private:
|
||||
~Dav1dDecoder();
|
||||
void ProcessDecode(MediaRawData* aSample);
|
||||
MediaResult DoDecode(MediaRawData* aSample);
|
||||
MediaResult DrainOutput();
|
||||
MediaResult OutputPicture(const Dav1dPicture& aPicture);
|
||||
void ProcessDrain();
|
||||
|
||||
const RefPtr<layers::ImageContainer> mImageContainer;
|
||||
const RefPtr<TaskQueue> mTaskQueue;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
Atomic<bool> mIsFlushing;
|
||||
|
||||
Dav1dContext* mDecoder;
|
||||
|
||||
const VideoInfo& mInfo;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // Dav1dDecoder_h_
|
||||
@@ -55,20 +55,12 @@ if CONFIG['MOZ_FFMPEG']:
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_AV1']:
|
||||
if CONFIG['AV1_USE_AOM']:
|
||||
EXPORTS += [
|
||||
'agnostic/AOMDecoder.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'agnostic/AOMDecoder.cpp',
|
||||
]
|
||||
else:
|
||||
EXPORTS += [
|
||||
'agnostic/Dav1dDecoder.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'agnostic/Dav1dDecoder.cpp',
|
||||
]
|
||||
EXPORTS += [
|
||||
'agnostic/AOMDecoder.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'agnostic/AOMDecoder.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_APPLEMEDIA']:
|
||||
EXPORTS += [
|
||||
|
||||
@@ -26,7 +26,7 @@ ThreadSharedFloatArrayBufferList::Create(uint32_t aChannelCount,
|
||||
new ThreadSharedFloatArrayBufferList(aChannelCount);
|
||||
|
||||
for (uint32_t i = 0; i < aChannelCount; ++i) {
|
||||
float* channelData = js_pod_calloc<float>(aLength);
|
||||
float* channelData = js_pod_malloc<float>(aLength);
|
||||
if (!channelData) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ public:
|
||||
}
|
||||
/**
|
||||
* Create with buffers suitable for transfer to
|
||||
* JS_NewArrayBufferWithContents(). The buffer contents are
|
||||
* zero-initialized; callers should set them using GetDataForWrite().
|
||||
* JS_NewArrayBufferWithContents(). The buffer contents are uninitialized
|
||||
* and so should be set using GetDataForWrite().
|
||||
*/
|
||||
static already_AddRefed<ThreadSharedFloatArrayBufferList>
|
||||
Create(uint32_t aChannelCount, size_t aLength, const mozilla::fallible_t&);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#if defined(MOZ_AV1) && defined(AV1_USE_AOM)
|
||||
#ifdef MOZ_AV1
|
||||
#include "AOMDecoder.h"
|
||||
#endif
|
||||
#include "MediaPrefs.h"
|
||||
@@ -136,3 +136,4 @@ WebMDecoder::GetMozDebugReaderData(nsAString& aString)
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
+1280
-1306
File diff suppressed because it is too large.
Load diff
@@ -24,11 +24,6 @@ if CONFIG['MOZ_WEBM_ENCODER']:
|
||||
'WebMWriter.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_AV1']:
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/media/platforms/agnostic',
|
||||
]
|
||||
|
||||
CXXFLAGS += CONFIG['MOZ_LIBVPX_CFLAGS']
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
@@ -293,12 +293,6 @@ SpeechSynthesis::ForceEnd()
|
||||
}
|
||||
}
|
||||
|
||||
void SpeechSynthesis::DisconnectFromOwner() {
|
||||
Pause();
|
||||
Cancel();
|
||||
DOMEventTargetHelper::DisconnectFromOwner();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SpeechSynthesis::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
|
||||
@@ -58,8 +58,6 @@ public:
|
||||
void GetVoices(nsTArray< RefPtr<SpeechSynthesisVoice> >& aResult);
|
||||
|
||||
void ForceEnd();
|
||||
|
||||
void DisconnectFromOwner() override;
|
||||
|
||||
IMPL_EVENT_HANDLER(voiceschanged)
|
||||
|
||||
|
||||
@@ -325,12 +325,12 @@ nsSynthVoiceRegistry::RemoveVoice(nsISpeechService* aService,
|
||||
(XRE_IsContentProcess()) ? "child" : "parent"));
|
||||
|
||||
bool found = false;
|
||||
RefPtr<VoiceData> retval = mUriVoiceMap.GetWeak(aUri, &found);
|
||||
VoiceData* retval = mUriVoiceMap.GetWeak(aUri, &found);
|
||||
|
||||
if(NS_WARN_IF(!found)) {
|
||||
if(NS_WARN_IF(!(found))) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
if(NS_WARN_IF(aService != retval->mService)) {
|
||||
if(NS_WARN_IF(!(aService == retval->mService))) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ UDPSocketParent::DoConnect(nsCOMPtr<nsIUDPSocket>& aSocket,
|
||||
const UDPAddressInfo& aAddressInfo)
|
||||
{
|
||||
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(), aAddressInfo.port()));
|
||||
if (NS_FAILED(ConnectInternal(aSocket, aAddressInfo.addr(), aAddressInfo.port()))) {
|
||||
if (NS_FAILED(ConnectInternal(aAddressInfo.addr(), aAddressInfo.port()))) {
|
||||
SendInternalError(aReturnThread, __LINE__);
|
||||
return;
|
||||
}
|
||||
@@ -320,15 +320,13 @@ UDPSocketParent::DoConnect(nsCOMPtr<nsIUDPSocket>& aSocket,
|
||||
}
|
||||
|
||||
nsresult
|
||||
UDPSocketParent::ConnectInternal(const nsCOMPtr<nsIUDPSocket>& aSocket,
|
||||
const nsCString& aHost,
|
||||
const uint16_t& aPort)
|
||||
UDPSocketParent::ConnectInternal(const nsCString& aHost, const uint16_t& aPort)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, nsCString(aHost).get(), aPort));
|
||||
|
||||
if (!aSocket) {
|
||||
if (!mSocket) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
@@ -342,7 +340,7 @@ UDPSocketParent::ConnectInternal(const nsCOMPtr<nsIUDPSocket>& aSocket,
|
||||
mozilla::net::NetAddr addr;
|
||||
PRNetAddrToNetAddr(&prAddr, &addr);
|
||||
|
||||
rv = aSocket->Connect(&addr);
|
||||
rv = mSocket->Connect(&addr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -62,9 +62,7 @@ private:
|
||||
const bool& aAddressReuse, const bool& aLoopback,
|
||||
const uint32_t& recvBufferSize,
|
||||
const uint32_t& sendBufferSize);
|
||||
nsresult ConnectInternal(const nsCOMPtr<nsIUDPSocket>& aSocket,
|
||||
const nsCString& aHost,
|
||||
const uint16_t& aPort);
|
||||
nsresult ConnectInternal(const nsCString& aHost, const uint16_t& aPort);
|
||||
void FireInternalError(uint32_t aLineNo);
|
||||
void SendInternalError(nsIEventTarget *aThread,
|
||||
uint32_t aLineNo);
|
||||
|
||||
+5
-14
@@ -1789,26 +1789,17 @@ URL::IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL,
|
||||
return URLWorker::IsValidURL(aGlobal, aURL, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<URL>
|
||||
URL::Parse(const GlobalObject& aGlobal, const nsAString& aURL,
|
||||
const Optional<nsAString>& aBase)
|
||||
{
|
||||
bool
|
||||
URL::CanParse(const GlobalObject& aGlobal, const nsAString& aURL,
|
||||
const Optional<nsAString>& aBase) {
|
||||
ErrorResult rv;
|
||||
RefPtr<URL> parsed = URL::Constructor(aGlobal, aURL, aBase, rv);
|
||||
if (rv.Failed() || !parsed) {
|
||||
rv.SuppressException();
|
||||
return nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return parsed.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
URL::CanParse(const GlobalObject& aGlobal, const nsAString& aURL,
|
||||
const Optional<nsAString>& aBase)
|
||||
{
|
||||
RefPtr<URL> parsed = URL::Parse(aGlobal, aURL, aBase);
|
||||
return !!parsed;
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<URLSearchParams>
|
||||
|
||||
@@ -88,10 +88,6 @@ public:
|
||||
IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<URL>
|
||||
Parse(const GlobalObject& aGlobal, const nsAString& aURL,
|
||||
const Optional<nsAString>& aBase);
|
||||
|
||||
static bool
|
||||
CanParse(const GlobalObject& aGlobal, const nsAString& aURL,
|
||||
const Optional<nsAString>& aBase);
|
||||
|
||||
@@ -448,34 +448,6 @@
|
||||
!URL.canParse("/static/client/runtime.js", "mailto:test@example.com"),
|
||||
"URL.canParse rejects root-relative input against opaque mailto: base",
|
||||
);
|
||||
ok("parse" in URL, "URL.parse exists");
|
||||
is(URL.parse.length, 1, "URL.parse has one required argument");
|
||||
var parsed = URL.parse("/static/client/runtime.js", "x:/");
|
||||
ok(parsed instanceof URL, "URL.parse returns a URL object on success");
|
||||
is(parsed.href, "x:/static/client/runtime.js", "URL.parse resolves against a base");
|
||||
is(URL.parse("/static/client/runtime.js"), null, "URL.parse returns null for invalid input");
|
||||
is(
|
||||
URL.parse("/static/client/runtime.js", "mailto:test@example.com"),
|
||||
null,
|
||||
"URL.parse returns null for an invalid base/input combination",
|
||||
);
|
||||
|
||||
var conversionOrder = [];
|
||||
parsed = URL.parse(
|
||||
{ toString: function() { conversionOrder.push("url"); return "child"; } },
|
||||
{ toString: function() { conversionOrder.push("base"); return "https://example.com/root/"; } },
|
||||
);
|
||||
is(conversionOrder.join(","), "url,base", "URL.parse converts its arguments in order");
|
||||
is(parsed.href, "https://example.com/root/child", "URL.parse uses converted arguments");
|
||||
|
||||
var sentinel = {};
|
||||
var thrown;
|
||||
try {
|
||||
URL.parse({ toString: function() { throw sentinel; } });
|
||||
} catch (e) {
|
||||
thrown = e;
|
||||
}
|
||||
is(thrown, sentinel, "URL.parse propagates argument conversion exceptions");
|
||||
SimpleTest.doesThrow(() => {
|
||||
new URL("/static/client/runtime.js", "mailto:test@example.com");
|
||||
}, "constructor rejects root-relative input against opaque mailto: base");
|
||||
|
||||
@@ -301,64 +301,6 @@ onmessage = function () {
|
||||
!URL.canParse("/static/client/runtime.js", "mailto:test@example.com"),
|
||||
"URL.canParse rejects root-relative input against opaque mailto: base",
|
||||
);
|
||||
ok("parse" in URL, "URL.parse exists");
|
||||
is(URL.parse.length, 1, "URL.parse has one required argument");
|
||||
let parsed = URL.parse("/static/client/runtime.js", "x:/");
|
||||
ok(parsed instanceof URL, "URL.parse returns a URL object on success");
|
||||
is(
|
||||
parsed.href,
|
||||
"x:/static/client/runtime.js",
|
||||
"URL.parse resolves against a base",
|
||||
);
|
||||
is(
|
||||
URL.parse("/static/client/runtime.js"),
|
||||
null,
|
||||
"URL.parse returns null for invalid input",
|
||||
);
|
||||
is(
|
||||
URL.parse("/static/client/runtime.js", "mailto:test@example.com"),
|
||||
null,
|
||||
"URL.parse returns null for an invalid base/input combination",
|
||||
);
|
||||
|
||||
const conversionOrder = [];
|
||||
parsed = URL.parse(
|
||||
{
|
||||
toString() {
|
||||
conversionOrder.push("url");
|
||||
return "child";
|
||||
},
|
||||
},
|
||||
{
|
||||
toString() {
|
||||
conversionOrder.push("base");
|
||||
return "https://example.com/root/";
|
||||
},
|
||||
},
|
||||
);
|
||||
is(
|
||||
conversionOrder.join(","),
|
||||
"url,base",
|
||||
"URL.parse converts its arguments in order",
|
||||
);
|
||||
is(
|
||||
parsed.href,
|
||||
"https://example.com/root/child",
|
||||
"URL.parse uses converted arguments",
|
||||
);
|
||||
|
||||
const sentinel = {};
|
||||
let thrown;
|
||||
try {
|
||||
URL.parse({
|
||||
toString() {
|
||||
throw sentinel;
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
thrown = e;
|
||||
}
|
||||
is(thrown, sentinel, "URL.parse propagates argument conversion exceptions");
|
||||
let mailtoRelativeFailed = false;
|
||||
try {
|
||||
new URL("/static/client/runtime.js", "mailto:test@example.com");
|
||||
|
||||
@@ -13,7 +13,6 @@ enum CSSStyleSheetParsingMode {
|
||||
"agent"
|
||||
};
|
||||
|
||||
[Constructor]
|
||||
interface CSSStyleSheet : StyleSheet {
|
||||
[Pure]
|
||||
readonly attribute CSSRule? ownerRule;
|
||||
|
||||
@@ -34,6 +34,7 @@ interface Element : Node {
|
||||
|
||||
[SameObject]
|
||||
readonly attribute NamedNodeMap attributes;
|
||||
[Pure]
|
||||
sequence<DOMString> getAttributeNames();
|
||||
[Pure]
|
||||
DOMString? getAttribute(DOMString name);
|
||||
@@ -158,7 +159,7 @@ interface Element : Node {
|
||||
* this property returns an object with computed values for grid
|
||||
* tracks and lines.
|
||||
*/
|
||||
[ChromeOnly]
|
||||
[ChromeOnly, Pure]
|
||||
sequence<Grid> getGridFragments();
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ interface HTMLElement : Element {
|
||||
[CEReactions, SetterThrows, Pure]
|
||||
attribute DOMString dir;
|
||||
|
||||
[CEReactions, GetterThrows]
|
||||
[CEReactions, GetterThrows, Pure]
|
||||
attribute [TreatNullAs=EmptyString] DOMString innerText;
|
||||
|
||||
// user interaction
|
||||
|
||||
@@ -51,7 +51,7 @@ interface HTMLInputElement : HTMLElement {
|
||||
attribute boolean formNoValidate;
|
||||
[CEReactions, Pure, SetterThrows]
|
||||
attribute DOMString formTarget;
|
||||
[CEReactions, SetterThrows]
|
||||
[CEReactions, Pure, SetterThrows]
|
||||
attribute unsigned long height;
|
||||
[Pure]
|
||||
attribute boolean indeterminate;
|
||||
|
||||
@@ -23,7 +23,6 @@ interface URL {
|
||||
[Throws]
|
||||
stringifier;
|
||||
|
||||
static URL? parse(USVString url, optional USVString base);
|
||||
static boolean canParse(USVString url, optional USVString base);
|
||||
|
||||
[Throws]
|
||||
|
||||
@@ -301,6 +301,7 @@ LoadContextOptions(const char* aPrefName, void* /* aClosure */)
|
||||
.setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
|
||||
.setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
|
||||
.setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams")))
|
||||
.setWeakRefs(GetWorkerPref<bool>(NS_LITERAL_CSTRING("weakrefs")))
|
||||
.setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")))
|
||||
.setArrayProtoValues(GetWorkerPref<bool>(
|
||||
NS_LITERAL_CSTRING("array_prototype_values")));
|
||||
|
||||
@@ -35,6 +35,7 @@ WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, O
|
||||
WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.streams.enabled", StreamsEnabled, STREAMS_ENABLED)
|
||||
WORKER_SIMPLE_PREF("javascript.options.weakrefs", WeakRefsEnabled, WEAKREFS_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
|
||||
WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)
|
||||
|
||||
@@ -823,37 +823,16 @@ nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||
}
|
||||
}
|
||||
|
||||
// The approach in WalkAllShadowRootHostRules seems reasonable on the surface and reminds me of what is done with mScopedRoot elsewhere. But something important is missing, either here or in the code that would normally use it.
|
||||
|
||||
void
|
||||
nsBindingManager::WalkAllShadowRootHostRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||
ElementDependentRuleProcessorData* aData)
|
||||
{
|
||||
if (!mBoundContentSet) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool oldOnlyMatchHostPseudo = aData->mTreeMatchContext.mOnlyMatchHostPseudo;
|
||||
nsIContent* oldScopedRoot = aData->mTreeMatchContext.mScopedRoot;
|
||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = true;
|
||||
|
||||
for (auto iter = mBoundContentSet->Iter(); !iter.Done(); iter.Next()) {
|
||||
nsIContent* boundContent = iter.Get()->GetKey();
|
||||
ShadowRoot* shadowRoot = boundContent->GetShadowRoot();
|
||||
if (!shadowRoot) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsXBLBinding* binding = shadowRoot->GetAssociatedBinding();
|
||||
if (!binding) {
|
||||
continue;
|
||||
}
|
||||
|
||||
aData->mTreeMatchContext.mScopedRoot = boundContent;
|
||||
binding->WalkRules(aFunc, aData);
|
||||
}
|
||||
|
||||
aData->mTreeMatchContext.mScopedRoot = oldScopedRoot;
|
||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = oldOnlyMatchHostPseudo;
|
||||
}
|
||||
{
|
||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = true;
|
||||
WalkAllRules(aFunc, aData, true);
|
||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
|
||||
|
||||
@@ -7,6 +7,8 @@ UNIFIED_SOURCES += [
|
||||
'CharDistribution.cpp',
|
||||
'JpCntx.cpp',
|
||||
'nsCharSetProber.cpp',
|
||||
'nsEscCharsetProber.cpp',
|
||||
'nsEscSM.cpp',
|
||||
'nsEUCJPProber.cpp',
|
||||
'nsLatin1Prober.cpp',
|
||||
'nsMBCSGroupProber.cpp',
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/* -*- Mode: C; 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 "nsEscCharsetProber.h"
|
||||
#include "nsUniversalDetector.h"
|
||||
|
||||
nsEscCharSetProber::nsEscCharSetProber()
|
||||
{
|
||||
mCodingSM = new nsCodingStateMachine(&ISO2022JPSMModel);
|
||||
mState = eDetecting;
|
||||
mDetectedCharset = nullptr;
|
||||
}
|
||||
|
||||
nsEscCharSetProber::~nsEscCharSetProber(void)
|
||||
{
|
||||
}
|
||||
|
||||
void nsEscCharSetProber::Reset(void)
|
||||
{
|
||||
mState = eDetecting;
|
||||
mCodingSM->Reset();
|
||||
mDetectedCharset = nullptr;
|
||||
}
|
||||
|
||||
nsProbingState nsEscCharSetProber::HandleData(const char* aBuf, uint32_t aLen)
|
||||
{
|
||||
nsSMState codingState;
|
||||
uint32_t i;
|
||||
|
||||
for ( i = 0; i < aLen && mState == eDetecting; i++)
|
||||
{
|
||||
codingState = mCodingSM->NextState(aBuf[i]);
|
||||
if (codingState == eItsMe)
|
||||
{
|
||||
mState = eFoundIt;
|
||||
mDetectedCharset = mCodingSM->GetCodingStateMachine();
|
||||
return mState;
|
||||
}
|
||||
}
|
||||
|
||||
return mState;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/* -*- Mode: C; 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/. */
|
||||
|
||||
#ifndef nsEscCharSetProber_h__
|
||||
#define nsEscCharSetProber_h__
|
||||
|
||||
#include "nsCharSetProber.h"
|
||||
#include "nsCodingStateMachine.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
class nsEscCharSetProber: public nsCharSetProber {
|
||||
public:
|
||||
nsEscCharSetProber();
|
||||
virtual ~nsEscCharSetProber(void);
|
||||
nsProbingState HandleData(const char* aBuf, uint32_t aLen);
|
||||
const char* GetCharSetName() {return mDetectedCharset;}
|
||||
nsProbingState GetState(void) {return mState;}
|
||||
void Reset(void);
|
||||
float GetConfidence(void){return (float)0.99;}
|
||||
|
||||
protected:
|
||||
void GetDistribution(uint32_t aCharLen, const char* aStr);
|
||||
|
||||
nsAutoPtr<nsCodingStateMachine> mCodingSM;
|
||||
nsProbingState mState;
|
||||
const char * mDetectedCharset;
|
||||
};
|
||||
|
||||
#endif /* nsEscCharSetProber_h__ */
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/* -*- Mode: C; 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 "nsCodingStateMachine.h"
|
||||
|
||||
static const uint32_t ISO2022JP_cls [ 256 / 8 ] = {
|
||||
PCK4BITS(2,0,0,0,0,0,0,0), // 00 - 07
|
||||
PCK4BITS(0,0,0,0,0,0,2,2), // 08 - 0f
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 10 - 17
|
||||
PCK4BITS(0,0,0,1,0,0,0,0), // 18 - 1f
|
||||
PCK4BITS(0,0,0,0,7,0,0,0), // 20 - 27
|
||||
PCK4BITS(3,0,0,0,0,0,0,0), // 28 - 2f
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 30 - 37
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 38 - 3f
|
||||
PCK4BITS(6,0,4,0,8,0,0,0), // 40 - 47
|
||||
PCK4BITS(0,9,5,0,0,0,0,0), // 48 - 4f
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 50 - 57
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 58 - 5f
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 60 - 67
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 68 - 6f
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 70 - 77
|
||||
PCK4BITS(0,0,0,0,0,0,0,0), // 78 - 7f
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // 80 - 87
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // 88 - 8f
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // 90 - 97
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // 98 - 9f
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // a0 - a7
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // a8 - af
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // b0 - b7
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // b8 - bf
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // c0 - c7
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // c8 - cf
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // d0 - d7
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // d8 - df
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // e0 - e7
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // e8 - ef
|
||||
PCK4BITS(2,2,2,2,2,2,2,2), // f0 - f7
|
||||
PCK4BITS(2,2,2,2,2,2,2,2) // f8 - ff
|
||||
};
|
||||
|
||||
|
||||
static const uint32_t ISO2022JP_st [ 9] = {
|
||||
PCK4BITS(eStart, 3,eError,eStart,eStart,eStart,eStart,eStart),//00-07
|
||||
PCK4BITS(eStart,eStart,eError,eError,eError,eError,eError,eError),//08-0f
|
||||
PCK4BITS(eError,eError,eError,eError,eItsMe,eItsMe,eItsMe,eItsMe),//10-17
|
||||
PCK4BITS(eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eItsMe,eError,eError),//18-1f
|
||||
PCK4BITS(eError, 5,eError,eError,eError, 4,eError,eError),//20-27
|
||||
PCK4BITS(eError,eError,eError, 6,eItsMe,eError,eItsMe,eError),//28-2f
|
||||
PCK4BITS(eError,eError,eError,eError,eError,eError,eItsMe,eItsMe),//30-37
|
||||
PCK4BITS(eError,eError,eError,eItsMe,eError,eError,eError,eError),//38-3f
|
||||
PCK4BITS(eError,eError,eError,eError,eItsMe,eError,eStart,eStart) //40-47
|
||||
};
|
||||
|
||||
static const uint32_t ISO2022JPCharLenTable[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
const SMModel ISO2022JPSMModel = {
|
||||
{eIdxSft4bits, eSftMsk4bits, eBitSft4bits, eUnitMsk4bits, ISO2022JP_cls },
|
||||
10,
|
||||
{eIdxSft4bits, eSftMsk4bits, eBitSft4bits, eUnitMsk4bits, ISO2022JP_st },
|
||||
CHAR_LEN_TABLE(ISO2022JPCharLenTable),
|
||||
"ISO-2022-JP",
|
||||
};
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "nsUniversalDetector.h"
|
||||
|
||||
#include "nsMBCSGroupProber.h"
|
||||
#include "nsEscCharsetProber.h"
|
||||
#include "nsLatin1Prober.h"
|
||||
|
||||
nsUniversalDetector::nsUniversalDetector()
|
||||
@@ -15,6 +16,7 @@ nsUniversalDetector::nsUniversalDetector()
|
||||
mDone = false;
|
||||
mBestGuess = -1; //illegal value as signal
|
||||
mInTag = false;
|
||||
mEscCharSetProber = nullptr;
|
||||
|
||||
mStart = true;
|
||||
mDetectedCharset = nullptr;
|
||||
@@ -31,6 +33,8 @@ nsUniversalDetector::~nsUniversalDetector()
|
||||
{
|
||||
for (int32_t i = 0; i < NUM_OF_CHARSET_PROBERS; i++)
|
||||
delete mCharSetProbers[i];
|
||||
|
||||
delete mEscCharSetProber;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -46,6 +50,9 @@ nsUniversalDetector::Reset()
|
||||
mInputState = ePureAscii;
|
||||
mLastChar = '\0';
|
||||
|
||||
if (mEscCharSetProber)
|
||||
mEscCharSetProber->Reset();
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0; i < NUM_OF_CHARSET_PROBERS; i++)
|
||||
if (mCharSetProbers[i])
|
||||
@@ -110,6 +117,12 @@ nsresult nsUniversalDetector::HandleData(const char* aBuf, uint32_t aLen)
|
||||
//adjust state
|
||||
mInputState = eHighbyte;
|
||||
|
||||
//kill mEscCharSetProber if it is active
|
||||
if (mEscCharSetProber) {
|
||||
delete mEscCharSetProber;
|
||||
mEscCharSetProber = nullptr;
|
||||
}
|
||||
|
||||
//start multibyte and singlebyte charset prober
|
||||
if (nullptr == mCharSetProbers[0])
|
||||
{
|
||||
@@ -127,6 +140,12 @@ nsresult nsUniversalDetector::HandleData(const char* aBuf, uint32_t aLen)
|
||||
}
|
||||
else
|
||||
{
|
||||
//ok, just pure ascii so far
|
||||
if ((ePureAscii == mInputState) && (aBuf[i] == '\033'))
|
||||
{
|
||||
//found escape character
|
||||
mInputState = eEscAscii;
|
||||
}
|
||||
mLastChar = aBuf[i];
|
||||
}
|
||||
}
|
||||
@@ -134,6 +153,19 @@ nsresult nsUniversalDetector::HandleData(const char* aBuf, uint32_t aLen)
|
||||
nsProbingState st;
|
||||
switch (mInputState)
|
||||
{
|
||||
case eEscAscii:
|
||||
if (nullptr == mEscCharSetProber) {
|
||||
mEscCharSetProber = new nsEscCharSetProber();
|
||||
if (nullptr == mEscCharSetProber)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
st = mEscCharSetProber->HandleData(aBuf, aLen);
|
||||
if (st == eFoundIt)
|
||||
{
|
||||
mDone = true;
|
||||
mDetectedCharset = mEscCharSetProber->GetCharSetName();
|
||||
}
|
||||
break;
|
||||
case eHighbyte:
|
||||
for (i = 0; i < NUM_OF_CHARSET_PROBERS; i++)
|
||||
{
|
||||
@@ -199,6 +231,8 @@ void nsUniversalDetector::DataEnd()
|
||||
Report(mCharSetProbers[maxProber]->GetCharSetName());
|
||||
}
|
||||
break;
|
||||
case eEscAscii:
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ class nsCharSetProber;
|
||||
|
||||
typedef enum {
|
||||
ePureAscii = 0,
|
||||
eHighbyte = 1
|
||||
eEscAscii = 1,
|
||||
eHighbyte = 2
|
||||
} nsInputState;
|
||||
|
||||
class nsUniversalDetector {
|
||||
@@ -36,6 +37,7 @@ protected:
|
||||
uint32_t mLanguageFilter;
|
||||
|
||||
nsCharSetProber *mCharSetProbers[NUM_OF_CHARSET_PROBERS];
|
||||
nsCharSetProber *mEscCharSetProber;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -805,10 +805,6 @@ cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr)
|
||||
}
|
||||
operand = decode_integer (operand, &size);
|
||||
decode_integer (operand, &offset);
|
||||
if (unlikely (offset < 0 || (unsigned long)offset > font->data_length)) {
|
||||
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
goto fail;
|
||||
}
|
||||
status = cff_dict_init (&font->fd_private_dict[i]);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
@@ -870,8 +866,6 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font)
|
||||
operand = cff_dict_get_operands (font->top_dict, CHARSTRINGS_OP, &size);
|
||||
decode_integer (operand, &offset);
|
||||
p = font->data + offset;
|
||||
if (unlikely (p < font->data || p > font->data_end))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
status = cff_index_read (&font->charstrings_index, &p, font->data_end);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
@@ -880,58 +874,49 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font)
|
||||
if (font->is_cid) {
|
||||
operand = cff_dict_get_operands (font->top_dict, FDSELECT_OP, &size);
|
||||
decode_integer (operand, &offset);
|
||||
p = font->data + offset;
|
||||
if (unlikely (p < font->data || p > font->data_end))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
status = cairo_cff_font_read_fdselect (font, p);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
status = cairo_cff_font_read_fdselect (font, font->data + offset);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
|
||||
operand = cff_dict_get_operands (font->top_dict, FDARRAY_OP, &size);
|
||||
decode_integer (operand, &offset);
|
||||
p = font->data + offset;
|
||||
if (unlikely (p < font->data || p > font->data_end))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
status = cairo_cff_font_read_cid_fontdict (font, p);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
status = cairo_cff_font_read_cid_fontdict (font, font->data + offset);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
} else {
|
||||
operand = cff_dict_get_operands (font->top_dict, PRIVATE_OP, &size);
|
||||
operand = decode_integer (operand, &size);
|
||||
decode_integer (operand, &offset);
|
||||
p = font->data + offset;
|
||||
if (unlikely (p < font->data || p > font->data_end))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
status = cairo_cff_font_read_private_dict (font,
|
||||
font->private_dict,
|
||||
&font->local_sub_index,
|
||||
p,
|
||||
size);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
&font->local_sub_index,
|
||||
font->data + offset,
|
||||
size);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Use maximum sized encoding to reserve space for later modification. */
|
||||
end_buf = encode_integer_max (buf, 0);
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
CHARSTRINGS_OP, buf, end_buf - buf);
|
||||
CHARSTRINGS_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
goto fail;
|
||||
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
FDSELECT_OP, buf, end_buf - buf);
|
||||
FDSELECT_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
goto fail;
|
||||
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
FDARRAY_OP, buf, end_buf - buf);
|
||||
FDARRAY_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
goto fail;
|
||||
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
CHARSET_OP, buf, end_buf - buf);
|
||||
CHARSET_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
goto fail;
|
||||
|
||||
cff_dict_remove (font->top_dict, ENCODING_OP);
|
||||
cff_dict_remove (font->top_dict, PRIVATE_OP);
|
||||
@@ -1000,26 +985,26 @@ cairo_cff_font_set_ros_strings (cairo_cff_font_t *font)
|
||||
(unsigned char *)registry,
|
||||
strlen(registry));
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
sid2 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
|
||||
status = cff_index_append_copy (&font->strings_subset_index,
|
||||
(unsigned char *)ordering,
|
||||
strlen(ordering));
|
||||
strlen(ordering));
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
p = encode_integer (buf, sid1);
|
||||
p = encode_integer (p, sid2);
|
||||
p = encode_integer (p, 0);
|
||||
status = cff_dict_set_operands (font->top_dict, ROS_OP, buf, p - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
p = encode_integer (buf, font->scaled_font_subset->num_glyphs);
|
||||
status = cff_dict_set_operands (font->top_dict, CIDCOUNT_OP, buf, p - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -1053,7 +1038,7 @@ cairo_cff_font_subset_dict_string(cairo_cff_font_t *font,
|
||||
p = encode_integer (buf, sid);
|
||||
status = cff_dict_set_operands (dict, operator, buf, p - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -1116,19 +1101,19 @@ cairo_cff_font_subset_fontdict (cairo_cff_font_t *font)
|
||||
font->fdselect_subset = calloc (font->scaled_font_subset->num_glyphs,
|
||||
sizeof (int));
|
||||
if (unlikely (font->fdselect_subset == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
font->fd_subset_map = calloc (font->num_fontdicts, sizeof (int));
|
||||
if (unlikely (font->fd_subset_map == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
font->private_dict_offset = calloc (font->num_fontdicts, sizeof (int));
|
||||
if (unlikely (font->private_dict_offset == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
reverse_map = calloc (font->num_fontdicts, sizeof (int));
|
||||
if (unlikely (reverse_map == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
for (i = 0; i < font->num_fontdicts; i++)
|
||||
reverse_map[i] = -1;
|
||||
@@ -1158,22 +1143,22 @@ cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font)
|
||||
font->num_fontdicts = 1;
|
||||
font->fd_dict = malloc (sizeof (cairo_hash_table_t *));
|
||||
if (unlikely (font->fd_dict == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
if (cff_dict_init (&font->fd_dict[0])) {
|
||||
free (font->fd_dict);
|
||||
font->fd_dict = NULL;
|
||||
font->num_fontdicts = 0;
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
free (font->fd_dict);
|
||||
font->fd_dict = NULL;
|
||||
font->num_fontdicts = 0;
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
}
|
||||
|
||||
font->fd_subset_map = malloc (sizeof (int));
|
||||
if (unlikely (font->fd_subset_map == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
font->private_dict_offset = malloc (sizeof (int));
|
||||
if (unlikely (font->private_dict_offset == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
font->fd_subset_map[0] = 0;
|
||||
font->num_subset_fontdicts = 1;
|
||||
@@ -1184,7 +1169,7 @@ cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font)
|
||||
end_buf = encode_integer_max (end_buf, 0);
|
||||
status = cff_dict_set_operands (font->fd_dict[0], PRIVATE_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -1223,7 +1208,7 @@ cairo_cff_font_subset_font (cairo_cff_font_t *font)
|
||||
|
||||
status = cairo_cff_font_set_ros_strings (font);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cairo_cff_font_subset_charstrings (font);
|
||||
if (unlikely (status))
|
||||
@@ -1234,7 +1219,7 @@ cairo_cff_font_subset_font (cairo_cff_font_t *font)
|
||||
else
|
||||
status = cairo_cff_font_create_cid_fontdict (font);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cairo_cff_font_subset_strings (font);
|
||||
if (unlikely (status))
|
||||
@@ -1286,7 +1271,7 @@ cairo_cff_font_write_name (cairo_cff_font_t *font)
|
||||
(unsigned char *) font->subset_font_name,
|
||||
strlen(font->subset_font_name));
|
||||
if (unlikely (status))
|
||||
goto FAIL;
|
||||
goto FAIL;
|
||||
|
||||
status = cff_index_write (&index, &font->output);
|
||||
if (unlikely (status))
|
||||
@@ -1479,9 +1464,9 @@ cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font)
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
offset_array_ptr = (uint32_t *) _cairo_array_index (&font->output, offset_array);
|
||||
offset_array_ptr = (uint32_t *) _cairo_array_index (&font->output, offset_array);
|
||||
*offset_array_ptr = cpu_to_be32(_cairo_array_num_elements (&font->output) - offset_base);
|
||||
offset_array += sizeof(uint32_t);
|
||||
offset_array += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
@@ -1581,15 +1566,15 @@ cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font)
|
||||
0,
|
||||
font->fd_dict[0],
|
||||
font->private_dict);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
status = cairo_cff_font_write_local_sub (font,
|
||||
0,
|
||||
font->private_dict,
|
||||
&font->local_sub_index);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
}
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
@@ -1713,7 +1698,7 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
|
||||
|
||||
backend = scaled_font_subset->scaled_font->backend;
|
||||
if (!backend->load_truetype_table)
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
data_length = 0;
|
||||
status = backend->load_truetype_table( scaled_font_subset->scaled_font,
|
||||
@@ -1751,12 +1736,12 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
|
||||
_cairo_array_init (&font->output, sizeof (char));
|
||||
status = _cairo_array_grow_by (&font->output, 4096);
|
||||
if (unlikely (status))
|
||||
goto fail2;
|
||||
goto fail2;
|
||||
|
||||
font->subset_font_name = strdup (subset_name);
|
||||
if (unlikely (font->subset_font_name == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail2;
|
||||
goto fail2;
|
||||
}
|
||||
font->x_min = (int16_t) be16_to_cpu (head.x_min);
|
||||
font->y_min = (int16_t) be16_to_cpu (head.y_min);
|
||||
@@ -1770,18 +1755,18 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
|
||||
|
||||
font->font_name = NULL;
|
||||
status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font,
|
||||
&font->ps_name,
|
||||
&font->font_name);
|
||||
&font->ps_name,
|
||||
&font->font_name);
|
||||
if (_cairo_status_is_error (status))
|
||||
goto fail3;
|
||||
goto fail3;
|
||||
|
||||
/* If the PS name is not found, create a CairoFont-x-y name. */
|
||||
if (font->ps_name == NULL) {
|
||||
font->ps_name = malloc (30);
|
||||
if (unlikely (font->ps_name == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail3;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(font->ps_name, 30, "CairoFont-%u-%u",
|
||||
scaled_font_subset->font_id,
|
||||
@@ -1796,7 +1781,7 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
|
||||
|
||||
status = cairo_cff_font_create_set_widths (font);
|
||||
if (unlikely (status))
|
||||
goto fail5;
|
||||
goto fail5;
|
||||
|
||||
font->data_length = data_length;
|
||||
font->data = malloc (data_length);
|
||||
@@ -1814,11 +1799,11 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
|
||||
|
||||
status = cff_dict_init (&font->top_dict);
|
||||
if (unlikely (status))
|
||||
goto fail6;
|
||||
goto fail6;
|
||||
|
||||
status = cff_dict_init (&font->private_dict);
|
||||
if (unlikely (status))
|
||||
goto fail7;
|
||||
goto fail7;
|
||||
|
||||
cff_index_init (&font->strings_index);
|
||||
cff_index_init (&font->charstrings_index);
|
||||
@@ -1846,7 +1831,7 @@ fail5:
|
||||
free (font->widths);
|
||||
fail4:
|
||||
if (font->font_name)
|
||||
free (font->font_name);
|
||||
free (font->font_name);
|
||||
fail3:
|
||||
free (font->subset_font_name);
|
||||
fail2:
|
||||
@@ -1863,7 +1848,7 @@ cairo_cff_font_destroy (cairo_cff_font_t *font)
|
||||
|
||||
free (font->widths);
|
||||
if (font->font_name)
|
||||
free (font->font_name);
|
||||
free (font->font_name);
|
||||
free (font->ps_name);
|
||||
free (font->subset_font_name);
|
||||
_cairo_array_fini (&font->output);
|
||||
@@ -1917,7 +1902,7 @@ cairo_cff_font_destroy (cairo_cff_font_t *font)
|
||||
|
||||
cairo_status_t
|
||||
_cairo_cff_subset_init (cairo_cff_subset_t *cff_subset,
|
||||
const char *subset_name,
|
||||
const char *subset_name,
|
||||
cairo_scaled_font_subset_t *font_subset)
|
||||
{
|
||||
cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */
|
||||
@@ -1928,32 +1913,32 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset,
|
||||
|
||||
status = _cairo_cff_font_create (font_subset, &font, subset_name);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cairo_cff_font_generate (font, &data, &length);
|
||||
if (unlikely (status))
|
||||
goto fail1;
|
||||
goto fail1;
|
||||
|
||||
cff_subset->ps_name = strdup (font->ps_name);
|
||||
if (unlikely (cff_subset->ps_name == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail1;
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
if (font->font_name) {
|
||||
cff_subset->font_name = strdup (font->font_name);
|
||||
if (cff_subset->font_name == NULL) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail2;
|
||||
}
|
||||
cff_subset->font_name = strdup (font->font_name);
|
||||
if (cff_subset->font_name == NULL) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail2;
|
||||
}
|
||||
} else {
|
||||
cff_subset->font_name = NULL;
|
||||
cff_subset->font_name = NULL;
|
||||
}
|
||||
|
||||
cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
|
||||
if (unlikely (cff_subset->widths == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail3;
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail3;
|
||||
}
|
||||
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
|
||||
cff_subset->widths[i] = (double)font->widths[i]/font->units_per_em;
|
||||
@@ -1967,8 +1952,8 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset,
|
||||
|
||||
cff_subset->data = malloc (length);
|
||||
if (unlikely (cff_subset->data == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail4;
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
memcpy (cff_subset->data, data, length);
|
||||
@@ -1982,7 +1967,7 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset,
|
||||
free (cff_subset->widths);
|
||||
fail3:
|
||||
if (cff_subset->font_name)
|
||||
free (cff_subset->font_name);
|
||||
free (cff_subset->font_name);
|
||||
fail2:
|
||||
free (cff_subset->ps_name);
|
||||
fail1:
|
||||
@@ -1996,7 +1981,7 @@ _cairo_cff_subset_fini (cairo_cff_subset_t *subset)
|
||||
{
|
||||
free (subset->ps_name);
|
||||
if (subset->font_name)
|
||||
free (subset->font_name);
|
||||
free (subset->font_name);
|
||||
free (subset->widths);
|
||||
free (subset->data);
|
||||
}
|
||||
@@ -2011,7 +1996,7 @@ _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset
|
||||
|
||||
font = calloc (1, sizeof (cairo_cff_font_t));
|
||||
if (unlikely (font == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
font->backend = NULL;
|
||||
font->scaled_font_subset = scaled_font_subset;
|
||||
@@ -2019,18 +2004,18 @@ _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset
|
||||
_cairo_array_init (&font->output, sizeof (char));
|
||||
status = _cairo_array_grow_by (&font->output, 4096);
|
||||
if (unlikely (status))
|
||||
goto fail1;
|
||||
goto fail1;
|
||||
|
||||
font->subset_font_name = strdup (subset_name);
|
||||
if (unlikely (font->subset_font_name == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail1;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
font->ps_name = strdup (subset_name);
|
||||
if (unlikely (font->ps_name == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail2;
|
||||
goto fail2;
|
||||
}
|
||||
font->font_name = NULL;
|
||||
|
||||
@@ -2053,11 +2038,11 @@ _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset
|
||||
|
||||
status = cff_dict_init (&font->top_dict);
|
||||
if (unlikely (status))
|
||||
goto fail4;
|
||||
goto fail4;
|
||||
|
||||
status = cff_dict_init (&font->private_dict);
|
||||
if (unlikely (status))
|
||||
goto fail5;
|
||||
goto fail5;
|
||||
|
||||
cff_index_init (&font->strings_index);
|
||||
cff_index_init (&font->charstrings_index);
|
||||
@@ -2083,7 +2068,7 @@ fail4:
|
||||
free (font->widths);
|
||||
fail3:
|
||||
if (font->font_name)
|
||||
free (font->font_name);
|
||||
free (font->font_name);
|
||||
free (font->ps_name);
|
||||
fail2:
|
||||
free (font->subset_font_name);
|
||||
@@ -2120,39 +2105,39 @@ cairo_cff_font_fallback_generate (cairo_cff_font_t *font,
|
||||
end_buf = encode_integer (end_buf, type2_subset->x_max);
|
||||
end_buf = encode_integer (end_buf, type2_subset->y_max);
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
FONTBBOX_OP, buf, end_buf - buf);
|
||||
FONTBBOX_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
end_buf = encode_integer_max (buf, 0);
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
CHARSTRINGS_OP, buf, end_buf - buf);
|
||||
CHARSTRINGS_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
FDSELECT_OP, buf, end_buf - buf);
|
||||
FDSELECT_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
FDARRAY_OP, buf, end_buf - buf);
|
||||
FDARRAY_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
CHARSET_OP, buf, end_buf - buf);
|
||||
CHARSET_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cairo_cff_font_set_ros_strings (font);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
/* Create CID FD dictionary */
|
||||
status = cairo_cff_font_create_cid_fontdict (font);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
/* Create charstrings */
|
||||
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
|
||||
@@ -2178,7 +2163,7 @@ cairo_cff_font_fallback_generate (cairo_cff_font_t *font,
|
||||
|
||||
cairo_status_t
|
||||
_cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset,
|
||||
const char *subset_name,
|
||||
const char *subset_name,
|
||||
cairo_scaled_font_subset_t *font_subset)
|
||||
{
|
||||
cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */
|
||||
@@ -2190,27 +2175,27 @@ _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset,
|
||||
|
||||
status = _cairo_cff_font_fallback_create (font_subset, &font, subset_name);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = _cairo_type2_charstrings_init (&type2_subset, font_subset);
|
||||
if (unlikely (status))
|
||||
goto fail1;
|
||||
goto fail1;
|
||||
|
||||
status = cairo_cff_font_fallback_generate (font, &type2_subset, &data, &length);
|
||||
if (unlikely (status))
|
||||
goto fail2;
|
||||
goto fail2;
|
||||
|
||||
cff_subset->font_name = NULL;
|
||||
cff_subset->ps_name = strdup (font->ps_name);
|
||||
if (unlikely (cff_subset->ps_name == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail2;
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
|
||||
if (unlikely (cff_subset->widths == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail3;
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
|
||||
@@ -2225,8 +2210,8 @@ _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset,
|
||||
|
||||
cff_subset->data = malloc (length);
|
||||
if (unlikely (cff_subset->data == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail4;
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
memcpy (cff_subset->data, data, length);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#ifndef CAIRO_DEPRECATED_H
|
||||
#define CAIRO_DEPRECATED_H
|
||||
|
||||
#define CAIRO_FONT_TYPE_ATSUI CAIRO_FONT_TYPE_QUARTZ
|
||||
|
||||
/* Obsolete functions. These definitions exist to coerce the compiler
|
||||
* into providing a little bit of guidance with its error
|
||||
* messages. The idea is to help users port their old code without
|
||||
@@ -57,6 +59,8 @@
|
||||
#define cairo_ps_surface_set_dpi cairo_ps_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution
|
||||
#define cairo_pdf_surface_set_dpi cairo_pdf_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution
|
||||
#define cairo_svg_surface_set_dpi cairo_svg_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution
|
||||
#define cairo_atsui_font_face_create_for_atsu_font_id cairo_atsui_font_face_create_for_atsu_font_id_REPLACED_BY_cairo_quartz_font_face_create_for_atsu_font_id
|
||||
|
||||
#define cairo_current_path cairo_current_path_DEPRECATED_BY_cairo_copy_path
|
||||
#define cairo_current_path_flat cairo_current_path_flat_DEPRECATED_BY_cairo_copy_path_flat
|
||||
#define cairo_get_path cairo_get_path_DEPRECATED_BY_cairo_copy_path
|
||||
|
||||
@@ -767,3 +767,45 @@ _cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font)
|
||||
|
||||
return ffont->ctFont;
|
||||
}
|
||||
|
||||
#if !defined(__LP64__) && !TARGET_OS_IPHONE
|
||||
/*
|
||||
* compat with old ATSUI backend
|
||||
*/
|
||||
|
||||
/**
|
||||
* cairo_quartz_font_face_create_for_atsu_font_id
|
||||
* @font_id: an ATSUFontID for the font.
|
||||
*
|
||||
* Creates a new font for the Quartz font backend based on an
|
||||
* #ATSUFontID. This font can then be used with
|
||||
* cairo_set_font_face() or cairo_scaled_font_create().
|
||||
*
|
||||
* Return value: a newly created #cairo_font_face_t. Free with
|
||||
* cairo_font_face_destroy() when you are done using it.
|
||||
*
|
||||
* Since: 1.6
|
||||
**/
|
||||
cairo_font_face_t *
|
||||
cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id)
|
||||
{
|
||||
ATSFontRef atsFont = FMGetATSFontRefFromFont (font_id);
|
||||
CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont);
|
||||
cairo_font_face_t *ff;
|
||||
|
||||
ff = cairo_quartz_font_face_create_for_cgfont (cgFont);
|
||||
|
||||
CGFontRelease (cgFont);
|
||||
|
||||
return ff;
|
||||
}
|
||||
|
||||
/* This is the old name for the above function, exported for compat purposes */
|
||||
cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id);
|
||||
|
||||
cairo_font_face_t *
|
||||
cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id)
|
||||
{
|
||||
return cairo_quartz_font_face_create_for_atsu_font_id (font_id);
|
||||
}
|
||||
#endif
|
||||
@@ -63,6 +63,11 @@ cairo_quartz_surface_get_image (cairo_surface_t *surface);
|
||||
cairo_public cairo_font_face_t *
|
||||
cairo_quartz_font_face_create_for_cgfont (CGFontRef font);
|
||||
|
||||
#if !defined(__LP64__) && !TARGET_OS_IPHONE
|
||||
cairo_public cairo_font_face_t *
|
||||
cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id);
|
||||
#endif
|
||||
|
||||
#endif /* CAIRO_HAS_QUARTZ_FONT */
|
||||
|
||||
CAIRO_END_DECLS
|
||||
|
||||
@@ -213,6 +213,8 @@
|
||||
#define cairo_qpainter_surface_create_with_qpixmap _moz_cairo_qpainter_surface_create_with_qpixmap
|
||||
#define cairo_qpainter_surface_get_image _moz_cairo_qpainter_surface_get_image
|
||||
#define cairo_qpainter_surface_get_qimage _moz_cairo_qpainter_surface_get_qimage
|
||||
#define cairo_qpainter_surface_get_qpainter _moz_cairo_qpainter_surface_get_qpainter
|
||||
#define cairo_quartz_font_face_create_for_atsu_font_id _moz_cairo_quartz_font_face_create_for_atsu_font_id
|
||||
#define cairo_quartz_font_face_create_for_cgfont _moz_cairo_quartz_font_face_create_for_cgfont
|
||||
#define cairo_quartz_image_surface_create _moz_cairo_quartz_image_surface_create
|
||||
#define cairo_quartz_image_surface_get_image _moz_cairo_quartz_image_surface_get_image
|
||||
|
||||
@@ -50,10 +50,6 @@
|
||||
#include "gfxWindowsPlatform.h"
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
#include "harfbuzz/hb.h"
|
||||
#include "harfbuzz/hb-ot.h"
|
||||
#include "graphite2/Font.h"
|
||||
@@ -888,7 +884,7 @@ gfxFont::GetGlyphHAdvance(DrawTarget* aDrawTarget, uint16_t aGID)
|
||||
if (!SetupCairoFont(aDrawTarget)) {
|
||||
return 0;
|
||||
}
|
||||
#if !defined(XP_MACOSX) || (defined(XP_MACOSX) && (defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6))
|
||||
#if !defined(XP_MACOSX) || (defined(XP_MACOSX) && !(defined(__ppc__) || defined(__ppc64__)))
|
||||
if (ProvidesGlyphWidths()) {
|
||||
return GetGlyphWidth(*aDrawTarget, aGID) / 65536.0;
|
||||
}
|
||||
|
||||
@@ -336,20 +336,7 @@ gfxGraphiteShaper::SetGlyphsFromSegment(DrawTarget *aDrawTarget,
|
||||
// not a one-to-one mapping with simple metrics: use DetailedGlyph
|
||||
AutoTArray<gfxShapedText::DetailedGlyph,8> details;
|
||||
float clusterLoc;
|
||||
|
||||
uint32_t glyph_start = c.baseGlyph;
|
||||
uint32_t glyph_end = glyph_start + c.nGlyphs;
|
||||
if (glyph_end < glyph_start) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
if (glyph_end - glyph_start > 0xFFFF) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
if (!details.SetCapacity(glyph_end - glyph_start, fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
for (uint32_t j = glyph_start; j < glyph_end; ++j) {
|
||||
for (uint32_t j = c.baseGlyph; j < c.baseGlyph + c.nGlyphs; ++j) {
|
||||
gfxShapedText::DetailedGlyph* d = details.AppendElement();
|
||||
d->mGlyphID = gids[j];
|
||||
d->mYOffset = roundY ? NSToIntRound(-yLocs[j]) * dev2appUnits :
|
||||
|
||||
@@ -19,10 +19,6 @@
|
||||
#include "unicode/unorm.h"
|
||||
#include "unicode/utext.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
static const UNormalizer2 * sNormalizer = nullptr;
|
||||
|
||||
#include <algorithm>
|
||||
@@ -331,7 +327,7 @@ gfxHarfBuzzShaper::GetGlyphHAdvance(hb_codepoint_t glyph) const
|
||||
glyph = mNumLongHMetrics - 1;
|
||||
}
|
||||
|
||||
#if defined(XP_MACOSX) && !(defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6))
|
||||
#if defined(XP_MACOSX) && (defined(__ppc__) || defined(__ppc64__))
|
||||
if (MOZ_UNLIKELY(mFont->mSpacingKludge && glyph == mFont->GetSpaceGlyph())) {
|
||||
// This is one of the fonts where the spacing went awry,
|
||||
// invariably on an nbsp character that escaped into the run.
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
|
||||
#include "cairo-quartz.h"
|
||||
|
||||
#include "nsCocoaFeatures.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
@@ -476,11 +474,25 @@ gfxMacFont::InitMetrics()
|
||||
// If we are dealing with a weird font (Apple is notorious), we could have
|
||||
// the situation where we wind up with space == maxAdvance due to a 10.4 bug.
|
||||
// In that case, kludge a reasonable upper limit (TenFourFox issue 355).
|
||||
//fprintf(stderr, "glyph: %i sB=%s width=%f avg=%f max=%f cgf=%f mAS=%f\n", glyphID, (IsSyntheticBold() ? "Y" : "n"), mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.maxAdvance, cgConvFactor, mAdjustedSize);
|
||||
if (MOZ_UNLIKELY(!hasX && mMetrics.spaceWidth >= mMetrics.maxAdvance)) {
|
||||
mMetrics.spaceWidth = std::min((float)mMetrics.emDescent * 1.4f,
|
||||
(float)mMetrics.maxAdvance * 0.4f);
|
||||
mSpacingKludge = true;
|
||||
}
|
||||
//fprintf(stderr, "glyph: %i sB=%s ......%f avg=%f max=%f cgf=%f mAS=%f\n", glyphID, (IsSyntheticBold() ? "Y" : "n"), mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.maxAdvance, cgConvFactor, mAdjustedSize);
|
||||
|
||||
|
||||
#if 0
|
||||
fprintf (stderr, "Font: %p (%s) size: %f\n", this,
|
||||
NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
|
||||
// fprintf (stderr, " fbounds.origin.x %f y %f size.width %f height %f\n", fbounds.origin.x, fbounds.origin.y, fbounds.size.width, fbounds.size.height);
|
||||
fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
|
||||
fprintf (stderr, " maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance);
|
||||
fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.internalLeading, mMetrics.externalLeading);
|
||||
fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f capHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight, mMetrics.capHeight);
|
||||
fprintf (stderr, " uOff: %f uSize: %f stOff: %f stSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
gfxFloat
|
||||
@@ -515,10 +527,7 @@ gfxMacFont::CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont,
|
||||
CGFloat aSize,
|
||||
CTFontDescriptorRef aFontDesc)
|
||||
{
|
||||
// Avoid calling potentially buggy variation APIs on pre-Sierra macOS
|
||||
// versions (see bug 1331683)
|
||||
CFDictionaryRef variations = nsCocoaFeatures::OnSierraOrLater() ?
|
||||
::CGFontCopyVariations(aCGFont) : 0;
|
||||
CFDictionaryRef variations = ::CGFontCopyVariations(aCGFont);
|
||||
CTFontRef ctFont;
|
||||
if (variations) {
|
||||
CFDictionaryRef varAttr =
|
||||
|
||||
@@ -42,10 +42,7 @@ public:
|
||||
// with embedded color bitmaps (Apple Color Emoji), as Core Text renders
|
||||
// the glyphs with non-linear scaling at small pixel sizes.
|
||||
virtual bool ProvidesGlyphWidths() const override {
|
||||
return
|
||||
#if defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
|
||||
mVariationFont ||
|
||||
#endif
|
||||
return mVariationFont ||
|
||||
mFontEntry->HasFontTable(TRUETYPE_TAG('s','b','i','x'));
|
||||
}
|
||||
|
||||
|
||||
+47
-27
@@ -147,6 +147,7 @@ gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
|
||||
: gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
|
||||
, mUserData(aParams->mUserData)
|
||||
, mFontGroup(aFontGroup)
|
||||
, mReleasedFontGroup(false)
|
||||
, mShapingState(eShapingState_Normal)
|
||||
{
|
||||
NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
|
||||
@@ -183,18 +184,30 @@ gfxTextRun::~gfxTextRun()
|
||||
mFlags = 0xFFFFFFFF;
|
||||
#endif
|
||||
|
||||
// The cached ellipsis textrun (if any) in a fontgroup will have already
|
||||
// been told to release its reference to the group, so we mustn't do that
|
||||
// again here.
|
||||
if (!mReleasedFontGroup) {
|
||||
#ifdef DEBUG
|
||||
gfxTextPerfMetrics* tp = mFontGroup->GetTextPerfMetrics();
|
||||
if (tp) {
|
||||
tp->current.textrunDestr++;
|
||||
}
|
||||
gfxTextPerfMetrics *tp = mFontGroup->GetTextPerfMetrics();
|
||||
if (tp) {
|
||||
tp->current.textrunDestr++;
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_RELEASE(mFontGroup);
|
||||
NS_RELEASE(mFontGroup);
|
||||
}
|
||||
|
||||
MOZ_COUNT_DTOR(gfxTextRun);
|
||||
}
|
||||
|
||||
void
|
||||
gfxTextRun::ReleaseFontGroup()
|
||||
{
|
||||
NS_ASSERTION(!mReleasedFontGroup, "doubly released!");
|
||||
NS_RELEASE(mFontGroup);
|
||||
mReleasedFontGroup = true;
|
||||
}
|
||||
|
||||
bool
|
||||
gfxTextRun::SetPotentialLineBreaks(Range aRange, const uint8_t* aBreakBefore)
|
||||
{
|
||||
@@ -231,13 +244,11 @@ gfxTextRun::ComputeLigatureData(Range aPartRange,
|
||||
LigatureData result;
|
||||
const CompressedGlyph *charGlyphs = mCharacterGlyphs;
|
||||
|
||||
uint32_t i = aPartRange.start;
|
||||
while (i && !charGlyphs[i].IsLigatureGroupStart()) {
|
||||
--i;
|
||||
uint32_t i;
|
||||
for (i = aPartRange.start; !charGlyphs[i].IsLigatureGroupStart(); --i) {
|
||||
NS_ASSERTION(i > 0, "Ligature at the start of the run??");
|
||||
}
|
||||
NS_ASSERTION(charGlyphs[i].IsLigatureGroupStart(), "Ligature at run start?");
|
||||
result.mRange.start = i;
|
||||
|
||||
for (i = aPartRange.start + 1;
|
||||
i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
|
||||
}
|
||||
@@ -1706,27 +1717,21 @@ gfxFontGroup::GetFontAt(int32_t i, uint32_t aCh)
|
||||
|
||||
RefPtr<gfxFont> font = ff.Font();
|
||||
if (!font) {
|
||||
RefPtr<gfxFontEntry> fe = ff.FontEntry();
|
||||
if (!fe) {
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<gfxCharacterMap> unicodeRangeMap;
|
||||
gfxFontEntry* fe = mFonts[i].FontEntry();
|
||||
gfxCharacterMap* unicodeRangeMap = nullptr;
|
||||
if (fe->mIsUserFontContainer) {
|
||||
// This raw pointer is OK because fe holds a strong ref to the object.
|
||||
gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe.get());
|
||||
gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
|
||||
if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
|
||||
ufe->CharacterInUnicodeRange(aCh) &&
|
||||
!FontLoadingForFamily(ff.Family(), aCh)) {
|
||||
ufe->Load();
|
||||
ff.CheckState(mSkipDrawing);
|
||||
}
|
||||
unicodeRangeMap = ufe->GetUnicodeRangeMap();
|
||||
// Update fe to refer to the actual platform font entry, rather than the
|
||||
// webfont wrapper. After this, we no longer have a strong ref to ufe.
|
||||
fe = ufe->GetPlatformFontEntry();
|
||||
if (!fe) {
|
||||
return nullptr;
|
||||
}
|
||||
unicodeRangeMap = ufe->GetUnicodeRangeMap();
|
||||
}
|
||||
font = fe->FindOrMakeFont(&mStyle, mFonts[i].NeedsBold(),
|
||||
unicodeRangeMap);
|
||||
@@ -2536,11 +2541,17 @@ gfxFontGroup::InitScriptRun(DrawTarget* aDrawTarget,
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<gfxTextRun>
|
||||
gfxFontGroup::MakeEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
|
||||
DrawTarget* aRefDrawTarget) {
|
||||
gfxTextRun *
|
||||
gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
|
||||
LazyReferenceDrawTargetGetter& aRefDrawTargetGetter)
|
||||
{
|
||||
MOZ_ASSERT(!(aFlags & ~TEXT_ORIENT_MASK),
|
||||
"flags here should only be used to specify orientation");
|
||||
if (mCachedEllipsisTextRun &&
|
||||
(mCachedEllipsisTextRun->GetFlags() & TEXT_ORIENT_MASK) == aFlags &&
|
||||
mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
|
||||
return mCachedEllipsisTextRun.get();
|
||||
}
|
||||
|
||||
// Use a Unicode ellipsis if the font supports it,
|
||||
// otherwise use three ASCII periods as fallback.
|
||||
@@ -2551,11 +2562,20 @@ gfxFontGroup::MakeEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
|
||||
: nsDependentString(kASCIIPeriodsChar,
|
||||
ArrayLength(kASCIIPeriodsChar) - 1);
|
||||
|
||||
RefPtr<DrawTarget> refDT = aRefDrawTargetGetter.GetRefDrawTarget();
|
||||
Parameters params = {
|
||||
aRefDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
|
||||
refDT, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
|
||||
};
|
||||
return MakeTextRun(ellipsis.get(), ellipsis.Length(), ¶ms,
|
||||
aFlags | TEXT_IS_PERSISTENT, nullptr);
|
||||
mCachedEllipsisTextRun =
|
||||
MakeTextRun(ellipsis.get(), ellipsis.Length(), ¶ms,
|
||||
aFlags | TEXT_IS_PERSISTENT, nullptr);
|
||||
if (!mCachedEllipsisTextRun) {
|
||||
return nullptr;
|
||||
}
|
||||
// don't let the presence of a cached ellipsis textrun prolong the
|
||||
// fontgroup's life
|
||||
mCachedEllipsisTextRun->ReleaseFontGroup();
|
||||
return mCachedEllipsisTextRun.get();
|
||||
}
|
||||
|
||||
already_AddRefed<gfxFont>
|
||||
|
||||
+18
-5
@@ -755,6 +755,8 @@ private:
|
||||
bool mSkipDrawing; // true if the font group we used had a user font
|
||||
// download that's in progress, so we should hide text
|
||||
// until the download completes (or timeout fires)
|
||||
bool mReleasedFontGroup; // we already called NS_RELEASE on
|
||||
// mFontGroup, so don't do it again
|
||||
|
||||
// shaping state for handling variant fallback features
|
||||
// such as subscript/superscript variant glyphs
|
||||
@@ -895,6 +897,7 @@ public:
|
||||
mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
|
||||
mSkipDrawing = false;
|
||||
mHyphenWidth = -1;
|
||||
mCachedEllipsisTextRun = nullptr;
|
||||
}
|
||||
|
||||
// If there is a user font set, check to see whether the font list or any
|
||||
@@ -908,11 +911,17 @@ public:
|
||||
return mSkipDrawing;
|
||||
}
|
||||
|
||||
// Make a textrun for the ellipsis character (with fallback to "..." if
|
||||
// ellipsis is not supported by the font).
|
||||
already_AddRefed<gfxTextRun> MakeEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
|
||||
uint32_t aFlags,
|
||||
DrawTarget* aRefDrawTarget);
|
||||
class LazyReferenceDrawTargetGetter {
|
||||
public:
|
||||
virtual already_AddRefed<DrawTarget> GetRefDrawTarget() = 0;
|
||||
};
|
||||
// The gfxFontGroup keeps ownership of this textrun.
|
||||
// It is only guaranteed to exist until the next call to GetEllipsisTextRun
|
||||
// (which might use a different appUnitsPerDev value or flags) for the font
|
||||
// group, or until UpdateUserFonts is called, or the fontgroup is destroyed.
|
||||
// Get it/use it/forget it :) - don't keep a reference that might go stale.
|
||||
gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
|
||||
LazyReferenceDrawTargetGetter& aRefDrawTargetGetter);
|
||||
|
||||
protected:
|
||||
// search through pref fonts for a character, return nullptr if no matching pref font
|
||||
@@ -1084,6 +1093,10 @@ protected:
|
||||
|
||||
gfxTextPerfMetrics *mTextPerf;
|
||||
|
||||
// Cache a textrun representing an ellipsis (useful for CSS text-overflow)
|
||||
// at a specific appUnitsPerDevPixel size and orientation
|
||||
RefPtr<gfxTextRun> mCachedEllipsisTextRun;
|
||||
|
||||
// cache the most recent pref font to avoid general pref font lookup
|
||||
RefPtr<gfxFontFamily> mLastPrefFamily;
|
||||
RefPtr<gfxFont> mLastPrefFont;
|
||||
|
||||
@@ -1637,13 +1637,10 @@ RasterImage::NotifyDecodeComplete(const DecoderFinalStatus& aStatus,
|
||||
|
||||
if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY) &&
|
||||
mHasBeenDecoded && mAnimationState) {
|
||||
// If we've successfully finished a full decode of all animation frames
|
||||
// then let our animation state know it's complete and to not to expect
|
||||
// any more frames.
|
||||
if (aStatus.mFinished && !aStatus.mHadError) {
|
||||
mAnimationState->NotifyDecodeComplete();
|
||||
mAnimationState->UpdateState(mAnimationFinished, this, mSize);
|
||||
}
|
||||
// We've finished a full decode of all animation frames and our AnimationState
|
||||
// has been notified about them all, so let it know not to expect anymore.
|
||||
mAnimationState->NotifyDecodeComplete();
|
||||
mAnimationState->UpdateState(mAnimationFinished, this, mSize);
|
||||
}
|
||||
|
||||
// Only act on errors if we have no usable frames from the decoder.
|
||||
|
||||
@@ -446,7 +446,7 @@ for (k = 0; k < 2; k++) {
|
||||
while ((c = fgetc(f)) != '\n' && c != EOF);
|
||||
/* issue warning if not a comment */
|
||||
if (buf[0] != '%') {
|
||||
fprintf(stderr, "Warning: skipping too long pattern (more than %zu chars)\n", sizeof(buf));
|
||||
fprintf(stderr, "Warning: skipping too long pattern (more than %lu chars)\n", sizeof(buf));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -47,12 +47,10 @@
|
||||
#include "builtin/AtomicsObject.h"
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#include "builtin/Promise.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "jsnum.h"
|
||||
@@ -60,9 +58,7 @@
|
||||
#include "jit/AtomicOperations.h"
|
||||
#include "jit/InlinableNatives.h"
|
||||
#include "js/Class.h"
|
||||
#include "vm/BigIntType.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/HelperThreads.h"
|
||||
#include "vm/Time.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
@@ -125,68 +121,6 @@ GetTypedArrayIndex(JSContext* cx, HandleValue v, Handle<TypedArrayObject*> view,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsWaitableTypedArray(Scalar::Type viewType)
|
||||
{
|
||||
return viewType == Scalar::Int32 || viewType == Scalar::BigInt64;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
GetWaiterByteOffset(Handle<TypedArrayObject*> view, uint32_t offset)
|
||||
{
|
||||
return view->byteOffset() + offset * TypedArrayElemSize(view->type());
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
BigIntToRawBits(Scalar::Type viewType, BigInt* value)
|
||||
{
|
||||
if (viewType == Scalar::BigInt64)
|
||||
return uint64_t(BigInt::toInt64(value));
|
||||
MOZ_ASSERT(viewType == Scalar::BigUint64);
|
||||
return BigInt::toUint64(value);
|
||||
}
|
||||
|
||||
static bool
|
||||
BigIntToRawBits(JSContext* cx, Scalar::Type viewType, HandleValue value, uint64_t* bits)
|
||||
{
|
||||
MOZ_ASSERT(Scalar::isBigIntType(viewType));
|
||||
|
||||
RootedBigInt bigint(cx, ToBigInt(cx, value));
|
||||
if (!bigint)
|
||||
return false;
|
||||
|
||||
*bits = BigIntToRawBits(viewType, bigint);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetBigIntResult(JSContext* cx, Scalar::Type viewType, uint64_t bits, MutableHandleValue result)
|
||||
{
|
||||
MOZ_ASSERT(Scalar::isBigIntType(viewType));
|
||||
|
||||
BigInt* bigint = viewType == Scalar::BigInt64
|
||||
? BigInt::createFromInt64(cx, mozilla::BitwiseCast<int64_t>(bits))
|
||||
: BigInt::createFromUint64(cx, bits);
|
||||
if (!bigint)
|
||||
return false;
|
||||
|
||||
result.setBigInt(bigint);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
WaitValueMatches(Handle<TypedArrayObject*> view, uint32_t offset, uint64_t expected)
|
||||
{
|
||||
SharedMem<void*> viewData = view->viewDataShared();
|
||||
if (view->type() == Scalar::BigInt64) {
|
||||
return jit::AtomicOperations::loadSafeWhenRacy(viewData.cast<uint64_t*>() + offset) ==
|
||||
expected;
|
||||
}
|
||||
|
||||
return uint32_t(jit::AtomicOperations::loadSafeWhenRacy(viewData.cast<int32_t*>() + offset)) ==
|
||||
uint32_t(expected);
|
||||
}
|
||||
|
||||
static int32_t
|
||||
CompareExchange(Scalar::Type viewType, int32_t oldCandidate, int32_t newCandidate,
|
||||
SharedMem<void*> viewData, uint32_t offset, bool* badArrayType = nullptr)
|
||||
@@ -257,20 +191,6 @@ js::atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp)
|
||||
uint32_t offset;
|
||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
|
||||
if (Scalar::isBigIntType(view->type())) {
|
||||
uint64_t oldCandidate;
|
||||
if (!BigIntToRawBits(cx, view->type(), oldv, &oldCandidate))
|
||||
return false;
|
||||
uint64_t newCandidate;
|
||||
if (!BigIntToRawBits(cx, view->type(), newv, &newCandidate))
|
||||
return false;
|
||||
|
||||
uint64_t result = jit::AtomicOperations::compareExchangeSeqCst(
|
||||
view->viewDataShared().cast<uint64_t*>() + offset, oldCandidate, newCandidate);
|
||||
return SetBigIntResult(cx, view->type(), result, r);
|
||||
}
|
||||
|
||||
int32_t oldCandidate;
|
||||
if (!ToInt32(cx, oldv, &oldCandidate))
|
||||
return false;
|
||||
@@ -339,22 +259,6 @@ js::atomics_load(JSContext* cx, unsigned argc, Value* vp)
|
||||
r.setNumber(v);
|
||||
return true;
|
||||
}
|
||||
case Scalar::BigInt64: {
|
||||
int64_t v = jit::AtomicOperations::loadSeqCst(viewData.cast<int64_t*>() + offset);
|
||||
BigInt* bigint = BigInt::createFromInt64(cx, v);
|
||||
if (!bigint)
|
||||
return false;
|
||||
r.setBigInt(bigint);
|
||||
return true;
|
||||
}
|
||||
case Scalar::BigUint64: {
|
||||
uint64_t v = jit::AtomicOperations::loadSeqCst(viewData.cast<uint64_t*>() + offset);
|
||||
BigInt* bigint = BigInt::createFromUint64(cx, v);
|
||||
if (!bigint)
|
||||
return false;
|
||||
r.setBigInt(bigint);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return ReportBadArrayType(cx);
|
||||
}
|
||||
@@ -433,25 +337,6 @@ ExchangeOrStore(JSContext* cx, unsigned argc, Value* vp)
|
||||
uint32_t offset;
|
||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
|
||||
if (Scalar::isBigIntType(view->type())) {
|
||||
RootedBigInt bigint(cx, ToBigInt(cx, valv));
|
||||
if (!bigint)
|
||||
return false;
|
||||
|
||||
uint64_t value = BigIntToRawBits(view->type(), bigint);
|
||||
if (op == DoStore) {
|
||||
jit::AtomicOperations::storeSeqCst(view->viewDataShared().cast<uint64_t*>() + offset,
|
||||
value);
|
||||
r.setBigInt(bigint);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t result = jit::AtomicOperations::exchangeSeqCst(
|
||||
view->viewDataShared().cast<uint64_t*>() + offset, value);
|
||||
return SetBigIntResult(cx, view->type(), result, r);
|
||||
}
|
||||
|
||||
double integerValue;
|
||||
if (!ToInteger(cx, valv, &integerValue))
|
||||
return false;
|
||||
@@ -495,24 +380,6 @@ AtomicsBinop(JSContext* cx, HandleValue objv, HandleValue idxv, HandleValue valv
|
||||
uint32_t offset;
|
||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
|
||||
if (Scalar::isBigIntType(view->type())) {
|
||||
uint64_t value;
|
||||
if (!BigIntToRawBits(cx, view->type(), valv, &value))
|
||||
return false;
|
||||
|
||||
SharedMem<uint64_t*> addr = view->viewDataShared().cast<uint64_t*>() + offset;
|
||||
uint64_t old = jit::AtomicOperations::loadSeqCst(addr);
|
||||
for (;;) {
|
||||
uint64_t replacement = T::operate64(old, value);
|
||||
uint64_t observed = jit::AtomicOperations::compareExchangeSeqCst(addr, old,
|
||||
replacement);
|
||||
if (observed == old)
|
||||
return SetBigIntResult(cx, view->type(), old, r);
|
||||
old = observed;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t numberValue;
|
||||
if (!ToInt32(cx, valv, &numberValue))
|
||||
return false;
|
||||
@@ -567,7 +434,6 @@ class PerformAdd
|
||||
public:
|
||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchAddSeqCst)
|
||||
static int32_t perform(int32_t x, int32_t y) { return x + y; }
|
||||
static uint64_t operate64(uint64_t x, uint64_t y) { return x + y; }
|
||||
};
|
||||
|
||||
bool
|
||||
@@ -582,7 +448,6 @@ class PerformSub
|
||||
public:
|
||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchSubSeqCst)
|
||||
static int32_t perform(int32_t x, int32_t y) { return x - y; }
|
||||
static uint64_t operate64(uint64_t x, uint64_t y) { return x - y; }
|
||||
};
|
||||
|
||||
bool
|
||||
@@ -597,7 +462,6 @@ class PerformAnd
|
||||
public:
|
||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchAndSeqCst)
|
||||
static int32_t perform(int32_t x, int32_t y) { return x & y; }
|
||||
static uint64_t operate64(uint64_t x, uint64_t y) { return x & y; }
|
||||
};
|
||||
|
||||
bool
|
||||
@@ -612,7 +476,6 @@ class PerformOr
|
||||
public:
|
||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchOrSeqCst)
|
||||
static int32_t perform(int32_t x, int32_t y) { return x | y; }
|
||||
static uint64_t operate64(uint64_t x, uint64_t y) { return x | y; }
|
||||
};
|
||||
|
||||
bool
|
||||
@@ -627,7 +490,6 @@ class PerformXor
|
||||
public:
|
||||
INTEGRAL_TYPES_FOR_EACH(jit::AtomicOperations::fetchXorSeqCst)
|
||||
static int32_t perform(int32_t x, int32_t y) { return x ^ y; }
|
||||
static uint64_t operate64(uint64_t x, uint64_t y) { return x ^ y; }
|
||||
};
|
||||
|
||||
bool
|
||||
@@ -831,13 +693,11 @@ js::atomics_cmpxchg_asm_callout(wasm::Instance* instance, int32_t vt, int32_t of
|
||||
|
||||
namespace js {
|
||||
|
||||
class AtomicsWaitAsyncTask;
|
||||
|
||||
// Represents one waiting worker.
|
||||
//
|
||||
// The type is declared opaque in SharedArrayObject.h. Instances of
|
||||
// js::FutexWaiter are linked onto a list across a call to FutexRuntime::wait()
|
||||
// or an async wait task.
|
||||
// js::FutexWaiter are stack-allocated and linked onto a list across a
|
||||
// call to FutexRuntime::wait().
|
||||
//
|
||||
// The 'waiters' field of the SharedArrayRawBuffer points to the highest
|
||||
// priority waiter in the list, and lower priority nodes are linked through
|
||||
@@ -851,34 +711,14 @@ class FutexWaiter
|
||||
public:
|
||||
FutexWaiter(uint32_t offset, JSRuntime* rt)
|
||||
: offset(offset),
|
||||
kind(Sync),
|
||||
rt(rt),
|
||||
asyncTask(nullptr),
|
||||
lower_pri(nullptr),
|
||||
back(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
FutexWaiter(uint32_t offset, AtomicsWaitAsyncTask* asyncTask)
|
||||
: offset(offset),
|
||||
kind(Async),
|
||||
rt(nullptr),
|
||||
asyncTask(asyncTask),
|
||||
lower_pri(nullptr),
|
||||
back(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
bool isWaiting() const;
|
||||
void notify();
|
||||
|
||||
uint32_t offset; // byte offset within the SharedArrayBuffer
|
||||
enum WaiterKind {
|
||||
Sync,
|
||||
Async
|
||||
} kind;
|
||||
uint32_t offset; // int32 element index within the SharedArrayBuffer
|
||||
JSRuntime* rt; // The runtime of the waiter
|
||||
AtomicsWaitAsyncTask* asyncTask; // The async waiter task, if any
|
||||
FutexWaiter* lower_pri; // Lower priority nodes in circular doubly-linked list of waiters
|
||||
FutexWaiter* back; // Other direction
|
||||
};
|
||||
@@ -902,195 +742,8 @@ class AutoLockFutexAPI
|
||||
js::UniqueLock<js::Mutex>& unique() { return *unique_; }
|
||||
};
|
||||
|
||||
static void
|
||||
AddWaiter(SharedArrayRawBuffer* sarb, FutexWaiter* waiter)
|
||||
{
|
||||
if (FutexWaiter* waiters = sarb->waiters()) {
|
||||
waiter->lower_pri = waiters;
|
||||
waiter->back = waiters->back;
|
||||
waiters->back->lower_pri = waiter;
|
||||
waiters->back = waiter;
|
||||
} else {
|
||||
waiter->lower_pri = waiter->back = waiter;
|
||||
sarb->setWaiters(waiter);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
RemoveWaiter(SharedArrayRawBuffer* sarb, FutexWaiter* waiter)
|
||||
{
|
||||
if (waiter->lower_pri == waiter) {
|
||||
sarb->setWaiters(nullptr);
|
||||
} else {
|
||||
waiter->lower_pri->back = waiter->back;
|
||||
waiter->back->lower_pri = waiter->lower_pri;
|
||||
if (sarb->waiters() == waiter)
|
||||
sarb->setWaiters(waiter->lower_pri);
|
||||
}
|
||||
waiter->lower_pri = nullptr;
|
||||
waiter->back = nullptr;
|
||||
}
|
||||
|
||||
class AtomicsWaitAsyncTask : public PromiseTask
|
||||
{
|
||||
enum class State {
|
||||
Waiting,
|
||||
Notified,
|
||||
TimedOut
|
||||
};
|
||||
|
||||
SharedArrayRawBuffer* sarb_;
|
||||
FutexWaiter waiter_;
|
||||
mozilla::Maybe<mozilla::TimeDuration> timeout_;
|
||||
ConditionVariable cond_;
|
||||
State state_;
|
||||
bool isInWaiterList_;
|
||||
|
||||
public:
|
||||
AtomicsWaitAsyncTask(JSContext* cx, Handle<PromiseObject*> promise,
|
||||
SharedArrayRawBuffer* sarb, uint32_t offset,
|
||||
mozilla::Maybe<mozilla::TimeDuration>& timeout)
|
||||
: PromiseTask(cx, promise),
|
||||
sarb_(sarb),
|
||||
waiter_(offset, this),
|
||||
timeout_(timeout),
|
||||
state_(State::Waiting),
|
||||
isInWaiterList_(false)
|
||||
{
|
||||
}
|
||||
|
||||
~AtomicsWaitAsyncTask() {
|
||||
if (isInWaiterList_) {
|
||||
AutoLockFutexAPI lock;
|
||||
if (isInWaiterList_)
|
||||
removeFromWaiterList();
|
||||
}
|
||||
sarb_->dropReference();
|
||||
}
|
||||
|
||||
FutexWaiter* waiter() {
|
||||
return &waiter_;
|
||||
}
|
||||
|
||||
void setInWaiterList() {
|
||||
isInWaiterList_ = true;
|
||||
}
|
||||
|
||||
bool isWaiting() const {
|
||||
return state_ == State::Waiting;
|
||||
}
|
||||
|
||||
void notify() {
|
||||
MOZ_ASSERT(isWaiting());
|
||||
state_ = State::Notified;
|
||||
cond_.notify_all();
|
||||
}
|
||||
|
||||
void execute() override {
|
||||
AutoLockFutexAPI lock;
|
||||
|
||||
const bool isTimed = timeout_.isSome();
|
||||
auto finalEnd = timeout_.map([](mozilla::TimeDuration& timeout) {
|
||||
return mozilla::TimeStamp::Now() + timeout;
|
||||
});
|
||||
auto maxSlice = mozilla::TimeDuration::FromSeconds(4000.0);
|
||||
|
||||
while (state_ == State::Waiting) {
|
||||
if (isTimed) {
|
||||
auto sliceEnd = finalEnd.map([&](mozilla::TimeStamp& finalEnd) {
|
||||
auto sliceEnd = mozilla::TimeStamp::Now() + maxSlice;
|
||||
if (finalEnd < sliceEnd)
|
||||
sliceEnd = finalEnd;
|
||||
return sliceEnd;
|
||||
});
|
||||
|
||||
mozilla::Unused << cond_.wait_until(lock.unique(), *sliceEnd);
|
||||
if (state_ == State::Waiting && mozilla::TimeStamp::Now() >= *finalEnd) {
|
||||
state_ = State::TimedOut;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cond_.wait(lock.unique());
|
||||
}
|
||||
}
|
||||
|
||||
if (isInWaiterList_)
|
||||
removeFromWaiterList();
|
||||
}
|
||||
|
||||
private:
|
||||
void removeFromWaiterList() {
|
||||
MOZ_ASSERT(isInWaiterList_);
|
||||
RemoveWaiter(sarb_, &waiter_);
|
||||
isInWaiterList_ = false;
|
||||
}
|
||||
|
||||
bool finishPromise(JSContext* cx, Handle<PromiseObject*> promise) override {
|
||||
RootedValue result(cx);
|
||||
if (state_ == State::TimedOut)
|
||||
result.setString(cx->names().futexTimedOut);
|
||||
else
|
||||
result.setString(cx->names().futexOK);
|
||||
return PromiseObject::resolve(cx, promise, result);
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
FutexWaiter::isWaiting() const
|
||||
{
|
||||
if (kind == Sync)
|
||||
return rt->fx.isWaiting();
|
||||
return asyncTask->isWaiting();
|
||||
}
|
||||
|
||||
void
|
||||
FutexWaiter::notify()
|
||||
{
|
||||
if (kind == Sync) {
|
||||
rt->fx.notify(FutexRuntime::NotifyExplicit);
|
||||
return;
|
||||
}
|
||||
asyncTask->notify();
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
static bool
|
||||
GetWaitTimeout(JSContext* cx, HandleValue timeoutv,
|
||||
mozilla::Maybe<mozilla::TimeDuration>* timeout)
|
||||
{
|
||||
timeout->reset();
|
||||
if (!timeoutv.isUndefined()) {
|
||||
double timeout_ms;
|
||||
if (!ToNumber(cx, timeoutv, &timeout_ms))
|
||||
return false;
|
||||
if (!mozilla::IsNaN(timeout_ms)) {
|
||||
if (timeout_ms < 0)
|
||||
*timeout = mozilla::Some(mozilla::TimeDuration::FromSeconds(0.0));
|
||||
else if (!mozilla::IsInfinite(timeout_ms))
|
||||
*timeout = mozilla::Some(mozilla::TimeDuration::FromMilliseconds(timeout_ms));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CreateWaitAsyncResult(JSContext* cx, bool isAsync, HandleValue value, MutableHandleValue rval)
|
||||
{
|
||||
RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
RootedValue asyncValue(cx, BooleanValue(isAsync));
|
||||
if (!NativeDefineDataProperty(cx, obj, cx->names().async, asyncValue, JSPROP_ENUMERATE))
|
||||
return false;
|
||||
if (!NativeDefineDataProperty(cx, obj, cx->names().value, value, JSPROP_ENUMERATE))
|
||||
return false;
|
||||
|
||||
rval.setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@@ -1106,24 +759,26 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||
Rooted<TypedArrayObject*> view(cx, nullptr);
|
||||
if (!GetSharedTypedArray(cx, objv, &view))
|
||||
return false;
|
||||
if (!IsWaitableTypedArray(view->type()))
|
||||
if (view->type() != Scalar::Int32)
|
||||
return ReportBadArrayType(cx);
|
||||
uint32_t offset;
|
||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
uint64_t value = 0;
|
||||
if (view->type() == Scalar::BigInt64) {
|
||||
if (!BigIntToRawBits(cx, view->type(), valv, &value))
|
||||
return false;
|
||||
} else {
|
||||
int32_t int32Value;
|
||||
if (!ToInt32(cx, valv, &int32Value))
|
||||
return false;
|
||||
value = uint32_t(int32Value);
|
||||
}
|
||||
mozilla::Maybe<mozilla::TimeDuration> timeout;
|
||||
if (!GetWaitTimeout(cx, timeoutv, &timeout))
|
||||
int32_t value;
|
||||
if (!ToInt32(cx, valv, &value))
|
||||
return false;
|
||||
mozilla::Maybe<mozilla::TimeDuration> timeout;
|
||||
if (!timeoutv.isUndefined()) {
|
||||
double timeout_ms;
|
||||
if (!ToNumber(cx, timeoutv, &timeout_ms))
|
||||
return false;
|
||||
if (!mozilla::IsNaN(timeout_ms)) {
|
||||
if (timeout_ms < 0)
|
||||
timeout = mozilla::Some(mozilla::TimeDuration::FromSeconds(0.0));
|
||||
else if (!mozilla::IsInfinite(timeout_ms))
|
||||
timeout = mozilla::Some(mozilla::TimeDuration::FromMilliseconds(timeout_ms));
|
||||
}
|
||||
}
|
||||
|
||||
if (!rt->fx.canWait())
|
||||
return ReportCannotWait(cx);
|
||||
@@ -1132,7 +787,8 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||
// and it provides the necessary memory fence.
|
||||
AutoLockFutexAPI lock;
|
||||
|
||||
if (!WaitValueMatches(view, offset, value)) {
|
||||
SharedMem<int32_t*> addr = view->viewDataShared().cast<int32_t*>() + offset;
|
||||
if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value) {
|
||||
r.setString(cx->names().futexNotEqual);
|
||||
return true;
|
||||
}
|
||||
@@ -1140,8 +796,16 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||
Rooted<SharedArrayBufferObject*> sab(cx, view->bufferShared());
|
||||
SharedArrayRawBuffer* sarb = sab->rawBufferObject();
|
||||
|
||||
FutexWaiter w(GetWaiterByteOffset(view, offset), rt);
|
||||
AddWaiter(sarb, &w);
|
||||
FutexWaiter w(offset, rt);
|
||||
if (FutexWaiter* waiters = sarb->waiters()) {
|
||||
w.lower_pri = waiters;
|
||||
w.back = waiters->back;
|
||||
waiters->back->lower_pri = &w;
|
||||
waiters->back = &w;
|
||||
} else {
|
||||
w.lower_pri = w.back = &w;
|
||||
sarb->setWaiters(&w);
|
||||
}
|
||||
|
||||
FutexRuntime::WaitResult result = FutexRuntime::FutexOK;
|
||||
bool retval = rt->fx.wait(cx, lock.unique(), timeout, &result);
|
||||
@@ -1156,96 +820,15 @@ js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
|
||||
}
|
||||
}
|
||||
|
||||
RemoveWaiter(sarb, &w);
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool
|
||||
js::atomics_waitAsync(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
HandleValue objv = args.get(0);
|
||||
HandleValue idxv = args.get(1);
|
||||
HandleValue valv = args.get(2);
|
||||
HandleValue timeoutv = args.get(3);
|
||||
MutableHandleValue r = args.rval();
|
||||
|
||||
Rooted<TypedArrayObject*> view(cx, nullptr);
|
||||
if (!GetSharedTypedArray(cx, objv, &view))
|
||||
return false;
|
||||
if (!IsWaitableTypedArray(view->type()))
|
||||
return ReportBadArrayType(cx);
|
||||
uint32_t offset;
|
||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
uint64_t value = 0;
|
||||
if (view->type() == Scalar::BigInt64) {
|
||||
if (!BigIntToRawBits(cx, view->type(), valv, &value))
|
||||
return false;
|
||||
if (w.lower_pri == &w) {
|
||||
sarb->setWaiters(nullptr);
|
||||
} else {
|
||||
int32_t int32Value;
|
||||
if (!ToInt32(cx, valv, &int32Value))
|
||||
return false;
|
||||
value = uint32_t(int32Value);
|
||||
w.lower_pri->back = w.back;
|
||||
w.back->lower_pri = w.lower_pri;
|
||||
if (sarb->waiters() == &w)
|
||||
sarb->setWaiters(w.lower_pri);
|
||||
}
|
||||
mozilla::Maybe<mozilla::TimeDuration> timeout;
|
||||
if (!GetWaitTimeout(cx, timeoutv, &timeout))
|
||||
return false;
|
||||
|
||||
Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
|
||||
if (!promise)
|
||||
return false;
|
||||
|
||||
RootedValue promiseValue(cx, ObjectValue(*promise));
|
||||
RootedValue result(cx);
|
||||
if (!CreateWaitAsyncResult(cx, true, promiseValue, &result))
|
||||
return false;
|
||||
|
||||
Rooted<SharedArrayBufferObject*> sab(cx, view->bufferShared());
|
||||
SharedArrayRawBuffer* sarb = sab->rawBufferObject();
|
||||
if (!sarb->addReference()) {
|
||||
JS_ReportErrorASCII(cx, "Reference count overflow on SharedArrayBuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto task = cx->make_unique<AtomicsWaitAsyncTask>(cx, promise, sarb, offset, timeout);
|
||||
if (!task) {
|
||||
sarb->dropReference();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isAsync = false;
|
||||
RootedValue immediateResult(cx);
|
||||
{
|
||||
AutoLockFutexAPI lock;
|
||||
|
||||
if (!WaitValueMatches(view, offset, value)) {
|
||||
immediateResult.setString(cx->names().futexNotEqual);
|
||||
} else if (timeout.isSome() && timeout->ToMilliseconds() == 0.0) {
|
||||
immediateResult.setString(cx->names().futexTimedOut);
|
||||
} else {
|
||||
if (!cx->startAsyncTaskCallback || !cx->finishAsyncTaskCallback ||
|
||||
!CanUseExtraThreads())
|
||||
{
|
||||
JS_ReportErrorASCII(cx, "Atomics.waitAsync not supported in this runtime.");
|
||||
return false;
|
||||
}
|
||||
|
||||
task->waiter()->offset = GetWaiterByteOffset(view, offset);
|
||||
AddWaiter(sarb, task->waiter());
|
||||
task->setInWaiterList();
|
||||
isAsync = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAsync)
|
||||
return CreateWaitAsyncResult(cx, false, immediateResult, r);
|
||||
|
||||
if (!StartPromiseTask(cx, Move(task)))
|
||||
return false;
|
||||
|
||||
r.set(result);
|
||||
return true;
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1260,12 +843,11 @@ js::atomics_notify(JSContext* cx, unsigned argc, Value* vp)
|
||||
Rooted<TypedArrayObject*> view(cx, nullptr);
|
||||
if (!GetSharedTypedArray(cx, objv, &view))
|
||||
return false;
|
||||
if (!IsWaitableTypedArray(view->type()))
|
||||
if (view->type() != Scalar::Int32)
|
||||
return ReportBadArrayType(cx);
|
||||
uint32_t offset;
|
||||
if (!GetTypedArrayIndex(cx, idxv, view, &offset))
|
||||
return false;
|
||||
offset = GetWaiterByteOffset(view, offset);
|
||||
double count;
|
||||
if (countv.isUndefined()) {
|
||||
count = mozilla::PositiveInfinity<double>();
|
||||
@@ -1288,9 +870,9 @@ js::atomics_notify(JSContext* cx, unsigned argc, Value* vp)
|
||||
do {
|
||||
FutexWaiter* c = iter;
|
||||
iter = iter->lower_pri;
|
||||
if (c->offset != offset || !c->isWaiting())
|
||||
if (c->offset != offset || !c->rt->fx.isWaiting())
|
||||
continue;
|
||||
c->notify();
|
||||
c->rt->fx.notify(FutexRuntime::NotifyExplicit);
|
||||
++woken;
|
||||
--count;
|
||||
} while (count > 0 && iter != waiters);
|
||||
@@ -1524,7 +1106,6 @@ const JSFunctionSpec AtomicsMethods[] = {
|
||||
JS_INLINABLE_FN("xor", atomics_xor, 3,0, AtomicsXor),
|
||||
JS_INLINABLE_FN("isLockFree", atomics_isLockFree, 1,0, AtomicsIsLockFree),
|
||||
JS_FN("wait", atomics_wait, 4,0),
|
||||
JS_FN("waitAsync", atomics_waitAsync, 4,0),
|
||||
JS_FN("notify", atomics_notify, 3,0),
|
||||
JS_FN("wake", atomics_notify, 3,0), //Legacy name
|
||||
JS_FS_END
|
||||
|
||||
@@ -35,7 +35,6 @@ class AtomicsObject : public JSObject
|
||||
[[nodiscard]] bool atomics_xor(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] bool atomics_isLockFree(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] bool atomics_wait(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] bool atomics_waitAsync(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] bool atomics_notify(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
/* asm.js callouts */
|
||||
|
||||
@@ -1,608 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* 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 "builtin/FinalizationRegistryObject.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "builtin/WeakRefObject.h"
|
||||
#include "gc/Nursery.h"
|
||||
#include "gc/Tracer.h"
|
||||
#include "vm/EqualityOperations.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/Symbol.h"
|
||||
|
||||
#include "jswrapper.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/Interpreter-inl.h"
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
enum class WeakCellKind {
|
||||
Empty,
|
||||
Object,
|
||||
Symbol
|
||||
};
|
||||
|
||||
static JSObject*
|
||||
NormalizeWeakObject(JSObject* obj)
|
||||
{
|
||||
if (JSObject* unwrapped = CheckedUnwrap(obj, /* stopAtWindowProxy = */ false))
|
||||
return unwrapped;
|
||||
return obj;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetPrototypeFromFinalizationRegistryConstructor(JSContext* cx, HandleObject newTarget,
|
||||
MutableHandleObject proto)
|
||||
{
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, proto))
|
||||
return false;
|
||||
if (proto)
|
||||
return true;
|
||||
|
||||
RootedObject realmObject(cx, CheckedUnwrap(newTarget, /* stopAtWindowProxy = */ false));
|
||||
if (!realmObject)
|
||||
return false;
|
||||
|
||||
{
|
||||
JSAutoCompartment ac(cx, realmObject);
|
||||
Rooted<GlobalObject*> global(cx, &realmObject->global());
|
||||
if (!GlobalObject::ensureConstructor(cx, global, JSProto_FinalizationRegistry))
|
||||
return false;
|
||||
proto.set(&global->getPrototype(JSProto_FinalizationRegistry).toObject());
|
||||
}
|
||||
|
||||
return cx->compartment()->wrap(cx, proto);
|
||||
}
|
||||
|
||||
struct WeakCell {
|
||||
WeakCellKind kind;
|
||||
WeakRef<JSObject*> object;
|
||||
WeakRef<JS::Symbol*> symbol;
|
||||
|
||||
WeakCell() : kind(WeakCellKind::Empty), object(nullptr), symbol(nullptr) {}
|
||||
explicit WeakCell(JSObject* obj) : kind(WeakCellKind::Object), object(obj), symbol(nullptr) {}
|
||||
explicit WeakCell(JS::Symbol* sym) : kind(WeakCellKind::Symbol), object(nullptr), symbol(sym) {}
|
||||
|
||||
void clear() {
|
||||
kind = WeakCellKind::Empty;
|
||||
object = nullptr;
|
||||
symbol = nullptr;
|
||||
}
|
||||
|
||||
bool isEmpty() const { return kind == WeakCellKind::Empty; }
|
||||
|
||||
bool isDead() const {
|
||||
if (kind == WeakCellKind::Object)
|
||||
return !object.unbarrieredGet();
|
||||
if (kind == WeakCellKind::Symbol)
|
||||
return !symbol.unbarrieredGet();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sameValue(HandleValue value) const {
|
||||
if (kind == WeakCellKind::Object)
|
||||
return value.isObject() && object.get() == NormalizeWeakObject(&value.toObject());
|
||||
if (kind == WeakCellKind::Symbol)
|
||||
return value.isSymbol() && symbol.get() == value.toSymbol();
|
||||
return false;
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc, const char* name) {
|
||||
if (kind == WeakCellKind::Object) {
|
||||
JSObject* target = object.unbarrieredGet();
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (IsInsideNursery(target)) {
|
||||
TraceManuallyBarrieredEdge(trc, object.unsafeGet(), name);
|
||||
} else {
|
||||
if (trc->isMarkingTracer() && !target->asTenured().zone()->isCollecting())
|
||||
return;
|
||||
TraceWeakEdge(trc, &object, name);
|
||||
}
|
||||
} else if (kind == WeakCellKind::Symbol) {
|
||||
JS::Symbol* target = symbol.unbarrieredGet();
|
||||
if (target) {
|
||||
if (trc->isMarkingTracer() && !target->asTenured().zone()->isCollecting())
|
||||
return;
|
||||
TraceWeakEdge(trc, &symbol, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void traceIfZoneIsCollecting(JSTracer* trc, const char* name) {
|
||||
if (kind == WeakCellKind::Object) {
|
||||
JSObject* target = object.unbarrieredGet();
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (IsInsideNursery(target)) {
|
||||
TraceManuallyBarrieredEdge(trc, object.unsafeGet(), name);
|
||||
} else if (target->asTenured().zone()->isCollecting()) {
|
||||
TraceWeakEdge(trc, &object, name);
|
||||
}
|
||||
} else if (kind == WeakCellKind::Symbol) {
|
||||
JS::Symbol* target = symbol.unbarrieredGet();
|
||||
if (target && target->asTenured().zone()->isCollecting())
|
||||
TraceWeakEdge(trc, &symbol, name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FinalizationRecord {
|
||||
WeakCell target;
|
||||
JS::Heap<Value> heldValue;
|
||||
WeakCell unregisterToken;
|
||||
bool active;
|
||||
bool queued;
|
||||
|
||||
FinalizationRecord(HandleValue targetValue, HandleValue heldValue,
|
||||
HandleValue unregisterTokenValue)
|
||||
: heldValue(heldValue),
|
||||
active(true),
|
||||
queued(false)
|
||||
{
|
||||
if (targetValue.isObject())
|
||||
target = WeakCell(NormalizeWeakObject(&targetValue.toObject()));
|
||||
else
|
||||
target = WeakCell(targetValue.toSymbol());
|
||||
|
||||
if (unregisterTokenValue.isObject())
|
||||
unregisterToken = WeakCell(NormalizeWeakObject(&unregisterTokenValue.toObject()));
|
||||
else if (unregisterTokenValue.isSymbol())
|
||||
unregisterToken = WeakCell(unregisterTokenValue.toSymbol());
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
if (active || queued)
|
||||
JS::TraceEdge(trc, &heldValue, "FinalizationRegistry held value");
|
||||
|
||||
if (active) {
|
||||
target.trace(trc, "FinalizationRegistry target");
|
||||
if (!unregisterToken.isEmpty())
|
||||
unregisterToken.trace(trc, "FinalizationRegistry unregister token");
|
||||
}
|
||||
}
|
||||
|
||||
void traceWeakEdgesForCollectedZones(JSTracer* trc) {
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
target.traceIfZoneIsCollecting(trc, "FinalizationRegistry target");
|
||||
if (!unregisterToken.isEmpty())
|
||||
unregisterToken.traceIfZoneIsCollecting(trc,
|
||||
"FinalizationRegistry unregister token");
|
||||
}
|
||||
|
||||
bool targetIsDead() const {
|
||||
return active && target.isDead();
|
||||
}
|
||||
|
||||
bool matchesToken(HandleValue token) const {
|
||||
return active && !queued && !unregisterToken.isEmpty() &&
|
||||
unregisterToken.sameValue(token);
|
||||
}
|
||||
|
||||
void queueForCleanup() {
|
||||
MOZ_ASSERT(active);
|
||||
MOZ_ASSERT(!queued);
|
||||
active = false;
|
||||
queued = true;
|
||||
target.clear();
|
||||
unregisterToken.clear();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
active = false;
|
||||
queued = false;
|
||||
target.clear();
|
||||
unregisterToken.clear();
|
||||
heldValue.setUndefined();
|
||||
}
|
||||
};
|
||||
|
||||
using FinalizationRecordVector = Vector<FinalizationRecord*, 0, SystemAllocPolicy>;
|
||||
|
||||
struct FinalizationRegistryObject::Data {
|
||||
JS::Heap<JSObject*> cleanupCallback;
|
||||
JS::Heap<JSObject*> cleanupJob;
|
||||
FinalizationRecordVector records;
|
||||
FinalizationRecordVector cleanupQueue;
|
||||
bool queuedForCleanup;
|
||||
|
||||
explicit Data(JSObject* cleanupCallback)
|
||||
: cleanupCallback(cleanupCallback),
|
||||
cleanupJob(nullptr),
|
||||
records(SystemAllocPolicy()),
|
||||
cleanupQueue(SystemAllocPolicy()),
|
||||
queuedForCleanup(false)
|
||||
{}
|
||||
|
||||
~Data() {
|
||||
for (size_t i = 0; i < records.length(); i++)
|
||||
js_delete(records[i]);
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
JS::TraceEdge(trc, &cleanupCallback, "FinalizationRegistry cleanup callback");
|
||||
if (cleanupJob.unbarrieredGet())
|
||||
JS::TraceEdge(trc, &cleanupJob, "FinalizationRegistry cleanup job");
|
||||
for (size_t i = 0; i < records.length(); i++)
|
||||
records[i]->trace(trc);
|
||||
}
|
||||
|
||||
bool appendRecord(FinalizationRecord* record) {
|
||||
return records.append(record);
|
||||
}
|
||||
|
||||
bool appendCleanupRecord(FinalizationRecord* record) {
|
||||
return cleanupQueue.append(record);
|
||||
}
|
||||
|
||||
void compactRecords() {
|
||||
for (size_t i = 0; i < records.length();) {
|
||||
FinalizationRecord* record = records[i];
|
||||
if (!record->active && !record->queued) {
|
||||
js_delete(record);
|
||||
records.erase(records.begin() + i);
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void unregister(HandleValue token, bool* removed) {
|
||||
*removed = false;
|
||||
for (size_t i = 0; i < records.length(); i++) {
|
||||
FinalizationRecord* record = records[i];
|
||||
if (record->matchesToken(token)) {
|
||||
record->clear();
|
||||
*removed = true;
|
||||
}
|
||||
}
|
||||
compactRecords();
|
||||
}
|
||||
};
|
||||
|
||||
static FinalizationRegistryObject::Data*
|
||||
GetData(JSObject* obj)
|
||||
{
|
||||
return obj->as<FinalizationRegistryObject>().getData();
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
IsFinalizationRegistry(HandleValue v)
|
||||
{
|
||||
return v.isObject() && v.toObject().is<FinalizationRegistryObject>();
|
||||
}
|
||||
|
||||
static bool
|
||||
FinalizationRegistry_register_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsFinalizationRegistry(args.thisv()));
|
||||
|
||||
if (!CanBeHeldWeakly(args.get(0))) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_BAD_FINALIZATION_REGISTRY_TARGET);
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue target(cx, args[0]);
|
||||
RootedValue heldValue(cx, args.get(1));
|
||||
|
||||
bool same;
|
||||
if (!SameValue(cx, target, heldValue, &same))
|
||||
return false;
|
||||
if (same) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_BAD_FINALIZATION_REGISTRY_HELD_VALUE);
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue unregisterToken(cx, args.get(2));
|
||||
if (!CanBeHeldWeakly(unregisterToken)) {
|
||||
if (!unregisterToken.isUndefined()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_BAD_FINALIZATION_REGISTRY_TOKEN);
|
||||
return false;
|
||||
}
|
||||
unregisterToken.setUndefined();
|
||||
}
|
||||
|
||||
FinalizationRecord* record = cx->new_<FinalizationRecord>(target, heldValue, unregisterToken);
|
||||
if (!record)
|
||||
return false;
|
||||
|
||||
auto* data = GetData(&args.thisv().toObject());
|
||||
if (!data->appendRecord(record)) {
|
||||
js_delete(record);
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
FinalizationRegistry_unregister_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsFinalizationRegistry(args.thisv()));
|
||||
|
||||
RootedValue unregisterToken(cx, args.get(0));
|
||||
if (!CanBeHeldWeakly(unregisterToken)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_BAD_FINALIZATION_REGISTRY_TOKEN);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool removed = false;
|
||||
auto* data = GetData(&args.thisv().toObject());
|
||||
data->unregister(unregisterToken, &removed);
|
||||
|
||||
args.rval().setBoolean(removed);
|
||||
return true;
|
||||
}
|
||||
|
||||
const JSPropertySpec FinalizationRegistryObject::properties[] = {
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec FinalizationRegistryObject::methods[] = {
|
||||
JS_FN("register", FinalizationRegistryObject::register_, 2, 0),
|
||||
JS_FN("unregister", FinalizationRegistryObject::unregister, 1, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static JSObject*
|
||||
InitFinalizationRegistryClass(JSContext* cx, HandleObject obj, bool defineMembers)
|
||||
{
|
||||
Handle<GlobalObject*> global = obj.as<GlobalObject>();
|
||||
RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx));
|
||||
if (!proto)
|
||||
return nullptr;
|
||||
|
||||
RootedFunction ctor(cx, GlobalObject::createConstructor(cx, FinalizationRegistryObject::construct,
|
||||
ClassName(JSProto_FinalizationRegistry, cx), 1));
|
||||
if (!ctor)
|
||||
return nullptr;
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto))
|
||||
return nullptr;
|
||||
|
||||
if (defineMembers) {
|
||||
if (!DefinePropertiesAndFunctions(cx, proto, FinalizationRegistryObject::properties,
|
||||
FinalizationRegistryObject::methods)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!DefineToStringTag(cx, proto, cx->names().FinalizationRegistry))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_FinalizationRegistry, ctor, proto))
|
||||
return nullptr;
|
||||
return proto;
|
||||
}
|
||||
|
||||
/* static */ FinalizationRegistryObject*
|
||||
FinalizationRegistryObject::create(JSContext* cx, HandleObject cleanupCallback,
|
||||
HandleObject proto /* = nullptr */)
|
||||
{
|
||||
Rooted<FinalizationRegistryObject*> obj(cx,
|
||||
NewObjectWithClassProto<FinalizationRegistryObject>(cx, proto));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
Data* data = cx->new_<Data>(cleanupCallback);
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
obj->setPrivate(data);
|
||||
|
||||
RootedAtom funName(cx, cx->names().empty);
|
||||
RootedFunction cleanupJob(cx, NewNativeFunction(cx, FinalizationRegistryObject::cleanupJob, 0,
|
||||
funName, gc::AllocKind::FUNCTION_EXTENDED,
|
||||
GenericObject));
|
||||
if (!cleanupJob)
|
||||
return nullptr;
|
||||
|
||||
cleanupJob->setExtendedSlot(0, ObjectValue(*obj));
|
||||
data->cleanupJob = cleanupJob;
|
||||
|
||||
if (!cx->zone()->finalizationRegistries.append(WeakRef<JSObject*>(obj))) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
FinalizationRegistryObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (!ThrowIfNotConstructing(cx, args, "FinalizationRegistry"))
|
||||
return false;
|
||||
|
||||
RootedValue cleanupCallbackValue(cx, args.get(0));
|
||||
RootedObject cleanupCallback(cx, ValueToCallable(cx, cleanupCallbackValue, 0, NO_CONSTRUCT));
|
||||
if (!cleanupCallback)
|
||||
return false;
|
||||
|
||||
RootedObject proto(cx);
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
if (!GetPrototypeFromFinalizationRegistryConstructor(cx, newTarget, &proto))
|
||||
return false;
|
||||
|
||||
Rooted<FinalizationRegistryObject*> obj(cx,
|
||||
FinalizationRegistryObject::create(cx, cleanupCallback, proto));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
FinalizationRegistryObject::register_(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsFinalizationRegistry, FinalizationRegistry_register_impl>(cx, args);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
FinalizationRegistryObject::unregister(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsFinalizationRegistry, FinalizationRegistry_unregister_impl>(cx, args);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
FinalizationRegistryObject::cleanupJob(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedFunction job(cx, &args.callee().as<JSFunction>());
|
||||
Rooted<FinalizationRegistryObject*> registry(cx,
|
||||
&job->getExtendedSlot(0).toObject().as<FinalizationRegistryObject>());
|
||||
Data* data = registry->getData();
|
||||
if (!data) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
data->queuedForCleanup = false;
|
||||
|
||||
RootedValue callback(cx, ObjectValue(*data->cleanupCallback.get()));
|
||||
RootedValue heldValue(cx);
|
||||
RootedValue ignored(cx);
|
||||
RootedValue undefined(cx, UndefinedValue());
|
||||
|
||||
while (data->cleanupQueue.length() > 0) {
|
||||
FinalizationRecord* record = data->cleanupQueue[0];
|
||||
data->cleanupQueue.erase(data->cleanupQueue.begin());
|
||||
|
||||
if (!record->queued) {
|
||||
data->compactRecords();
|
||||
continue;
|
||||
}
|
||||
|
||||
heldValue.set(record->heldValue.get());
|
||||
record->clear();
|
||||
data->compactRecords();
|
||||
|
||||
if (!Call(cx, callback, undefined, heldValue, &ignored))
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FinalizationRegistryObject::trace(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
if (Data* data = GetData(obj))
|
||||
data->trace(trc);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
FinalizationRegistryObject::finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
if (Data* data = GetData(obj))
|
||||
fop->delete_(data);
|
||||
}
|
||||
|
||||
void
|
||||
FinalizationRegistryObject::traceWeakEdgesForCollectedZones(JSTracer* trc)
|
||||
{
|
||||
Data* data = getData();
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < data->records.length(); i++)
|
||||
data->records[i]->traceWeakEdgesForCollectedZones(trc);
|
||||
}
|
||||
|
||||
void
|
||||
FinalizationRegistryObject::sweepAfterGC(JSRuntime* rt)
|
||||
{
|
||||
Data* data = getData();
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
bool needsCleanupJob = false;
|
||||
for (size_t i = 0; i < data->records.length(); i++) {
|
||||
FinalizationRecord* record = data->records[i];
|
||||
if (!record->targetIsDead())
|
||||
continue;
|
||||
|
||||
record->queueForCleanup();
|
||||
if (!data->appendCleanupRecord(record)) {
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
oomUnsafe.crash("queueing FinalizationRegistry cleanup record");
|
||||
}
|
||||
needsCleanupJob = true;
|
||||
}
|
||||
|
||||
if (needsCleanupJob && !data->queuedForCleanup) {
|
||||
JSContext* cx = rt->contextFromMainThread();
|
||||
RootedObject cleanupJob(cx, data->cleanupJob.unbarrieredGet());
|
||||
if (!rt->enqueueFinalizationRegistryCleanupJob(cx, cleanupJob)) {
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
oomUnsafe.crash("queueing FinalizationRegistry cleanup job");
|
||||
}
|
||||
data->queuedForCleanup = true;
|
||||
}
|
||||
|
||||
data->compactRecords();
|
||||
}
|
||||
|
||||
static const ClassOps FinalizationRegistryObjectClassOps = {
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* setProperty */
|
||||
nullptr, /* enumerate */
|
||||
nullptr, /* resolve */
|
||||
nullptr, /* mayResolve */
|
||||
FinalizationRegistryObject::finalize,
|
||||
nullptr, /* call */
|
||||
nullptr, /* hasInstance */
|
||||
nullptr, /* construct */
|
||||
FinalizationRegistryObject::trace
|
||||
};
|
||||
|
||||
const Class FinalizationRegistryObject::class_ = {
|
||||
"FinalizationRegistry",
|
||||
JSCLASS_HAS_PRIVATE |
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_FinalizationRegistry) |
|
||||
JSCLASS_FOREGROUND_FINALIZE,
|
||||
&FinalizationRegistryObjectClassOps
|
||||
};
|
||||
|
||||
/* static */ JSObject*
|
||||
FinalizationRegistryObject::initClass(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
return ::InitFinalizationRegistryClass(cx, obj, true);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::InitFinalizationRegistryClass(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
return FinalizationRegistryObject::initClass(cx, obj);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::InitBareFinalizationRegistryCtor(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
return ::InitFinalizationRegistryClass(cx, obj, false);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* 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 builtin_FinalizationRegistryObject_h
|
||||
#define builtin_FinalizationRegistryObject_h
|
||||
|
||||
#include "gc/Barrier.h"
|
||||
#include "vm/NativeObject.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class FinalizationRegistryObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
struct Data;
|
||||
|
||||
static const Class class_;
|
||||
|
||||
static JSObject* initClass(JSContext* cx, HandleObject obj);
|
||||
static FinalizationRegistryObject* create(JSContext* cx, HandleObject cleanupCallback,
|
||||
HandleObject proto = nullptr);
|
||||
|
||||
static void trace(JSTracer* trc, JSObject* obj);
|
||||
static void finalize(FreeOp* fop, JSObject* obj);
|
||||
void traceWeakEdgesForCollectedZones(JSTracer* trc);
|
||||
[[nodiscard]] static bool construct(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool register_(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool unregister(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool cleanupJob(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
void sweepAfterGC(JSRuntime* rt);
|
||||
|
||||
Data* getData() const {
|
||||
return static_cast<Data*>(getPrivate());
|
||||
}
|
||||
|
||||
static const JSPropertySpec properties[];
|
||||
static const JSFunctionSpec methods[];
|
||||
};
|
||||
|
||||
extern JSObject*
|
||||
InitFinalizationRegistryClass(JSContext* cx, HandleObject obj);
|
||||
|
||||
extern JSObject*
|
||||
InitBareFinalizationRegistryCtor(JSContext* cx, HandleObject obj);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* builtin_FinalizationRegistryObject_h */
|
||||
@@ -55,49 +55,6 @@ function MapForEach(callbackfn, thisArg = undefined) {
|
||||
}
|
||||
}
|
||||
|
||||
// ES2024
|
||||
// Map.groupBy ( items, callbackfn )
|
||||
function MapGroupBy(items, callbackfn) {
|
||||
// Step 1.
|
||||
RequireObjectCoercible(items);
|
||||
|
||||
// Step 2.
|
||||
if (!IsCallable(callbackfn))
|
||||
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
|
||||
|
||||
// Step 3.
|
||||
var groups = std_Map_create();
|
||||
|
||||
// Step 4.
|
||||
var k = 0;
|
||||
|
||||
// Steps 5-8.
|
||||
for (var value of allowContentIter(items)) {
|
||||
// Step 6.a.
|
||||
if (k >= MAX_NUMERIC_INDEX)
|
||||
ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
|
||||
|
||||
// Step 6.b.
|
||||
var key = callContentFunction(callbackfn, undefined, value, k);
|
||||
|
||||
// Steps 6.c-d.
|
||||
var elements;
|
||||
if (callFunction(std_Map_has, groups, key)) {
|
||||
elements = callFunction(std_Map_get, groups, key);
|
||||
callFunction(std_Array_push, elements, value);
|
||||
} else {
|
||||
elements = [value];
|
||||
callFunction(std_Map_set, groups, key, elements);
|
||||
}
|
||||
|
||||
// Step 6.e.
|
||||
k++;
|
||||
}
|
||||
|
||||
// Step 9.
|
||||
return groups;
|
||||
}
|
||||
|
||||
var iteratorTemp = { mapIterationResultPair : null };
|
||||
|
||||
function MapIteratorNext() {
|
||||
|
||||
@@ -332,15 +332,9 @@ const JSPropertySpec MapObject::staticProperties[] = {
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
const JSFunctionSpec MapObject::staticMethods[] = {
|
||||
JS_SELF_HOSTED_FN("groupBy", "MapGroupBy", 2, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static JSObject*
|
||||
InitClass(JSContext* cx, Handle<GlobalObject*> global, const Class* clasp, JSProtoKey key, Native construct,
|
||||
const JSPropertySpec* properties, const JSFunctionSpec* methods,
|
||||
const JSFunctionSpec* staticMethods,
|
||||
const JSPropertySpec* staticProperties)
|
||||
{
|
||||
RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx));
|
||||
@@ -349,13 +343,8 @@ InitClass(JSContext* cx, Handle<GlobalObject*> global, const Class* clasp, JSPro
|
||||
|
||||
Rooted<JSFunction*> ctor(cx, global->createConstructor(cx, construct, ClassName(key, cx), 0));
|
||||
if (!ctor ||
|
||||
!JS_DefineProperties(cx, ctor, staticProperties))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (staticMethods && !JS_DefineFunctions(cx, ctor, staticMethods))
|
||||
return nullptr;
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, proto) ||
|
||||
!JS_DefineProperties(cx, ctor, staticProperties) ||
|
||||
!LinkConstructorAndPrototype(cx, ctor, proto) ||
|
||||
!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
|
||||
!GlobalObject::initBuiltinConstructor(cx, global, key, ctor, proto))
|
||||
{
|
||||
@@ -370,7 +359,7 @@ MapObject::initClass(JSContext* cx, JSObject* obj)
|
||||
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
|
||||
RootedObject proto(cx,
|
||||
InitClass(cx, global, &class_, JSProto_Map, construct, properties, methods,
|
||||
staticMethods, staticProperties));
|
||||
staticProperties));
|
||||
if (proto) {
|
||||
// Define the "entries" method.
|
||||
JSFunction* fun = JS_DefineFunction(cx, proto, "entries", entries, 0, 0);
|
||||
@@ -1095,7 +1084,7 @@ SetObject::initClass(JSContext* cx, JSObject* obj)
|
||||
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
|
||||
RootedObject proto(cx,
|
||||
InitClass(cx, global, &class_, JSProto_Set, construct, properties, methods,
|
||||
nullptr, staticProperties));
|
||||
staticProperties));
|
||||
if (proto) {
|
||||
// Define the "values" method.
|
||||
JSFunction* fun = JS_DefineFunction(cx, proto, "values", values, 0, 0);
|
||||
|
||||
@@ -110,9 +110,7 @@ class MapObject : public NativeObject {
|
||||
[[nodiscard]] static bool getKeysAndValuesInterleaved(JSContext* cx, HandleObject obj,
|
||||
JS::MutableHandle<GCVector<JS::Value>> entries);
|
||||
[[nodiscard]] static bool entries(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool get(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool has(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool set(JSContext* cx, unsigned argc, Value* vp);
|
||||
static MapObject* create(JSContext* cx, HandleObject proto = nullptr);
|
||||
|
||||
// Publicly exposed Map calls for JSAPI access (webidl maplike/setlike
|
||||
@@ -139,7 +137,6 @@ class MapObject : public NativeObject {
|
||||
|
||||
static const JSPropertySpec properties[];
|
||||
static const JSFunctionSpec methods[];
|
||||
static const JSFunctionSpec staticMethods[];
|
||||
static const JSPropertySpec staticProperties[];
|
||||
ValueMap* getData() { return static_cast<ValueMap*>(getPrivate()); }
|
||||
static ValueMap& extract(HandleObject o);
|
||||
@@ -156,8 +153,10 @@ class MapObject : public NativeObject {
|
||||
[[nodiscard]] static bool size_impl(JSContext* cx, const CallArgs& args);
|
||||
[[nodiscard]] static bool size(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool get_impl(JSContext* cx, const CallArgs& args);
|
||||
[[nodiscard]] static bool get(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool has_impl(JSContext* cx, const CallArgs& args);
|
||||
[[nodiscard]] static bool set_impl(JSContext* cx, const CallArgs& args);
|
||||
[[nodiscard]] static bool set(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool delete_impl(JSContext* cx, const CallArgs& args);
|
||||
[[nodiscard]] static bool delete_(JSContext* cx, unsigned argc, Value* vp);
|
||||
[[nodiscard]] static bool keys_impl(JSContext* cx, const CallArgs& args);
|
||||
|
||||
@@ -240,16 +240,12 @@ function ObjectGroupBy(items, callbackfn) {
|
||||
// Steps 5-8.
|
||||
for (var value of allowContentIter(items)) {
|
||||
// Step 6.a.
|
||||
if (k >= MAX_NUMERIC_INDEX)
|
||||
ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
|
||||
|
||||
// Step 6.b.
|
||||
var key = callContentFunction(callbackfn, undefined, value, k);
|
||||
|
||||
// Step 6.c.
|
||||
// Step 6.b.
|
||||
key = ToPropertyKey(key);
|
||||
|
||||
// Steps 6.d-e.
|
||||
// Steps 6.c-d.
|
||||
var elements = groups[key];
|
||||
if (elements === undefined) {
|
||||
_DefineDataProperty(groups, key, [value]);
|
||||
@@ -257,7 +253,7 @@ function ObjectGroupBy(items, callbackfn) {
|
||||
callFunction(std_Array_push, elements, value);
|
||||
}
|
||||
|
||||
// Step 6.f.
|
||||
// Step 6.e.
|
||||
k++;
|
||||
}
|
||||
|
||||
|
||||
@@ -3722,48 +3722,6 @@ Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
// ES2024
|
||||
// Promise.withResolvers ( )
|
||||
static bool
|
||||
Promise_static_withResolvers(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
// Step 1.
|
||||
if (!args.thisv().isObject()) {
|
||||
ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, -1, args.thisv(), nullptr);
|
||||
return false;
|
||||
}
|
||||
RootedObject C(cx, &args.thisv().toObject());
|
||||
|
||||
// Step 2.
|
||||
Rooted<PromiseCapability> capability(cx);
|
||||
if (!NewPromiseCapability(cx, C, &capability, false))
|
||||
return false;
|
||||
|
||||
// Step 3.
|
||||
RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
// Steps 4-7.
|
||||
RootedValue promise(cx, ObjectValue(*capability.promise()));
|
||||
if (!::JS_DefineProperty(cx, obj, "promise", promise, JSPROP_ENUMERATE))
|
||||
return false;
|
||||
|
||||
RootedValue resolve(cx, ObjectValue(*capability.resolve()));
|
||||
if (!::JS_DefineProperty(cx, obj, "resolve", resolve, JSPROP_ENUMERATE))
|
||||
return false;
|
||||
|
||||
RootedValue reject(cx, ObjectValue(*capability.reject()));
|
||||
if (!::JS_DefineProperty(cx, obj, "reject", reject, JSPROP_ENUMERATE))
|
||||
return false;
|
||||
|
||||
// Step 8.
|
||||
args.rval().setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unforgeable version of ES2016, 25.4.4.5, Promise.resolve.
|
||||
*/
|
||||
@@ -5293,7 +5251,6 @@ static const JSFunctionSpec promise_static_methods[] = {
|
||||
JS_FN("race", Promise_static_race, 1, 0),
|
||||
JS_FN("reject", Promise_reject, 1, 0),
|
||||
JS_FN("resolve", Promise_static_resolve, 1, 0),
|
||||
JS_FN("withResolvers", Promise_static_withResolvers, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
||||
@@ -8,11 +8,8 @@
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "builtin/SelfHostingDefines.h"
|
||||
#include "irregexp/RegExpParser.h"
|
||||
#include "jit/InlinableNatives.h"
|
||||
#include "vm/RegExpStatics.h"
|
||||
@@ -1757,9 +1754,7 @@ js::RegExpGetSubstitution(JSContext* cx, HandleArrayObject matchResult, HandleLi
|
||||
// Step 6.
|
||||
MOZ_ASSERT(position <= string->length());
|
||||
|
||||
// String substitutions can only reference $1 through $99.
|
||||
uint32_t nCaptures = std::min<uint32_t>(matchResultLength - 1,
|
||||
REGEXP_MAX_SUBSTITUTION_CAPTURES);
|
||||
uint32_t nCaptures = matchResultLength - 1;
|
||||
Rooted<CapturesVector> captures(cx, CapturesVector(cx));
|
||||
if (!captures.reserve(nCaptures))
|
||||
return false;
|
||||
|
||||
@@ -463,12 +463,6 @@ function RegExpGetComplexReplacement(result, matched, S, position,
|
||||
// Step 14.k.i (reordered).
|
||||
_DefineDataProperty(captures, capturesLength++, matched);
|
||||
|
||||
// String substitutions can only reference $1 through $99. Keep getting and
|
||||
// stringifying all captures because those operations are observable.
|
||||
var storedCaptures = functionalReplace ?
|
||||
nCaptures :
|
||||
std_Math_min(nCaptures, REGEXP_MAX_SUBSTITUTION_CAPTURES);
|
||||
|
||||
// Step 14.g, 14.i, 14.i.iv.
|
||||
for (var n = 1; n <= nCaptures; n++) {
|
||||
// Step 14.i.i.
|
||||
@@ -479,9 +473,7 @@ function RegExpGetComplexReplacement(result, matched, S, position,
|
||||
capN = ToString(capN);
|
||||
|
||||
// Step 14.i.iii.
|
||||
if (n <= storedCaptures) {
|
||||
_DefineDataProperty(captures, capturesLength++, capN);
|
||||
}
|
||||
_DefineDataProperty(captures, capturesLength++, capN);
|
||||
}
|
||||
|
||||
// Step 14.j.
|
||||
|
||||
@@ -79,8 +79,6 @@
|
||||
|
||||
#define REGEXP_FLAGS_SLOT 2
|
||||
|
||||
#define REGEXP_MAX_SUBSTITUTION_CAPTURES 99
|
||||
|
||||
#define REGEXP_IGNORECASE_FLAG 0x01
|
||||
#define REGEXP_GLOBAL_FLAG 0x02
|
||||
#define REGEXP_MULTILINE_FLAG 0x04
|
||||
|
||||
@@ -654,82 +654,6 @@ function String_repeat(count) {
|
||||
return T;
|
||||
}
|
||||
|
||||
// ES2024
|
||||
// String.prototype.isWellFormed ( )
|
||||
function String_isWellFormed() {
|
||||
// Steps 1-2.
|
||||
RequireObjectCoercible(this);
|
||||
var S = ToString(this);
|
||||
|
||||
// Step 3.
|
||||
var length = S.length;
|
||||
for (var k = 0; k < length; k++) {
|
||||
var c = callFunction(std_String_charCodeAt, S, k);
|
||||
if (c >= 0xD800 && c <= 0xDBFF) {
|
||||
if (k + 1 >= length)
|
||||
return false;
|
||||
|
||||
var d = callFunction(std_String_charCodeAt, S, k + 1);
|
||||
if (d < 0xDC00 || d > 0xDFFF)
|
||||
return false;
|
||||
|
||||
k++;
|
||||
} else if (c >= 0xDC00 && c <= 0xDFFF) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4.
|
||||
return true;
|
||||
}
|
||||
|
||||
// ES2024
|
||||
// String.prototype.toWellFormed ( )
|
||||
function String_toWellFormed() {
|
||||
// Steps 1-2.
|
||||
RequireObjectCoercible(this);
|
||||
var S = ToString(this);
|
||||
|
||||
// Step 3.
|
||||
var length = S.length;
|
||||
var result = "";
|
||||
var copied = 0;
|
||||
|
||||
for (var k = 0; k < length; k++) {
|
||||
var c = callFunction(std_String_charCodeAt, S, k);
|
||||
var isUnpairedSurrogate = false;
|
||||
|
||||
if (c >= 0xD800 && c <= 0xDBFF) {
|
||||
if (k + 1 < length) {
|
||||
var d = callFunction(std_String_charCodeAt, S, k + 1);
|
||||
if (d >= 0xDC00 && d <= 0xDFFF) {
|
||||
k++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
isUnpairedSurrogate = true;
|
||||
} else if (c >= 0xDC00 && c <= 0xDFFF) {
|
||||
isUnpairedSurrogate = true;
|
||||
}
|
||||
|
||||
if (isUnpairedSurrogate) {
|
||||
if (copied < k)
|
||||
result += callFunction(String_substring, S, copied, k);
|
||||
result += "\uFFFD";
|
||||
copied = k + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (copied === 0)
|
||||
return S;
|
||||
|
||||
if (copied < length)
|
||||
result += callFunction(String_substring, S, copied, length);
|
||||
|
||||
// Step 4.
|
||||
return result;
|
||||
}
|
||||
|
||||
// ES6 draft specification, section 21.1.3.27, version 2013-09-27.
|
||||
function String_iterator() {
|
||||
RequireObjectCoercible(this);
|
||||
|
||||
@@ -41,21 +41,10 @@ function TypedArrayContentTypeIsBigIntMethod() {
|
||||
return IsBigInt64TypedArray(this) || IsBigUint64TypedArray(this);
|
||||
}
|
||||
|
||||
function ThrowIfTypedArrayOutOfBounds(tarray) {
|
||||
if (TypedArrayIsOutOfBounds(tarray))
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS);
|
||||
}
|
||||
|
||||
function ThrowIfPossiblyWrappedTypedArrayOutOfBounds(tarray) {
|
||||
if (PossiblyWrappedTypedArrayIsOutOfBounds(tarray))
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_OUT_OF_BOUNDS);
|
||||
}
|
||||
|
||||
function GetAttachedArrayBuffer(tarray) {
|
||||
var buffer = ViewedArrayBufferIfReified(tarray);
|
||||
if (IsDetachedBuffer(buffer))
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
ThrowIfTypedArrayOutOfBounds(tarray);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -78,7 +67,6 @@ function IsTypedArrayEnsuringArrayBuffer(arg) {
|
||||
if (IsObject(arg) && IsPossiblyWrappedTypedArray(arg)) {
|
||||
if (PossiblyWrappedTypedArrayHasDetachedBuffer(arg))
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
ThrowIfPossiblyWrappedTypedArrayOutOfBounds(arg);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -101,7 +89,6 @@ function ValidateTypedArray(obj, error) {
|
||||
if (IsPossiblyWrappedTypedArray(obj)) {
|
||||
if (PossiblyWrappedTypedArrayHasDetachedBuffer(obj))
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
ThrowIfPossiblyWrappedTypedArrayOutOfBounds(obj);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1143,18 +1130,12 @@ function TypedArraySet(overloaded, offset = 0) {
|
||||
// Steps 9-10.
|
||||
var targetBuffer = GetAttachedArrayBuffer(target);
|
||||
|
||||
ThrowIfTypedArrayOutOfBounds(target);
|
||||
|
||||
// Step 11.
|
||||
var targetLength = TypedArrayLength(target);
|
||||
|
||||
// Steps 12 et seq.
|
||||
if (IsPossiblyWrappedTypedArray(overloaded)) {
|
||||
if (PossiblyWrappedTypedArrayHasDetachedBuffer(overloaded))
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
ThrowIfPossiblyWrappedTypedArrayOutOfBounds(overloaded);
|
||||
if (IsPossiblyWrappedTypedArray(overloaded))
|
||||
return SetFromTypedArray(target, overloaded, targetOffset, targetLength);
|
||||
}
|
||||
|
||||
return SetFromNonTypedArray(target, overloaded, targetOffset, targetLength, targetBuffer);
|
||||
}
|
||||
@@ -1491,8 +1472,6 @@ function TypedArraySubarray(begin, end) {
|
||||
"TypedArraySubarray");
|
||||
}
|
||||
|
||||
GetAttachedArrayBuffer(obj);
|
||||
|
||||
// Steps 4-6.
|
||||
var buffer = TypedArrayBuffer(obj);
|
||||
var srcLength = TypedArrayLength(obj);
|
||||
@@ -1973,10 +1952,7 @@ function ArrayBufferSlice(start, end) {
|
||||
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
|
||||
|
||||
// Steps 19-21.
|
||||
var currentLen = ArrayBufferByteLength(O);
|
||||
var copyLen = first >= currentLen ? 0 : std_Math_min(newLen, currentLen - first);
|
||||
if (copyLen > 0)
|
||||
ArrayBufferCopyData(new_, 0, O, first | 0, copyLen | 0, isWrapped);
|
||||
ArrayBufferCopyData(new_, 0, O, first | 0, newLen | 0, isWrapped);
|
||||
|
||||
// Step 22.
|
||||
return new_;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "builtin/WeakRefObject.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
|
||||
#include "vm/Interpreter-inl.h"
|
||||
@@ -22,80 +21,21 @@ IsWeakMap(HandleValue v)
|
||||
return v.isObject() && v.toObject().is<WeakMapObject>();
|
||||
}
|
||||
|
||||
struct WeakMapObject::Data
|
||||
{
|
||||
ObjectValueMap objectMap;
|
||||
SymbolValueMap symbolMap;
|
||||
|
||||
Data(JSContext* cx, JSObject* owner)
|
||||
: objectMap(cx, owner),
|
||||
symbolMap(cx, owner)
|
||||
{}
|
||||
|
||||
bool init() {
|
||||
return objectMap.init() && symbolMap.init();
|
||||
}
|
||||
};
|
||||
|
||||
ObjectValueMap*
|
||||
WeakMapObject::getMap()
|
||||
{
|
||||
Data* data = getData();
|
||||
return data ? &data->objectMap : nullptr;
|
||||
}
|
||||
|
||||
SymbolValueMap*
|
||||
WeakMapObject::getSymbolMap()
|
||||
{
|
||||
Data* data = getData();
|
||||
return data ? &data->symbolMap : nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
EnsureWeakMapData(JSContext* cx, Handle<WeakMapObject*> mapObj)
|
||||
{
|
||||
if (mapObj->getData())
|
||||
return true;
|
||||
|
||||
auto data = cx->make_unique<WeakMapObject::Data>(cx, mapObj.get());
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
if (!data->init()) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
mapObj->setPrivate(data.release());
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
WeakMap_has_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
||||
|
||||
if (!CanBeHeldWeakly(args.get(0))) {
|
||||
if (!args.get(0).isObject()) {
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
WeakMapObject& weakMap = args.thisv().toObject().as<WeakMapObject>();
|
||||
if (args.get(0).isObject()) {
|
||||
if (ObjectValueMap* map = weakMap.getMap()) {
|
||||
JSObject* key = &args[0].toObject();
|
||||
if (map->has(key)) {
|
||||
args.rval().setBoolean(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (SymbolValueMap* map = weakMap.getSymbolMap()) {
|
||||
JS::Symbol* key = args[0].toSymbol();
|
||||
if (map->has(key)) {
|
||||
args.rval().setBoolean(true);
|
||||
return true;
|
||||
}
|
||||
if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
|
||||
JSObject* key = &args[0].toObject();
|
||||
if (map->has(key)) {
|
||||
args.rval().setBoolean(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,27 +55,16 @@ WeakMap_get_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
||||
|
||||
if (!CanBeHeldWeakly(args.get(0))) {
|
||||
if (!args.get(0).isObject()) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
WeakMapObject& weakMap = args.thisv().toObject().as<WeakMapObject>();
|
||||
if (args.get(0).isObject()) {
|
||||
if (ObjectValueMap* map = weakMap.getMap()) {
|
||||
JSObject* key = &args[0].toObject();
|
||||
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
|
||||
args.rval().set(ptr->value());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (SymbolValueMap* map = weakMap.getSymbolMap()) {
|
||||
JS::Symbol* key = args[0].toSymbol();
|
||||
if (SymbolValueMap::Ptr ptr = map->lookup(key)) {
|
||||
args.rval().set(ptr->value());
|
||||
return true;
|
||||
}
|
||||
if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
|
||||
JSObject* key = &args[0].toObject();
|
||||
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
|
||||
args.rval().set(ptr->value());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,29 +84,17 @@ WeakMap_delete_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
||||
|
||||
if (!CanBeHeldWeakly(args.get(0))) {
|
||||
if (!args.get(0).isObject()) {
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
WeakMapObject& weakMap = args.thisv().toObject().as<WeakMapObject>();
|
||||
if (args.get(0).isObject()) {
|
||||
if (ObjectValueMap* map = weakMap.getMap()) {
|
||||
JSObject* key = &args[0].toObject();
|
||||
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
|
||||
map->remove(ptr);
|
||||
args.rval().setBoolean(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (SymbolValueMap* map = weakMap.getSymbolMap()) {
|
||||
JS::Symbol* key = args[0].toSymbol();
|
||||
if (SymbolValueMap::Ptr ptr = map->lookup(key)) {
|
||||
map->remove(ptr);
|
||||
args.rval().setBoolean(true);
|
||||
return true;
|
||||
}
|
||||
if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
|
||||
JSObject* key = &args[0].toObject();
|
||||
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
|
||||
map->remove(ptr);
|
||||
args.rval().setBoolean(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,13 +126,22 @@ TryPreserveReflector(JSContext* cx, HandleObject obj)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetWeakMapObjectEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
||||
HandleObject key, HandleValue value)
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
||||
HandleObject key, HandleValue value)
|
||||
{
|
||||
if (!EnsureWeakMapData(cx, mapObj))
|
||||
return false;
|
||||
ObjectValueMap* map = mapObj->getMap();
|
||||
if (!map) {
|
||||
auto newMap = cx->make_unique<ObjectValueMap>(cx, mapObj.get());
|
||||
if (!newMap)
|
||||
return false;
|
||||
if (!newMap->init()) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
map = newMap.release();
|
||||
mapObj->setPrivate(map);
|
||||
}
|
||||
|
||||
// Preserve wrapped native keys to prevent wrapper optimization.
|
||||
if (!TryPreserveReflector(cx, key))
|
||||
@@ -236,28 +162,13 @@ SetWeakMapObjectEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetWeakMapSymbolEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
||||
Handle<JS::Symbol*> key, HandleValue value)
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
WeakMap_set_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
if (!EnsureWeakMapData(cx, mapObj))
|
||||
return false;
|
||||
SymbolValueMap* map = mapObj->getSymbolMap();
|
||||
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
||||
|
||||
MOZ_ASSERT_IF(value.isObject(), value.toObject().compartment() == mapObj->compartment());
|
||||
if (!map->put(key, value)) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
||||
HandleValue key, HandleValue value)
|
||||
{
|
||||
if (!CanBeHeldWeakly(key)) {
|
||||
UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, key, nullptr);
|
||||
if (!args.get(0).isObject()) {
|
||||
UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args.get(0), nullptr);
|
||||
if (!bytes)
|
||||
return false;
|
||||
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
|
||||
@@ -265,24 +176,11 @@ SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key.isObject()) {
|
||||
RootedObject objectKey(cx, &key.toObject());
|
||||
return SetWeakMapObjectEntryInternal(cx, mapObj, objectKey, value);
|
||||
}
|
||||
|
||||
Rooted<JS::Symbol*> symbolKey(cx, key.toSymbol());
|
||||
return SetWeakMapSymbolEntryInternal(cx, mapObj, symbolKey, value);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
WeakMap_set_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsWeakMap(args.thisv()));
|
||||
|
||||
RootedObject key(cx, &args[0].toObject());
|
||||
Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
|
||||
Rooted<WeakMapObject*> map(cx, &thisObj->as<WeakMapObject>());
|
||||
|
||||
if (!SetWeakMapEntryInternal(cx, map, args.get(0), args.get(1)))
|
||||
if (!SetWeakMapEntryInternal(cx, map, key, args.get(1)))
|
||||
return false;
|
||||
args.rval().set(args.thisv());
|
||||
return true;
|
||||
@@ -295,13 +193,6 @@ js::WeakMap_set(JSContext* cx, unsigned argc, Value* vp)
|
||||
return CallNonGenericMethod<IsWeakMap, WeakMap_set_impl>(cx, args);
|
||||
}
|
||||
|
||||
bool
|
||||
js::SetWeakMapEntryValue(JSContext* cx, HandleObject mapObj, HandleValue key, HandleValue val)
|
||||
{
|
||||
Rooted<WeakMapObject*> rootedMap(cx, &mapObj->as<WeakMapObject>());
|
||||
return SetWeakMapEntryInternal(cx, rootedMap, key, val);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHandleObject ret)
|
||||
{
|
||||
@@ -314,11 +205,11 @@ JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHan
|
||||
RootedObject arr(cx, NewDenseEmptyArray(cx));
|
||||
if (!arr)
|
||||
return false;
|
||||
WeakMapObject::Data* data = obj->as<WeakMapObject>().getData();
|
||||
if (data) {
|
||||
ObjectValueMap* map = obj->as<WeakMapObject>().getMap();
|
||||
if (map) {
|
||||
// Prevent GC from mutating the weakmap while iterating.
|
||||
AutoSuppressGC suppress(cx);
|
||||
for (ObjectValueMap::Base::Range r = data->objectMap.all(); !r.empty(); r.popFront()) {
|
||||
for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) {
|
||||
JS::ExposeObjectToActiveJS(r.front().key());
|
||||
RootedObject key(cx, r.front().key());
|
||||
if (!cx->compartment()->wrap(cx, &key))
|
||||
@@ -326,14 +217,6 @@ JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHan
|
||||
if (!NewbornArrayPush(cx, arr, ObjectValue(*key)))
|
||||
return false;
|
||||
}
|
||||
for (SymbolValueMap::Base::Range r = data->symbolMap.all(); !r.empty(); r.popFront()) {
|
||||
gc::ExposeGCThingToActiveJS(JS::GCCellPtr(r.front().key().get()));
|
||||
RootedValue key(cx, SymbolValue(r.front().key()));
|
||||
if (!cx->compartment()->wrap(cx, &key))
|
||||
return false;
|
||||
if (!NewbornArrayPush(cx, arr, key))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ret.set(arr);
|
||||
return true;
|
||||
@@ -342,23 +225,21 @@ JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHan
|
||||
static void
|
||||
WeakMap_mark(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
if (WeakMapObject::Data* data = obj->as<WeakMapObject>().getData()) {
|
||||
data->objectMap.trace(trc);
|
||||
data->symbolMap.trace(trc);
|
||||
}
|
||||
if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap())
|
||||
map->trace(trc);
|
||||
}
|
||||
|
||||
static void
|
||||
WeakMap_finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(fop->maybeOffMainThread());
|
||||
if (WeakMapObject::Data* data = obj->as<WeakMapObject>().getData()) {
|
||||
if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap()) {
|
||||
#ifdef DEBUG
|
||||
data->~Data();
|
||||
memset(static_cast<void*>(data), 0xdc, sizeof(*data));
|
||||
fop->free_(data);
|
||||
map->~ObjectValueMap();
|
||||
memset(static_cast<void*>(map), 0xdc, sizeof(*map));
|
||||
fop->free_(map);
|
||||
#else
|
||||
fop->delete_(data);
|
||||
fop->delete_(map);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -401,8 +282,7 @@ JS::SetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key,
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, key, val);
|
||||
Rooted<WeakMapObject*> rootedMap(cx, &mapObj->as<WeakMapObject>());
|
||||
RootedValue keyValue(cx, ObjectValue(*key));
|
||||
return SetWeakMapEntryInternal(cx, rootedMap, keyValue, val);
|
||||
return SetWeakMapEntryInternal(cx, rootedMap, key, val);
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -506,3 +386,4 @@ js::InitBareWeakMapCtor(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
return InitWeakMapClass(cx, obj, false);
|
||||
}
|
||||
|
||||
@@ -16,11 +16,7 @@ class WeakMapObject : public NativeObject
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
struct Data;
|
||||
|
||||
Data* getData() { return static_cast<Data*>(getPrivate()); }
|
||||
ObjectValueMap* getMap();
|
||||
SymbolValueMap* getSymbolMap();
|
||||
ObjectValueMap* getMap() { return static_cast<ObjectValueMap*>(getPrivate()); }
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
#include "gc/Nursery.h"
|
||||
#include "gc/Tracer.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Symbol.h"
|
||||
|
||||
#include "jswrapper.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/Interpreter-inl.h"
|
||||
@@ -33,64 +31,17 @@ IsWeakRef(HandleValue v)
|
||||
return v.isObject() && v.toObject().is<WeakRefObject>();
|
||||
}
|
||||
|
||||
static bool
|
||||
GetPrototypeFromWeakRefConstructor(JSContext* cx, HandleObject newTarget,
|
||||
MutableHandleObject proto)
|
||||
{
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, proto))
|
||||
return false;
|
||||
if (proto)
|
||||
return true;
|
||||
|
||||
RootedObject realmObject(cx, CheckedUnwrap(newTarget, /* stopAtWindowProxy = */ false));
|
||||
if (!realmObject)
|
||||
return false;
|
||||
|
||||
{
|
||||
JSAutoCompartment ac(cx, realmObject);
|
||||
Rooted<GlobalObject*> global(cx, &realmObject->global());
|
||||
if (!GlobalObject::ensureConstructor(cx, global, JSProto_WeakRef))
|
||||
return false;
|
||||
proto.set(&global->getPrototype(JSProto_WeakRef).toObject());
|
||||
}
|
||||
|
||||
return cx->compartment()->wrap(cx, proto);
|
||||
}
|
||||
|
||||
static bool
|
||||
WeakRef_deref_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsWeakRef(args.thisv()));
|
||||
|
||||
WeakRefObject::Referent* data = GetReferent(&args.thisv().toObject());
|
||||
if (!data) {
|
||||
JSObject* target = data ? data->target.get() : nullptr;
|
||||
if (target)
|
||||
args.rval().setObject(*target);
|
||||
else
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (data->isObject()) {
|
||||
JSObject* target = data->objectTarget.get();
|
||||
if (target) {
|
||||
RootedValue kept(cx, ObjectValue(*target));
|
||||
if (!cx->runtime()->addWeakRefKeptObject(cx, kept))
|
||||
return false;
|
||||
args.rval().set(kept);
|
||||
} else {
|
||||
args.rval().setUndefined();
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(data->isSymbol());
|
||||
JS::Symbol* target = data->symbolTarget.get();
|
||||
if (target) {
|
||||
RootedValue kept(cx, SymbolValue(target));
|
||||
if (!cx->runtime()->addWeakRefKeptObject(cx, kept))
|
||||
return false;
|
||||
args.rval().set(kept);
|
||||
} else {
|
||||
args.rval().setUndefined();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -132,18 +83,13 @@ InitWeakRefClass(JSContext* cx, HandleObject obj, bool defineMembers)
|
||||
}
|
||||
|
||||
/* static */ WeakRefObject*
|
||||
WeakRefObject::create(JSContext* cx, HandleValue target, HandleObject proto /* = nullptr */)
|
||||
WeakRefObject::create(JSContext* cx, HandleObject target, HandleObject proto /* = nullptr */)
|
||||
{
|
||||
Rooted<WeakRefObject*> obj(cx, NewObjectWithClassProto<WeakRefObject>(cx, proto));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
Referent* data;
|
||||
if (target.isObject())
|
||||
data = cx->new_<Referent>(&target.toObject());
|
||||
else
|
||||
data = cx->new_<Referent>(target.toSymbol());
|
||||
|
||||
Referent* data = cx->new_<Referent>(target, cx->options().weakRefs());
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
@@ -151,18 +97,6 @@ WeakRefObject::create(JSContext* cx, HandleValue target, HandleObject proto /* =
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool
|
||||
js::CanBeHeldWeakly(HandleValue target)
|
||||
{
|
||||
if (target.isObject())
|
||||
return true;
|
||||
|
||||
if (target.isSymbol())
|
||||
return target.toSymbol()->code() != JS::SymbolCode::InSymbolRegistry;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
WeakRefObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@@ -171,25 +105,28 @@ WeakRefObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||
if (!ThrowIfNotConstructing(cx, args, "WeakRef"))
|
||||
return false;
|
||||
|
||||
if (!CanBeHeldWeakly(args.get(0))) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_WEAKREF_TARGET);
|
||||
if (!args.get(0).isObject()) {
|
||||
UniqueChars bytes =
|
||||
DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args.get(0), nullptr);
|
||||
if (!bytes)
|
||||
return false;
|
||||
|
||||
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
|
||||
bytes.get());
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue target(cx, args[0]);
|
||||
RootedObject target(cx, &args[0].toObject());
|
||||
|
||||
RootedObject proto(cx);
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
if (!GetPrototypeFromWeakRefConstructor(cx, newTarget, &proto))
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
return false;
|
||||
|
||||
Rooted<WeakRefObject*> obj(cx, WeakRefObject::create(cx, target, proto));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
if (!cx->runtime()->addWeakRefKeptObject(cx, target))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
@@ -205,23 +142,19 @@ WeakRefObject::deref(JSContext* cx, unsigned argc, Value* vp)
|
||||
WeakRefObject::trace(JSTracer* trc, JSObject* obj)
|
||||
{
|
||||
if (Referent* data = GetReferent(obj)) {
|
||||
if (data->isObject()) {
|
||||
JSObject* target = data->objectTarget.unbarrieredGet();
|
||||
if (!target)
|
||||
return;
|
||||
JSObject* target = data->target.unbarrieredGet();
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (IsInsideNursery(target)) {
|
||||
// Weak edges must be tenured; trace strongly while referent is in the nursery.
|
||||
TraceManuallyBarrieredEdge(trc, data->objectTarget.unsafeGet(),
|
||||
"WeakRef nursery referent");
|
||||
} else {
|
||||
TraceWeakEdge(trc, &data->objectTarget, "WeakRef referent");
|
||||
}
|
||||
// When pref-disabled, keep referent alive via strong trace so deref()
|
||||
// stays usable as a stub without touching GC internals.
|
||||
if (!data->enabled) {
|
||||
TraceManuallyBarrieredEdge(trc, data->target.unsafeGet(), "WeakRef stub referent");
|
||||
} else if (IsInsideNursery(target)) {
|
||||
// Weak edges must be tenured; trace strongly while referent is in the nursery.
|
||||
TraceManuallyBarrieredEdge(trc, data->target.unsafeGet(), "WeakRef nursery referent");
|
||||
} else {
|
||||
MOZ_ASSERT(data->isSymbol());
|
||||
JS::Symbol* target = data->symbolTarget.unbarrieredGet();
|
||||
if (target)
|
||||
TraceWeakEdge(trc, &data->symbolTarget, "WeakRef symbol referent");
|
||||
TraceWeakEdge(trc, &data->target, "WeakRef referent");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,6 +162,7 @@ WeakRefObject::trace(JSTracer* trc, JSObject* obj)
|
||||
/* static */ void
|
||||
WeakRefObject::finalize(FreeOp* fop, JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(!fop->maybeOffMainThread());
|
||||
if (Referent* data = GetReferent(obj))
|
||||
fop->delete_(data);
|
||||
}
|
||||
|
||||
@@ -11,34 +11,20 @@
|
||||
|
||||
namespace js {
|
||||
|
||||
bool CanBeHeldWeakly(HandleValue target);
|
||||
|
||||
class WeakRefObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
struct Referent {
|
||||
enum class Kind {
|
||||
Object,
|
||||
Symbol
|
||||
};
|
||||
|
||||
explicit Referent(JSObject* obj)
|
||||
: kind(Kind::Object), objectTarget(obj), symbolTarget(nullptr) {}
|
||||
explicit Referent(JS::Symbol* sym)
|
||||
: kind(Kind::Symbol), objectTarget(nullptr), symbolTarget(sym) {}
|
||||
|
||||
bool isObject() const { return kind == Kind::Object; }
|
||||
bool isSymbol() const { return kind == Kind::Symbol; }
|
||||
|
||||
Kind kind;
|
||||
WeakRef<JSObject*> objectTarget;
|
||||
WeakRef<JS::Symbol*> symbolTarget;
|
||||
explicit Referent(JSObject* obj, bool enabled)
|
||||
: target(obj), enabled(enabled) {}
|
||||
WeakRef<JSObject*> target;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static const Class class_;
|
||||
|
||||
static JSObject* initClass(JSContext* cx, HandleObject obj);
|
||||
static WeakRefObject* create(JSContext* cx, HandleValue target, HandleObject proto = nullptr);
|
||||
static WeakRefObject* create(JSContext* cx, HandleObject target, HandleObject proto = nullptr);
|
||||
|
||||
static void trace(JSTracer* trc, JSObject* obj);
|
||||
static void finalize(FreeOp* fop, JSObject* obj);
|
||||
@@ -51,8 +37,7 @@ class WeakRefObject : public NativeObject
|
||||
|
||||
WeakRef<JSObject*>& target() {
|
||||
MOZ_ASSERT(getData());
|
||||
MOZ_ASSERT(getData()->isObject());
|
||||
return getData()->objectTarget;
|
||||
return getData()->target;
|
||||
}
|
||||
|
||||
static const JSPropertySpec properties[];
|
||||
|
||||
@@ -32,7 +32,7 @@ function WeakSet_add(value) {
|
||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "add", typeof S);
|
||||
|
||||
// Step 5.
|
||||
if (!CanBeHeldWeakly(value))
|
||||
if (!IsObject(value))
|
||||
ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, DecompileArg(0, value));
|
||||
|
||||
// Steps 7-8.
|
||||
@@ -55,7 +55,7 @@ function WeakSet_delete(value) {
|
||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "delete", typeof S);
|
||||
|
||||
// Step 5.
|
||||
if (!CanBeHeldWeakly(value))
|
||||
if (!IsObject(value))
|
||||
return false;
|
||||
|
||||
// Steps 7-8.
|
||||
@@ -75,7 +75,7 @@ function WeakSet_has(value) {
|
||||
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "has", typeof S);
|
||||
|
||||
// Step 6.
|
||||
if (!CanBeHeldWeakly(value))
|
||||
if (!IsObject(value))
|
||||
return false;
|
||||
|
||||
// Steps 7-8.
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "builtin/MapObject.h"
|
||||
#include "builtin/SelfHostingDefines.h"
|
||||
#include "builtin/WeakMapObject.h"
|
||||
#include "builtin/WeakRefObject.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
|
||||
@@ -109,6 +108,7 @@ WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
||||
if (optimized) {
|
||||
RootedValue keyVal(cx);
|
||||
RootedObject keyObject(cx);
|
||||
RootedValue placeholder(cx, BooleanValue(true));
|
||||
RootedObject map(cx, &obj->getReservedSlot(WEAKSET_MAP_SLOT).toObject());
|
||||
RootedArrayObject array(cx, &iterable.toObject().as<ArrayObject>());
|
||||
@@ -116,7 +116,7 @@ WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||
keyVal.set(array->getDenseElement(index));
|
||||
MOZ_ASSERT(!keyVal.isMagic(JS_ELEMENTS_HOLE));
|
||||
|
||||
if (!CanBeHeldWeakly(keyVal)) {
|
||||
if (keyVal.isPrimitive()) {
|
||||
UniqueChars bytes =
|
||||
DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, keyVal, nullptr);
|
||||
if (!bytes)
|
||||
@@ -126,7 +126,8 @@ WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetWeakMapEntryValue(cx, map, keyVal, placeholder))
|
||||
keyObject = &keyVal.toObject();
|
||||
if (!SetWeakMapEntry(cx, map, keyObject, placeholder))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
||||
+14
-4
@@ -13,6 +13,7 @@
|
||||
#include "jsgc.h"
|
||||
|
||||
#include "gc/Heap.h"
|
||||
#include "gc/IdleGC.h"
|
||||
#include "gc/Nursery.h"
|
||||
#include "gc/Statistics.h"
|
||||
#include "gc/StoreBuffer.h"
|
||||
@@ -650,6 +651,13 @@ class GCRuntime
|
||||
void onOutOfMallocMemory();
|
||||
void onOutOfMallocMemory(const AutoLockGC& lock);
|
||||
|
||||
/* Idle-time GC notifications. */
|
||||
void notifyJSExecutionStart();
|
||||
void notifyJSExecutionEnd();
|
||||
IdleGCManager& idleGCMgr() {
|
||||
return idleGC;
|
||||
}
|
||||
|
||||
size_t maxMallocBytesAllocated() { return maxMallocBytes; }
|
||||
|
||||
uint64_t nextCellUniqueId() {
|
||||
@@ -939,8 +947,6 @@ class GCRuntime
|
||||
IncrementalProgress drainMarkStack(SliceBudget& sliceBudget, gcstats::Phase phase);
|
||||
template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase);
|
||||
void markWeakReferencesInCurrentGroup(gcstats::Phase phase);
|
||||
template <class ZoneIterT> void traceFinalizationRegistryWeakRefs();
|
||||
void traceFinalizationRegistryWeakRefsInCurrentGroup();
|
||||
template <class ZoneIterT, class CompartmentIterT> void markGrayReferences(gcstats::Phase phase);
|
||||
void markBufferedGrayRoots(JS::Zone* zone);
|
||||
void markGrayReferencesInCurrentGroup(gcstats::Phase phase);
|
||||
@@ -953,7 +959,6 @@ class GCRuntime
|
||||
void getNextZoneGroup();
|
||||
void endMarkingZoneGroup();
|
||||
void beginSweepingZoneGroup(AutoLockForExclusiveAccess& lock);
|
||||
void sweepFinalizationRegistries();
|
||||
bool shouldReleaseObservedTypes();
|
||||
void endSweepingZoneGroup();
|
||||
IncrementalProgress performSweepActions(SliceBudget& sliceBudget, AutoLockForExclusiveAccess& lock);
|
||||
@@ -969,6 +974,8 @@ class GCRuntime
|
||||
void sweepZones(FreeOp* fop, bool lastGC);
|
||||
void decommitAllWithoutUnlocking(const AutoLockGC& lock);
|
||||
void startDecommit();
|
||||
bool sweepBackgroundFinalizePhaseInParallel(ZoneList& zones, const FinalizePhase& phase,
|
||||
Arena** emptyArenas);
|
||||
void queueZonesForBackgroundSweep(ZoneList& zones);
|
||||
void sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks);
|
||||
void assertBackgroundSweepingFinished();
|
||||
@@ -982,7 +989,7 @@ class GCRuntime
|
||||
[[nodiscard]] bool relocateArenas(Zone* zone, JS::gcreason::Reason reason,
|
||||
Arena*& relocatedListOut, SliceBudget& sliceBudget);
|
||||
void updateTypeDescrObjects(MovingTracer* trc, Zone* zone);
|
||||
void updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, size_t bgTaskCount);
|
||||
void updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds);
|
||||
void updateAllCellPointers(MovingTracer* trc, Zone* zone);
|
||||
void updatePointersToRelocatedCells(Zone* zone, AutoLockForExclusiveAccess& lock);
|
||||
void protectAndHoldArenas(Arena* arenaList);
|
||||
@@ -1022,6 +1029,9 @@ class GCRuntime
|
||||
GCSchedulingTunables tunables;
|
||||
GCSchedulingState schedulingState;
|
||||
|
||||
/* Idle-time garbage collection manager. */
|
||||
IdleGCManager idleGC;
|
||||
|
||||
MemProfiler mMemProfiler;
|
||||
|
||||
private:
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* 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/. */
|
||||
|
||||
/**
|
||||
* IDLE-TIME GARBAGE COLLECTION SYSTEM
|
||||
*
|
||||
* Overview
|
||||
* --------
|
||||
* This system implements idle-time-only garbage collection, deferring GC work
|
||||
* to periods when the JavaScript engine is not actively processing code.
|
||||
* This improves perceived responsiveness by avoiding GC pauses during critical
|
||||
* execution windows.
|
||||
*
|
||||
* Architecture
|
||||
* -----------
|
||||
*
|
||||
* The system consists of several key components:
|
||||
*
|
||||
* 1. IdleGCManager (gc/IdleGC.h, gc/IdleGC.cpp)
|
||||
* - Tracks when the JS engine is executing vs. idle
|
||||
* - Maintains timestamp of last execution activity
|
||||
* - Checks if sufficient idle time has passed
|
||||
* - Configurable idle threshold (default: 100ms)
|
||||
* - Can determine which GC reasons should bypass idle checks
|
||||
*
|
||||
* 2. GCRuntime Integration (gc/GCRuntime.h)
|
||||
* - Contains an IdleGCManager instance
|
||||
* - Provides public methods: notifyJSExecutionStart/End()
|
||||
* - Modified checkIfGCAllowedInCurrentState() to check idle status
|
||||
*
|
||||
* 3. Activity Tracking (vm/Runtime.cpp)
|
||||
* - triggerActivityCallback() now notifies IdleGCManager
|
||||
* - Called when JS enters/exits request (execution boundary)
|
||||
* - Updated in jsapi.cpp's StartRequest/StopRequest functions
|
||||
*
|
||||
* 4. Public API (jsapi.h, jsapi.cpp)
|
||||
* - JS_SetIdleGCEnabled() / JS_IsIdleGCEnabled()
|
||||
* - JS_SetIdleGCThreshold() / JS_GetIdleGCThreshold()
|
||||
* - JS_GetIdleTimeSinceLastExecution()
|
||||
* - Allows embedders to configure idle GC behavior
|
||||
*
|
||||
* Behavior
|
||||
* --------
|
||||
*
|
||||
* Normal GC Trigger (Idle GC Enabled):
|
||||
*
|
||||
* 1. JS code executes → notifyJSExecutionStart() called
|
||||
* 2. JS code finishes → notifyJSExecutionEnd() called, timestamp recorded
|
||||
* 3. GC needed → checkIfGCAllowedInCurrentState() checks idle status
|
||||
* 4a. If idle >= threshold → GC proceeds normally
|
||||
* 4b. If still executing/idle < threshold → GC is deferred
|
||||
* 5. Once idle threshold is met, next GC request proceeds
|
||||
*
|
||||
* Critical GC Triggers (Always Proceed):
|
||||
*
|
||||
* The following GC reasons bypass idle checking:
|
||||
* - OUT_OF_MEMORY: Memory pressure conditions
|
||||
* - ALLOC_TRIGGER: Allocation threshold exceeded
|
||||
* - MALLOC_PRESSURE: Malloc pressure from OS
|
||||
* - EAGER_ALLOC_TRIGGER: Eager allocation trigger
|
||||
* - API: Explicit JS API calls
|
||||
* - DETERMINISTIC: Deterministic tests
|
||||
* - EVICT_NURSERY: Nursery eviction
|
||||
* - SHUTDOWN_CC, DESTROY_RUNTIME, LAST_DITCH: Shutdown GCs
|
||||
* - DESTROY_ZONE, COMPARTMENT_REVOKED: Zone/compartment destruction
|
||||
*
|
||||
* Configuration
|
||||
* -------------
|
||||
*
|
||||
* Idle GC Enabled (default: true):
|
||||
* - Enables idle-time-only GC mode
|
||||
* - Can be toggled dynamically via JS_SetIdleGCEnabled()
|
||||
*
|
||||
* Idle Threshold (default: 100ms):
|
||||
* - Minimum idle time before GC is permitted
|
||||
* - Configurable via JS_SetIdleGCThreshold(ms)
|
||||
* - Typical values: 50-200ms depending on application
|
||||
*
|
||||
* Integration Examples
|
||||
* --------------------
|
||||
*
|
||||
* Browser Integration:
|
||||
*
|
||||
* // When browser loads configuration
|
||||
* JS_SetIdleGCEnabled(cx, true);
|
||||
* JS_SetIdleGCThreshold(cx, 100); // 100ms idle threshold
|
||||
*
|
||||
* // Monitor idle GC effectiveness (optional)
|
||||
* uint64_t idleTime = JS_GetIdleTimeSinceLastExecution(cx);
|
||||
* if (idleTime > JS_GetIdleGCThreshold(cx)) {
|
||||
* // System is idle, GC would be allowed if triggered
|
||||
* }
|
||||
*
|
||||
* Disabling for Specific Scenarios:
|
||||
*
|
||||
* // During initialization when nothing is "idle" yet
|
||||
* JS_SetIdleGCEnabled(cx, false);
|
||||
* // ... do initial setup ...
|
||||
* JS_SetIdleGCEnabled(cx, true); // Re-enable for normal operation
|
||||
*
|
||||
* Performance Considerations
|
||||
* --------------------------
|
||||
*
|
||||
* Benefits:
|
||||
* - Reduced jank during active JS execution
|
||||
* - GC pauses moved to idle periods where users won't notice
|
||||
* - Especially effective for interactive applications
|
||||
* - Improves Time-to-Interactive and First Input Delay metrics
|
||||
*
|
||||
* Tradeoffs:
|
||||
* - May accumulate more garbage before collection
|
||||
* - Requires predictable idle periods (not suitable for all workloads)
|
||||
* - Critical memory pressure GCs still proceed immediately
|
||||
*
|
||||
* Tuning:
|
||||
* - Lower threshold (50ms) = more frequent GC, less memory overhead
|
||||
* - Higher threshold (200ms) = less GC overhead, more memory usage
|
||||
* - Optimal value depends on application's execution pattern
|
||||
*
|
||||
* Testing
|
||||
* -------
|
||||
*
|
||||
* Unit Tests:
|
||||
* // Test idle detection
|
||||
* JS_SetIdleGCThreshold(cx, 100);
|
||||
* // Simulate JS execution
|
||||
* cx->runtime()->gc.notifyJSExecutionStart();
|
||||
* // ... wait 50ms ...
|
||||
* MOZ_ASSERT(!cx->runtime()->gc.idleGCMgr().isIdleEnough());
|
||||
* cx->runtime()->gc.notifyJSExecutionEnd();
|
||||
* // ... wait 150ms ...
|
||||
* MOZ_ASSERT(cx->runtime()->gc.idleGCMgr().isIdleEnough());
|
||||
*
|
||||
* Integration Tests:
|
||||
* - Verify GC is deferred during active execution
|
||||
* - Verify GC proceeds after idle period
|
||||
* - Verify critical GC reasons bypass idle check
|
||||
* - Measure latency improvements
|
||||
*
|
||||
* Implementation Notes
|
||||
* --------------------
|
||||
*
|
||||
* Thread Safety:
|
||||
* - IdleGCManager uses mozilla::Atomic for thread-safe state
|
||||
* - TimeStamp operations are atomic
|
||||
* - No additional locking needed beyond existing GC locks
|
||||
*
|
||||
* Compatibility:
|
||||
* - Works with both incremental and non-incremental GC
|
||||
* - Compatible with generational GC
|
||||
* - Works with zone GC and full GC
|
||||
* - Respects existing GC suppression mechanisms
|
||||
*
|
||||
* Future Enhancements
|
||||
* -------------------
|
||||
*
|
||||
* Potential improvements:
|
||||
* - Adaptive idle threshold based on historical GC times
|
||||
* - Per-zone idle configuration
|
||||
* - Integration with browser rendering idle callback API
|
||||
* - Metrics/telemetry for idle GC effectiveness
|
||||
* - Machine learning-based prediction of idle periods
|
||||
* - Cooperative GC scheduling with other subsystems
|
||||
*
|
||||
*/
|
||||
|
||||
#include "gc/IdleGC.h"
|
||||
#include "jsapi.h"
|
||||
|
||||
namespace js {
|
||||
namespace gc {
|
||||
|
||||
IdleGCManager::IdleGCManager()
|
||||
: lastExecutionTime_(mozilla::TimeStamp::Now()),
|
||||
idleGCEnabled_(true),
|
||||
idleThresholdMs_(100), // 100ms default idle threshold
|
||||
isExecuting_(false)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
IdleGCManager::notifyJSExecutionStart()
|
||||
{
|
||||
isExecuting_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
IdleGCManager::notifyJSExecutionEnd()
|
||||
{
|
||||
isExecuting_ = false;
|
||||
lastExecutionTime_ = mozilla::TimeStamp::Now();
|
||||
}
|
||||
|
||||
bool
|
||||
IdleGCManager::isIdleEnough() const
|
||||
{
|
||||
if (!idleGCEnabled_) {
|
||||
return true; // If disabled, always consider idle
|
||||
}
|
||||
|
||||
if (isExecuting_) {
|
||||
return false; // Still executing, not idle
|
||||
}
|
||||
|
||||
uint64_t idleTime = idleTimeSinceLastExecution();
|
||||
return idleTime >= idleThresholdMs_;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
IdleGCManager::idleTimeSinceLastExecution() const
|
||||
{
|
||||
mozilla::TimeStamp now = mozilla::TimeStamp::Now();
|
||||
mozilla::TimeDuration idle = now - lastExecutionTime_;
|
||||
return idle.ToMilliseconds();
|
||||
}
|
||||
|
||||
bool
|
||||
IdleGCManager::shouldBypassIdleCheck(JS::gcreason::Reason reason)
|
||||
{
|
||||
// These reasons indicate urgent GC needs that should bypass idle checking
|
||||
switch (reason) {
|
||||
// Allocation and memory pressure conditions.
|
||||
case JS::gcreason::ALLOC_TRIGGER:
|
||||
case JS::gcreason::EAGER_ALLOC_TRIGGER:
|
||||
case JS::gcreason::TOO_MUCH_MALLOC:
|
||||
case JS::gcreason::MEM_PRESSURE:
|
||||
case JS::gcreason::LAST_DITCH:
|
||||
|
||||
// Nursery/store-buffer pressure.
|
||||
case JS::gcreason::OUT_OF_NURSERY:
|
||||
case JS::gcreason::EVICT_NURSERY:
|
||||
case JS::gcreason::FULL_STORE_BUFFER:
|
||||
case JS::gcreason::SHARED_MEMORY_LIMIT:
|
||||
|
||||
// Explicit API calls
|
||||
case JS::gcreason::API:
|
||||
case JS::gcreason::ABORT_GC:
|
||||
|
||||
// Shutdown and finalization
|
||||
case JS::gcreason::SHUTDOWN_CC:
|
||||
case JS::gcreason::DESTROY_RUNTIME:
|
||||
case JS::gcreason::NSJSCONTEXT_DESTROY:
|
||||
case JS::gcreason::XPCONNECT_SHUTDOWN:
|
||||
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
IdleGCManager::reset()
|
||||
{
|
||||
lastExecutionTime_ = mozilla::TimeStamp::Now();
|
||||
isExecuting_ = false;
|
||||
}
|
||||
|
||||
} // namespace gc
|
||||
} // namespace js
|
||||
@@ -0,0 +1,118 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* 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 gc_IdleGC_h
|
||||
#define gc_IdleGC_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "js/GCAPI.h"
|
||||
|
||||
namespace js {
|
||||
namespace gc {
|
||||
|
||||
/*
|
||||
* Idle-Time Garbage Collection System
|
||||
* ====================================
|
||||
*
|
||||
* This system defers garbage collection to occur only during periods when
|
||||
* the browser is not actively processing JavaScript. This helps maintain
|
||||
* responsiveness by avoiding GC pauses during critical execution windows.
|
||||
*
|
||||
* When JavaScript execution is active, GC triggers are deferred. Once the
|
||||
* JS engine has been idle for a configurable threshold period, pending GC
|
||||
* work is performed immediately or incrementally as appropriate.
|
||||
*
|
||||
* Key characteristics:
|
||||
* - Tracks JavaScript activity via hooks in the execution engine
|
||||
* - Configurable idle time threshold (default: 100ms)
|
||||
* - Can be disabled per-zone or globally
|
||||
* - Works with both incremental and non-incremental GC modes
|
||||
* - Respects critical GC reasons that override idle checking
|
||||
*/
|
||||
|
||||
class IdleGCManager
|
||||
{
|
||||
public:
|
||||
// Initialize the idle GC manager
|
||||
IdleGCManager();
|
||||
|
||||
/*
|
||||
* Called when JavaScript execution begins. Marks the engine as active.
|
||||
*/
|
||||
void notifyJSExecutionStart();
|
||||
|
||||
/*
|
||||
* Called when JavaScript execution ends. Records the end time for
|
||||
* idle detection purposes.
|
||||
*/
|
||||
void notifyJSExecutionEnd();
|
||||
|
||||
/*
|
||||
* Check if the system has been idle for long enough to permit GC.
|
||||
* Returns true if sufficient idle time has passed since last JS execution.
|
||||
*/
|
||||
bool isIdleEnough() const;
|
||||
|
||||
/*
|
||||
* Get the amount of idle time since the last JS execution.
|
||||
* Returns time in milliseconds.
|
||||
*/
|
||||
uint64_t idleTimeSinceLastExecution() const;
|
||||
|
||||
/*
|
||||
* Set the idle threshold - minimum idle time before GC is permitted.
|
||||
* Time is in milliseconds. Default is 100ms.
|
||||
*/
|
||||
void setIdleThresholdMs(uint64_t thresholdMs) {
|
||||
idleThresholdMs_ = thresholdMs;
|
||||
}
|
||||
|
||||
uint64_t idleThresholdMs() const {
|
||||
return idleThresholdMs_;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable or disable idle-time-only GC mode.
|
||||
*/
|
||||
void setIdleGCEnabled(bool enabled) {
|
||||
idleGCEnabled_ = enabled;
|
||||
}
|
||||
|
||||
bool isIdleGCEnabled() const {
|
||||
return idleGCEnabled_;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a GC reason should bypass idle checking.
|
||||
* Critical reasons (OOM-like pressure, nursery pressure, explicit requests)
|
||||
* always proceed.
|
||||
*/
|
||||
static bool shouldBypassIdleCheck(JS::gcreason::Reason reason);
|
||||
|
||||
/*
|
||||
* Reset idle tracking state (used during GC or at shutdown).
|
||||
*/
|
||||
void reset();
|
||||
|
||||
private:
|
||||
// Timestamp of the last JavaScript execution activity
|
||||
mozilla::TimeStamp lastExecutionTime_;
|
||||
|
||||
// Whether idle-time-only GC mode is enabled
|
||||
mozilla::Atomic<bool, mozilla::ReleaseAcquire> idleGCEnabled_;
|
||||
|
||||
// Minimum idle time (in milliseconds) before GC is permitted
|
||||
mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire> idleThresholdMs_;
|
||||
|
||||
// Whether the JS engine is currently executing
|
||||
mozilla::Atomic<bool, mozilla::ReleaseAcquire> isExecuting_;
|
||||
};
|
||||
|
||||
} // namespace gc
|
||||
} // namespace js
|
||||
|
||||
#endif // gc_IdleGC_h
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "ds/SplayTree.h"
|
||||
#include "gc/Barrier.h"
|
||||
#include "gc/FindSCCs.h"
|
||||
#include "gc/GCRuntime.h"
|
||||
#include "js/GCHashTable.h"
|
||||
@@ -326,10 +325,6 @@ struct Zone : public JS::shadow::Zone,
|
||||
using WeakEdges = js::Vector<js::gc::TenuredCell**, 0, js::SystemAllocPolicy>;
|
||||
WeakEdges gcWeakRefs;
|
||||
|
||||
// FinalizationRegistry objects with weakly-held target cells in this zone.
|
||||
using FinalizationRegistryVector = js::Vector<js::WeakRef<JSObject*>, 0, js::SystemAllocPolicy>;
|
||||
FinalizationRegistryVector finalizationRegistries;
|
||||
|
||||
// List of non-ephemeron weak containers to sweep during beginSweepingZoneGroup.
|
||||
mozilla::LinkedList<WeakCache<void*>> weakCaches_;
|
||||
void registerWeakCache(WeakCache<void*>* cachep) {
|
||||
|
||||
Loaded 100 of 746 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user