зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to central, a=merge
This commit is contained in:
Коммит
ac656a7d21
|
@ -74,3 +74,15 @@ mobile/android/gradle/.gradle
|
|||
|
||||
# Ignore node_modules from eslint-plugin-mozilla
|
||||
testing/eslint-plugin-mozilla/node_modules/
|
||||
|
||||
# Ignore talos virtualenv and tp5n files.
|
||||
# The tp5n set is supposed to be decompressed at
|
||||
# testing/talos/talos/page_load_test/tp5n in order to run tests like tps
|
||||
# locally. Similarly, running talos requires a Python package virtual
|
||||
# environment. Both the virtual environment and tp5n files end up littering
|
||||
# the status command, so we ignore them.
|
||||
testing/talos/.Python
|
||||
testing/talos/bin/
|
||||
testing/talos/include/
|
||||
testing/talos/lib/
|
||||
testing/talos/talos/page_load_test/tp5n/
|
||||
|
|
12
.hgignore
12
.hgignore
|
@ -96,3 +96,15 @@ GPATH
|
|||
|
||||
# Ignore node_modules from eslint-plugin-mozilla
|
||||
^testing/eslint-plugin-mozilla/node_modules/
|
||||
|
||||
# Ignore talos virtualenv and tp5n files.
|
||||
# The tp5n set is supposed to be decompressed at
|
||||
# testing/talos/talos/page_load_test/tp5n in order to run tests like tps
|
||||
# locally. Similarly, running talos requires a Python package virtual
|
||||
# environment. Both the virtual environment and tp5n files end up littering
|
||||
# the status command, so we ignore them.
|
||||
^testing/talos/.Python
|
||||
^testing/talos/bin/
|
||||
^testing/talos/include/
|
||||
^testing/talos/lib/
|
||||
^testing/talos/talos/page_load_test/tp5n/
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
border-radius: 2px;
|
||||
box-shadow: 1px 1px 1px #444;
|
||||
display: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#virtual-cursor-box.show {
|
||||
|
|
|
@ -522,7 +522,9 @@ var Output = {
|
|||
|
||||
stop: function stop() {
|
||||
if (this.highlightBox) {
|
||||
Utils.win.document.documentElement.removeChild(this.highlightBox.get());
|
||||
let doc = Utils.win.document;
|
||||
(doc.body || doc.documentElement).documentElement.removeChild(
|
||||
this.highlightBox.get());
|
||||
delete this.highlightBox;
|
||||
}
|
||||
},
|
||||
|
@ -538,16 +540,17 @@ var Output = {
|
|||
{
|
||||
let highlightBox = null;
|
||||
if (!this.highlightBox) {
|
||||
let doc = Utils.win.document;
|
||||
// Add highlight box
|
||||
highlightBox = Utils.win.document.
|
||||
createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
Utils.win.document.documentElement.appendChild(highlightBox);
|
||||
let parent = doc.body || doc.documentElement;
|
||||
parent.appendChild(highlightBox);
|
||||
highlightBox.id = 'virtual-cursor-box';
|
||||
|
||||
// Add highlight inset for inner shadow
|
||||
highlightBox.appendChild(
|
||||
Utils.win.document.createElementNS(
|
||||
'http://www.w3.org/1999/xhtml', 'div'));
|
||||
doc.createElementNS('http://www.w3.org/1999/xhtml', 'div'));
|
||||
|
||||
this.highlightBox = Cu.getWeakReference(highlightBox);
|
||||
} else {
|
||||
|
|
|
@ -20,11 +20,11 @@ MOZ_ARG_WITH_STRING(android-gnu-compiler-version,
|
|||
gnu compiler version to use],
|
||||
android_gnu_compiler_version=$withval)
|
||||
|
||||
MOZ_ARG_ENABLE_BOOL(android-libstdcxx,
|
||||
[ --enable-android-libstdcxx
|
||||
use GNU libstdc++ instead of STLPort],
|
||||
MOZ_ANDROID_LIBSTDCXX=1,
|
||||
MOZ_ANDROID_LIBSTDCXX= )
|
||||
MOZ_ARG_WITH_STRING(android-cxx-stl,
|
||||
[ --with-android-cxx-stl=VALUE
|
||||
use the specified C++ STL (stlport, libstdc++, libc++)],
|
||||
android_cxx_stl=$withval,
|
||||
android_cxx_stl=mozstlport)
|
||||
|
||||
define([MIN_ANDROID_VERSION], [9])
|
||||
android_version=MIN_ANDROID_VERSION
|
||||
|
@ -210,26 +210,60 @@ if test "$OS_TARGET" = "Android" -a -z "$gonkdir"; then
|
|||
|
||||
AC_SUBST(ANDROID_CPU_ARCH)
|
||||
|
||||
cpu_arch_dir="$ANDROID_CPU_ARCH"
|
||||
if test "$MOZ_THUMB2" = 1; then
|
||||
cpu_arch_dir="$cpu_arch_dir/thumb"
|
||||
fi
|
||||
|
||||
if test -z "$STLPORT_CPPFLAGS$STLPORT_LIBS"; then
|
||||
if test -n "$MOZ_ANDROID_LIBSTDCXX" ; then
|
||||
case "$android_cxx_stl" in
|
||||
libstdc++)
|
||||
# android-ndk-r8b and later
|
||||
ndk_base="$android_ndk/sources/cxx-stl/gnu-libstdc++/$android_gnu_compiler_version"
|
||||
ndk_libs="$ndk_base/libs/$ANDROID_CPU_ARCH"
|
||||
ndk_libs_include="$ndk_base/libs/$ANDROID_CPU_ARCH"
|
||||
ndk_libs="$ndk_base/libs/$cpu_arch_dir"
|
||||
ndk_include="$ndk_base/include"
|
||||
|
||||
if test -e "$ndk_libs/libgnustl_static.a"; then
|
||||
STLPORT_LIBS="-L$ndk_libs -lgnustl_static"
|
||||
STLPORT_CPPFLAGS="-I$ndk_include -I$ndk_include/backward -I$ndk_libs/include"
|
||||
else
|
||||
if ! test -e "$ndk_libs/libgnustl_static.a"; then
|
||||
AC_MSG_ERROR([Couldn't find path to gnu-libstdc++ in the android ndk])
|
||||
fi
|
||||
else
|
||||
|
||||
STLPORT_LIBS="-L$ndk_libs -lgnustl_static"
|
||||
STLPORT_CPPFLAGS="-I$ndk_include -I$ndk_include/backward -I$ndk_libs_include/include"
|
||||
;;
|
||||
libc++)
|
||||
# android-ndk-r8b and later
|
||||
ndk_base="$android_ndk/sources/cxx-stl"
|
||||
cxx_base="$ndk_base/llvm-libc++"
|
||||
cxx_libs="$cxx_base/libs/$cpu_arch_dir"
|
||||
cxx_include="$cxx_base/libcxx/include"
|
||||
cxxabi_base="$ndk_base/llvm-libc++abi"
|
||||
cxxabi_include="$cxxabi_base/libcxxabi/include"
|
||||
|
||||
if ! test -e "$cxx_libs/libc++_static.a"; then
|
||||
AC_MSG_ERROR([Couldn't find path to llvm-libc++ in the android ndk])
|
||||
fi
|
||||
|
||||
STLPORT_LIBS="-L$cxx_libs -lc++_static"
|
||||
# Add android/support/include/ for prototyping long double math
|
||||
# functions, locale-specific C library functions, multibyte support,
|
||||
# etc.
|
||||
STLPORT_CPPFLAGS="-I$android_ndk/sources/android/support/include -I$cxx_include -I$cxxabi_include"
|
||||
;;
|
||||
mozstlport)
|
||||
# We don't need to set STLPORT_LIBS, because the build system will
|
||||
# take care of linking in our home-built stlport where it is needed.
|
||||
STLPORT_CPPFLAGS="-isystem $_topsrcdir/build/stlport/stlport -isystem $_topsrcdir/build/stlport/overrides -isystem $android_ndk/sources/cxx-stl/system/include"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([Bad value for --enable-android-cxx-stl])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
CXXFLAGS="$CXXFLAGS $STLPORT_CPPFLAGS"
|
||||
fi
|
||||
AC_SUBST([MOZ_ANDROID_LIBSTDCXX])
|
||||
MOZ_ANDROID_CXX_STL=$android_cxx_stl
|
||||
AC_SUBST([MOZ_ANDROID_CXX_STL])
|
||||
AC_SUBST([STLPORT_LIBS])
|
||||
|
||||
])
|
||||
|
|
|
@ -279,6 +279,8 @@ public:
|
|||
CustomTypeAnnotation(const char *Spelling, const char *Pretty)
|
||||
: Spelling(Spelling), Pretty(Pretty){};
|
||||
|
||||
virtual ~CustomTypeAnnotation() {}
|
||||
|
||||
// Checks if this custom annotation "effectively affects" the given type.
|
||||
bool hasEffectiveAnnotation(QualType T) {
|
||||
return directAnnotationReason(T).valid();
|
||||
|
@ -299,6 +301,10 @@ public:
|
|||
private:
|
||||
bool hasLiteralAnnotation(QualType T) const;
|
||||
AnnotationReason directAnnotationReason(QualType T);
|
||||
|
||||
protected:
|
||||
// Allow subclasses to apply annotations to external code:
|
||||
virtual bool hasFakeAnnotation(const TagDecl *D) const { return false; }
|
||||
};
|
||||
|
||||
static CustomTypeAnnotation StackClass =
|
||||
|
@ -313,8 +319,32 @@ static CustomTypeAnnotation NonTemporaryClass =
|
|||
CustomTypeAnnotation("moz_non_temporary_class", "non-temporary");
|
||||
static CustomTypeAnnotation MustUse =
|
||||
CustomTypeAnnotation("moz_must_use", "must-use");
|
||||
static CustomTypeAnnotation NonMemMovable =
|
||||
CustomTypeAnnotation("moz_non_memmovable", "non-memmove()able");
|
||||
|
||||
class MemMoveAnnotation final : public CustomTypeAnnotation {
|
||||
public:
|
||||
MemMoveAnnotation()
|
||||
: CustomTypeAnnotation("moz_non_memmovable", "non-memmove()able") {}
|
||||
|
||||
virtual ~MemMoveAnnotation() {}
|
||||
|
||||
protected:
|
||||
bool hasFakeAnnotation(const TagDecl *D) const override {
|
||||
// Annotate everything in ::std, with a few exceptions; see bug
|
||||
// 1201314 for discussion.
|
||||
if (getDeclarationNamespace(D) == "std") {
|
||||
// This doesn't check that it's really ::std::pair and not
|
||||
// ::std::something_else::pair, but should be good enough.
|
||||
StringRef Name = D->getName();
|
||||
if (Name == "pair" || Name == "atomic" || Name == "__atomic_base") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
static MemMoveAnnotation NonMemMovable = MemMoveAnnotation();
|
||||
|
||||
class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> {
|
||||
DiagnosticsEngine &Diag;
|
||||
|
@ -768,7 +798,7 @@ bool CustomTypeAnnotation::hasLiteralAnnotation(QualType T) const {
|
|||
#else
|
||||
if (const CXXRecordDecl *D = T->getAsCXXRecordDecl()) {
|
||||
#endif
|
||||
return MozChecker::hasCustomAnnotation(D, Spelling);
|
||||
return hasFakeAnnotation(D) || MozChecker::hasCustomAnnotation(D, Spelling);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
#define MOZ_NEEDS_MEMMOVABLE_TYPE __attribute__((annotate("moz_needs_memmovable_type")))
|
||||
|
||||
template<class T>
|
||||
class MOZ_NEEDS_MEMMOVABLE_TYPE Mover { T mForceInst; }; // expected-error-re 4 {{Cannot instantiate 'Mover<{{.*}}>' with non-memmovable template argument '{{.*}}'}}
|
||||
|
||||
namespace std {
|
||||
// In theory defining things in std:: like this invokes undefined
|
||||
// behavior, but in practice it's good enough for this test case.
|
||||
template<class C> class basic_string { };
|
||||
typedef basic_string<char> string;
|
||||
template<class T, class U> class pair { T mT; U mU; }; // expected-note {{std::pair<bool, std::basic_string<char> >' is a non-memmove()able type because member 'mU' is a non-memmove()able type 'std::basic_string<char>'}}
|
||||
class arbitrary_name { };
|
||||
}
|
||||
|
||||
class HasString { std::string m; }; // expected-note {{'HasString' is a non-memmove()able type because member 'm' is a non-memmove()able type 'std::string' (aka 'basic_string<char>')}}
|
||||
|
||||
static Mover<std::string> bad; // expected-note {{instantiation of 'Mover<std::basic_string<char> >' requested here}}
|
||||
static Mover<HasString> bad_mem; // expected-note {{instantiation of 'Mover<HasString>' requested here}}
|
||||
static Mover<std::arbitrary_name> assumed_bad; // expected-note {{instantiation of 'Mover<std::arbitrary_name>' requested here}}
|
||||
static Mover<std::pair<bool, int>> good;
|
||||
static Mover<std::pair<bool, std::string>> not_good; // expected-note {{instantiation of 'Mover<std::pair<bool, std::basic_string<char> > >' requested here}}
|
|
@ -24,6 +24,7 @@ SOURCES += [
|
|||
'TestNoExplicitMoveConstructor.cpp',
|
||||
'TestNonHeapClass.cpp',
|
||||
'TestNonMemMovable.cpp',
|
||||
'TestNonMemMovableStd.cpp',
|
||||
'TestNonTemporaryClass.cpp',
|
||||
'TestNoRefcountedInsideLambdas.cpp',
|
||||
'TestRefCountedCopyConstructor.cpp',
|
||||
|
|
|
@ -15,7 +15,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
|||
else:
|
||||
DIRS += ['unix']
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'Android' and not CONFIG['MOZ_ANDROID_LIBSTDCXX']:
|
||||
if CONFIG['OS_TARGET'] == 'Android' and CONFIG['MOZ_ANDROID_CXX_STL'] == 'mozstlport':
|
||||
DIRS += ['stlport']
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
||||
|
|
|
@ -21,7 +21,8 @@ def Binary():
|
|||
if CONFIG['STLPORT_LIBS']:
|
||||
OS_LIBS += [CONFIG['STLPORT_LIBS']]
|
||||
elif CONFIG['OS_TARGET'] == 'Android':
|
||||
USE_LIBS += ['stlport']
|
||||
if CONFIG['MOZ_ANDROID_CXX_STL'] == 'mozstlport':
|
||||
USE_LIBS += ['stlport']
|
||||
|
||||
|
||||
@template
|
||||
|
|
|
@ -318,6 +318,7 @@ if test -n "$gonkdir" ; then
|
|||
AC_DEFINE(HAVE_PTHREADS)
|
||||
MOZ_CHROME_FILE_FORMAT=omni
|
||||
direct_nspr_config=1
|
||||
android_cxx_stl=mozstlport
|
||||
else
|
||||
MOZ_ANDROID_NDK
|
||||
|
||||
|
|
|
@ -419,16 +419,26 @@ GetDataInfo(const nsACString& aUri)
|
|||
}
|
||||
|
||||
DataInfo* res;
|
||||
nsCString uriIgnoringRef;
|
||||
int32_t hashPos = aUri.FindChar('#');
|
||||
if (hashPos < 0) {
|
||||
uriIgnoringRef = aUri;
|
||||
|
||||
// Let's remove any fragment and query from this URI.
|
||||
int32_t hasFragmentPos = aUri.FindChar('#');
|
||||
int32_t hasQueryPos = aUri.FindChar('?');
|
||||
|
||||
int32_t pos = -1;
|
||||
if (hasFragmentPos >= 0 && hasQueryPos >= 0) {
|
||||
pos = std::min(hasFragmentPos, hasQueryPos);
|
||||
} else if (hasFragmentPos >= 0) {
|
||||
pos = hasFragmentPos;
|
||||
} else {
|
||||
pos = hasQueryPos;
|
||||
}
|
||||
else {
|
||||
uriIgnoringRef = StringHead(aUri, hashPos);
|
||||
|
||||
if (pos < 0) {
|
||||
gDataTable->Get(aUri, &res);
|
||||
} else {
|
||||
gDataTable->Get(StringHead(aUri, pos), &res);
|
||||
}
|
||||
gDataTable->Get(uriIgnoringRef, &res);
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -383,6 +383,7 @@ skip-if = buildapp == 'b2g' || (android_version == '18' && debug) # b2g(flaky on
|
|||
support-files = test_XHR_timeout.js
|
||||
[test_base.xhtml]
|
||||
[test_blobconstructor.html]
|
||||
[test_blob_fragment_and_query.html]
|
||||
[test_bug166235.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined)
|
||||
[test_bug199959.html]
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Blob URI with fragments</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
var blob = new Blob(['hello world']);
|
||||
ok(blob, "We have a blob.");
|
||||
|
||||
var url = URL.createObjectURL(blob);
|
||||
ok(url, "We have a URI");
|
||||
|
||||
var tests = [
|
||||
url,
|
||||
url + "?aa",
|
||||
url + "#bb",
|
||||
url + "?cc#dd",
|
||||
url + "#ee?ff",
|
||||
];
|
||||
|
||||
function runTest() {
|
||||
if (!tests.length) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var test = tests.shift();
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', test);
|
||||
xhr.onload = function() {
|
||||
is(xhr.responseText, 'hello world', 'URL: ' + test);
|
||||
runTest();
|
||||
}
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -2758,17 +2758,12 @@ nsresult HTMLMediaElement::InitializeDecoderAsClone(MediaDecoder* aOriginal)
|
|||
MediaResource* originalResource = aOriginal->GetResource();
|
||||
if (!originalResource)
|
||||
return NS_ERROR_FAILURE;
|
||||
nsRefPtr<MediaDecoder> decoder = aOriginal->Clone();
|
||||
nsRefPtr<MediaDecoder> decoder = aOriginal->Clone(this);
|
||||
if (!decoder)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
LOG(LogLevel::Debug, ("%p Cloned decoder %p from %p", this, decoder.get(), aOriginal));
|
||||
|
||||
if (!decoder->Init(this)) {
|
||||
LOG(LogLevel::Debug, ("%p Failed to init cloned decoder %p", this, decoder.get()));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
decoder->SetMediaSeekable(aOriginal->IsMediaSeekable());
|
||||
|
||||
nsRefPtr<MediaResource> resource = originalResource->CloneData(decoder);
|
||||
|
|
|
@ -586,33 +586,33 @@ InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
|
|||
|
||||
#ifdef MOZ_FMP4
|
||||
if (IsMP4SupportedType(aType)) {
|
||||
decoder = new MP4Decoder();
|
||||
decoder = new MP4Decoder(aOwner);
|
||||
return decoder.forget();
|
||||
}
|
||||
#endif
|
||||
if (IsMP3SupportedType(aType)) {
|
||||
decoder = new MP3Decoder();
|
||||
decoder = new MP3Decoder(aOwner);
|
||||
return decoder.forget();
|
||||
}
|
||||
#ifdef MOZ_GSTREAMER
|
||||
if (IsGStreamerSupportedType(aType)) {
|
||||
decoder = new GStreamerDecoder();
|
||||
decoder = new GStreamerDecoder(aOwner);
|
||||
return decoder.forget();
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_RAW
|
||||
if (IsRawType(aType)) {
|
||||
decoder = new RawDecoder();
|
||||
decoder = new RawDecoder(aOwner);
|
||||
return decoder.forget();
|
||||
}
|
||||
#endif
|
||||
if (IsOggType(aType)) {
|
||||
decoder = new OggDecoder();
|
||||
decoder = new OggDecoder(aOwner);
|
||||
return decoder.forget();
|
||||
}
|
||||
#ifdef MOZ_WAVE
|
||||
if (IsWaveType(aType)) {
|
||||
decoder = new WaveDecoder();
|
||||
decoder = new WaveDecoder(aOwner);
|
||||
return decoder.forget();
|
||||
}
|
||||
#endif
|
||||
|
@ -635,10 +635,10 @@ InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
|
|||
}
|
||||
#if ANDROID_VERSION >= 18
|
||||
decoder = MediaDecoder::IsOmxAsyncEnabled()
|
||||
? static_cast<MediaDecoder*>(new MediaCodecDecoder())
|
||||
: static_cast<MediaDecoder*>(new MediaOmxDecoder());
|
||||
? static_cast<MediaDecoder*>(new MediaCodecDecoder(aOwner))
|
||||
: static_cast<MediaDecoder*>(new MediaOmxDecoder(aOwner));
|
||||
#else
|
||||
decoder = new MediaOmxDecoder();
|
||||
decoder = new MediaOmxDecoder(aOwner);
|
||||
#endif
|
||||
return decoder.forget();
|
||||
}
|
||||
|
@ -647,10 +647,10 @@ InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
|
|||
if (IsRtspSupportedType(aType)) {
|
||||
#if ANDROID_VERSION >= 18
|
||||
decoder = MediaDecoder::IsOmxAsyncEnabled()
|
||||
? static_cast<MediaDecoder*>(new RtspMediaCodecDecoder())
|
||||
: static_cast<MediaDecoder*>(new RtspOmxDecoder());
|
||||
? static_cast<MediaDecoder*>(new RtspMediaCodecDecoder(aOwner))
|
||||
: static_cast<MediaDecoder*>(new RtspOmxDecoder(aOwner));
|
||||
#else
|
||||
decoder = new RtspOmxDecoder();
|
||||
decoder = new RtspOmxDecoder(aOwner);
|
||||
#endif
|
||||
return decoder.forget();
|
||||
}
|
||||
|
@ -658,25 +658,25 @@ InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
|
|||
#ifdef MOZ_ANDROID_OMX
|
||||
if (MediaDecoder::IsAndroidMediaEnabled() &&
|
||||
EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
|
||||
decoder = new AndroidMediaDecoder(aType);
|
||||
decoder = new AndroidMediaDecoder(aOwner, aType);
|
||||
return decoder.forget();
|
||||
}
|
||||
#endif
|
||||
if (DecoderTraits::IsWebMType(aType)) {
|
||||
decoder = new WebMDecoder();
|
||||
decoder = new WebMDecoder(aOwner);
|
||||
return decoder.forget();
|
||||
}
|
||||
#ifdef MOZ_DIRECTSHOW
|
||||
// Note: DirectShow should come before WMF, so that we prefer DirectShow's
|
||||
// MP3 support over WMF's.
|
||||
if (IsDirectShowSupportedType(aType)) {
|
||||
decoder = new DirectShowDecoder();
|
||||
decoder = new DirectShowDecoder(aOwner);
|
||||
return decoder.forget();
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_APPLEMEDIA
|
||||
if (IsAppleMediaSupportedType(aType)) {
|
||||
decoder = new AppleDecoder();
|
||||
decoder = new AppleDecoder(aOwner);
|
||||
return decoder.forget();
|
||||
}
|
||||
#endif
|
||||
|
@ -689,11 +689,7 @@ already_AddRefed<MediaDecoder>
|
|||
DecoderTraits::CreateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<MediaDecoder> decoder(InstantiateDecoder(aType, aOwner));
|
||||
NS_ENSURE_TRUE(decoder != nullptr, nullptr);
|
||||
NS_ENSURE_TRUE(decoder->Init(aOwner), nullptr);
|
||||
|
||||
return decoder.forget();
|
||||
return InstantiateDecoder(aType, aOwner);
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
namespace mozilla {
|
||||
|
||||
MediaDecoder*
|
||||
MP3Decoder::Clone() {
|
||||
MP3Decoder::Clone(MediaDecoderOwner* aOwner) {
|
||||
if (!IsEnabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new MP3Decoder();
|
||||
return new MP3Decoder(aOwner);
|
||||
}
|
||||
|
||||
MediaDecoderStateMachine*
|
||||
|
|
|
@ -13,7 +13,8 @@ namespace mozilla {
|
|||
class MP3Decoder : public MediaDecoder {
|
||||
public:
|
||||
// MediaDecoder interface.
|
||||
MediaDecoder* Clone() override;
|
||||
explicit MP3Decoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
|
||||
MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
|
||||
MediaDecoderStateMachine* CreateStateMachine() override;
|
||||
|
||||
// Returns true if the MP3 backend is preffed on, and we're running on a
|
||||
|
|
|
@ -348,7 +348,7 @@ MediaDecoder::IsInfinite()
|
|||
return mInfiniteStream;
|
||||
}
|
||||
|
||||
MediaDecoder::MediaDecoder()
|
||||
MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner)
|
||||
: mWatchManager(this, AbstractThread::MainThread())
|
||||
, mDormantSupported(false)
|
||||
, mLogicalPosition(0.0)
|
||||
|
@ -358,7 +358,8 @@ MediaDecoder::MediaDecoder()
|
|||
#endif
|
||||
, mIgnoreProgressData(false)
|
||||
, mInfiniteStream(false)
|
||||
, mOwner(nullptr)
|
||||
, mOwner(aOwner)
|
||||
, mVideoFrameContainer(aOwner->GetVideoFrameContainer())
|
||||
, mPlaybackStatistics(new MediaChannelStatistics())
|
||||
, mPinnedForSeek(false)
|
||||
, mShuttingDown(false)
|
||||
|
@ -441,16 +442,8 @@ MediaDecoder::MediaDecoder()
|
|||
|
||||
// mIgnoreProgressData
|
||||
mWatchManager.Watch(mLogicallySeeking, &MediaDecoder::SeekingChanged);
|
||||
}
|
||||
|
||||
bool
|
||||
MediaDecoder::Init(MediaDecoderOwner* aOwner)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mOwner = aOwner;
|
||||
mVideoFrameContainer = aOwner->GetVideoFrameContainer();
|
||||
MediaShutdownManager::Instance().Register(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -299,24 +299,18 @@ public:
|
|||
// Must be called exactly once, on the main thread, during startup.
|
||||
static void InitStatics();
|
||||
|
||||
MediaDecoder();
|
||||
explicit MediaDecoder(MediaDecoderOwner* aOwner);
|
||||
|
||||
// Reset the decoder and notify the media element that
|
||||
// server connection is closed.
|
||||
virtual void ResetConnectionState();
|
||||
// Create a new decoder of the same type as this one.
|
||||
// Subclasses must implement this.
|
||||
virtual MediaDecoder* Clone() = 0;
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) = 0;
|
||||
// Create a new state machine to run this decoder.
|
||||
// Subclasses must implement this.
|
||||
virtual MediaDecoderStateMachine* CreateStateMachine() = 0;
|
||||
|
||||
// Call on the main thread only.
|
||||
// Perform any initialization required for the decoder.
|
||||
// Return true on successful initialisation, false
|
||||
// on failure.
|
||||
virtual bool Init(MediaDecoderOwner* aOwner);
|
||||
|
||||
// Cleanup internal data structures. Must be called on the main
|
||||
// thread by the owning object before that object disposes of this object.
|
||||
virtual void Shutdown();
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
AndroidMediaDecoder::AndroidMediaDecoder(const nsACString& aType) : mType(aType)
|
||||
AndroidMediaDecoder::AndroidMediaDecoder(MediaDecoderOwner* aOwner,
|
||||
const nsACString& aType)
|
||||
: MediaDecoder(aOwner), mType(aType)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -15,14 +15,16 @@ class AndroidMediaDecoder : public MediaDecoder
|
|||
{
|
||||
nsCString mType;
|
||||
public:
|
||||
AndroidMediaDecoder(const nsACString& aType);
|
||||
AndroidMediaDecoder(MediaDecoderOwner* aOwner, const nsACString& aType);
|
||||
|
||||
const nsresult GetContentType(nsACString& aType) const {
|
||||
aType = mType;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual MediaDecoder* Clone() { return new AndroidMediaDecoder(mType); }
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) {
|
||||
return new AndroidMediaDecoder(aOwner, mType);
|
||||
}
|
||||
virtual MediaDecoderStateMachine* CreateStateMachine();
|
||||
};
|
||||
|
||||
|
|
|
@ -9,15 +9,15 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
AppleDecoder::AppleDecoder()
|
||||
: MediaDecoder()
|
||||
AppleDecoder::AppleDecoder(MediaDecoderOwner* aOwner)
|
||||
: MediaDecoder(aOwner)
|
||||
{
|
||||
}
|
||||
|
||||
MediaDecoder *
|
||||
AppleDecoder::Clone()
|
||||
AppleDecoder::Clone(MediaDecoderOwner* aOwner)
|
||||
{
|
||||
return new AppleDecoder();
|
||||
return new AppleDecoder(aOwner);
|
||||
}
|
||||
|
||||
MediaDecoderStateMachine *
|
||||
|
|
|
@ -12,9 +12,9 @@ namespace mozilla {
|
|||
class AppleDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
AppleDecoder();
|
||||
explicit AppleDecoder(MediaDecoderOwner* aOwner);
|
||||
|
||||
virtual MediaDecoder* Clone() override;
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
|
||||
virtual MediaDecoderStateMachine* CreateStateMachine() override;
|
||||
|
||||
};
|
||||
|
|
|
@ -49,7 +49,8 @@ DirectShowDecoder::IsEnabled()
|
|||
Preferences::GetBool("media.directshow.enabled");
|
||||
}
|
||||
|
||||
DirectShowDecoder::DirectShowDecoder()
|
||||
DirectShowDecoder::DirectShowDecoder(MediaDecoderOwner* aOwner)
|
||||
: MediaDecoder(aOwner)
|
||||
{
|
||||
MOZ_COUNT_CTOR(DirectShowDecoder);
|
||||
}
|
||||
|
|
|
@ -16,14 +16,14 @@ class DirectShowDecoder : public MediaDecoder
|
|||
{
|
||||
public:
|
||||
|
||||
DirectShowDecoder();
|
||||
explicit DirectShowDecoder(MediaDecoderOwner* aOwner);
|
||||
virtual ~DirectShowDecoder();
|
||||
|
||||
MediaDecoder* Clone() override {
|
||||
MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
|
||||
if (!IsEnabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new DirectShowDecoder();
|
||||
return new DirectShowDecoder(aOwner);
|
||||
}
|
||||
|
||||
MediaDecoderStateMachine* CreateStateMachine() override;
|
||||
|
|
|
@ -37,7 +37,8 @@ namespace mozilla {
|
|||
#undef MP4_READER_DORMANT_HEURISTIC
|
||||
#endif
|
||||
|
||||
MP4Decoder::MP4Decoder()
|
||||
MP4Decoder::MP4Decoder(MediaDecoderOwner* aOwner)
|
||||
: MediaDecoder(aOwner)
|
||||
{
|
||||
#if defined(MP4_READER_DORMANT_HEURISTIC)
|
||||
mDormantSupported = Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false);
|
||||
|
|
|
@ -14,13 +14,13 @@ namespace mozilla {
|
|||
class MP4Decoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
MP4Decoder();
|
||||
explicit MP4Decoder(MediaDecoderOwner* aOwner);
|
||||
|
||||
virtual MediaDecoder* Clone() override {
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
|
||||
if (!IsEnabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new MP4Decoder();
|
||||
return new MP4Decoder(aOwner);
|
||||
}
|
||||
|
||||
virtual MediaDecoderStateMachine* CreateStateMachine() override;
|
||||
|
|
|
@ -15,7 +15,10 @@ namespace mozilla {
|
|||
class GStreamerDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
virtual MediaDecoder* Clone() { return new GStreamerDecoder(); }
|
||||
explicit GStreamerDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) {
|
||||
return new GStreamerDecoder(aOwner);
|
||||
}
|
||||
virtual MediaDecoderStateMachine* CreateStateMachine();
|
||||
static bool CanHandleMediaType(const nsACString& aMIMEType, const nsAString* aCodecs);
|
||||
};
|
||||
|
|
|
@ -27,15 +27,15 @@ using namespace mozilla::media;
|
|||
namespace mozilla {
|
||||
|
||||
MediaSourceDecoder::MediaSourceDecoder(dom::HTMLMediaElement* aElement)
|
||||
: mMediaSource(nullptr)
|
||||
: MediaDecoder(aElement)
|
||||
, mMediaSource(nullptr)
|
||||
, mEnded(false)
|
||||
{
|
||||
SetExplicitDuration(UnspecifiedNaN<double>());
|
||||
Init(aElement);
|
||||
}
|
||||
|
||||
MediaDecoder*
|
||||
MediaSourceDecoder::Clone()
|
||||
MediaSourceDecoder::Clone(MediaDecoderOwner* aOwner)
|
||||
{
|
||||
// TODO: Sort out cloning.
|
||||
return nullptr;
|
||||
|
|
|
@ -36,7 +36,7 @@ class MediaSourceDecoder : public MediaDecoder
|
|||
public:
|
||||
explicit MediaSourceDecoder(dom::HTMLMediaElement* aElement);
|
||||
|
||||
virtual MediaDecoder* Clone() override;
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
|
||||
virtual MediaDecoderStateMachine* CreateStateMachine() override;
|
||||
virtual nsresult Load(nsIStreamListener**) override;
|
||||
virtual media::TimeIntervals GetSeekable() override;
|
||||
|
|
|
@ -13,16 +13,17 @@ namespace mozilla {
|
|||
class OggDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
OggDecoder()
|
||||
: mShutdownBitMonitor("mShutdownBitMonitor")
|
||||
explicit OggDecoder(MediaDecoderOwner* aOwner)
|
||||
: MediaDecoder(aOwner)
|
||||
, mShutdownBitMonitor("mShutdownBitMonitor")
|
||||
, mShutdownBit(false)
|
||||
{}
|
||||
|
||||
virtual MediaDecoder* Clone() override {
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
|
||||
if (!IsOggEnabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new OggDecoder();
|
||||
return new OggDecoder(aOwner);
|
||||
}
|
||||
virtual MediaDecoderStateMachine* CreateStateMachine() override;
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
namespace mozilla {
|
||||
|
||||
MediaDecoder*
|
||||
MediaCodecDecoder::Clone()
|
||||
MediaCodecDecoder::Clone(MediaDecoderOwner* aOwner)
|
||||
{
|
||||
return new MediaCodecDecoder();
|
||||
return new MediaCodecDecoder(aOwner);
|
||||
}
|
||||
|
||||
MediaOmxCommonReader*
|
||||
|
|
|
@ -15,8 +15,10 @@ namespace mozilla {
|
|||
class MediaCodecDecoder : public MediaOmxCommonDecoder
|
||||
{
|
||||
public:
|
||||
explicit MediaCodecDecoder(MediaDecoderOwner* aOwner)
|
||||
: MediaOmxCommonDecoder(aOwner) {}
|
||||
|
||||
virtual MediaDecoder* Clone();
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner);
|
||||
|
||||
virtual MediaOmxCommonReader* CreateReader();
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ namespace mozilla {
|
|||
extern PRLogModuleInfo* gMediaDecoderLog;
|
||||
#define DECODER_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
|
||||
|
||||
MediaOmxCommonDecoder::MediaOmxCommonDecoder()
|
||||
: MediaDecoder()
|
||||
MediaOmxCommonDecoder::MediaOmxCommonDecoder(MediaDecoderOwner* aOwner)
|
||||
: MediaDecoder(aOwner)
|
||||
, mReader(nullptr)
|
||||
, mCanOffloadAudio(false)
|
||||
, mFallbackToStateMachine(false)
|
||||
|
|
|
@ -21,7 +21,7 @@ class MediaOmxCommonReader;
|
|||
class MediaOmxCommonDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
MediaOmxCommonDecoder();
|
||||
explicit MediaOmxCommonDecoder(MediaDecoderOwner* aOwner);
|
||||
|
||||
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
MediaDecoderEventVisibility aEventVisibility) override;
|
||||
|
|
|
@ -13,9 +13,9 @@ using namespace android;
|
|||
namespace mozilla {
|
||||
|
||||
MediaDecoder*
|
||||
MediaOmxDecoder::Clone()
|
||||
MediaOmxDecoder::Clone(MediaDecoderOwner* aOwner)
|
||||
{
|
||||
return new MediaOmxDecoder();
|
||||
return new MediaOmxDecoder(aOwner);
|
||||
}
|
||||
|
||||
MediaOmxCommonReader*
|
||||
|
|
|
@ -13,7 +13,9 @@ namespace mozilla {
|
|||
class MediaOmxDecoder : public MediaOmxCommonDecoder
|
||||
{
|
||||
public:
|
||||
virtual MediaDecoder* Clone();
|
||||
explicit MediaOmxDecoder(MediaDecoderOwner* aOwner)
|
||||
: MediaOmxCommonDecoder(aOwner) {}
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner);
|
||||
virtual MediaOmxCommonReader* CreateReader();
|
||||
virtual MediaDecoderStateMachine* CreateStateMachineFromReader(MediaOmxCommonReader* aReader);
|
||||
};
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
namespace mozilla {
|
||||
|
||||
MediaDecoder*
|
||||
RtspMediaCodecDecoder::Clone()
|
||||
RtspMediaCodecDecoder::Clone(MediaDecoderOwner* aOwner)
|
||||
{
|
||||
return new RtspMediaCodecDecoder();
|
||||
return new RtspMediaCodecDecoder(aOwner);
|
||||
}
|
||||
|
||||
MediaOmxCommonReader*
|
||||
|
|
|
@ -14,7 +14,10 @@ namespace mozilla {
|
|||
class RtspMediaCodecDecoder final : public MediaOmxCommonDecoder
|
||||
{
|
||||
public:
|
||||
virtual MediaDecoder* Clone() override;
|
||||
explicit RtspMediaCodecDecoder(MediaDecoderOwner* aOwner)
|
||||
: MediaOmxCommonDecoder(aOwner) {}
|
||||
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
|
||||
|
||||
virtual MediaOmxCommonReader* CreateReader() override;
|
||||
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
MediaDecoder* RtspOmxDecoder::Clone()
|
||||
MediaDecoder* RtspOmxDecoder::Clone(MediaDecoderOwner* aOwner)
|
||||
{
|
||||
return new RtspOmxDecoder();
|
||||
return new RtspOmxDecoder(aOwner);
|
||||
}
|
||||
|
||||
MediaDecoderStateMachine*
|
||||
|
|
|
@ -21,8 +21,7 @@ namespace mozilla {
|
|||
class RtspOmxDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
RtspOmxDecoder()
|
||||
: MediaDecoder() {
|
||||
explicit RtspOmxDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {
|
||||
MOZ_COUNT_CTOR(RtspOmxDecoder);
|
||||
}
|
||||
|
||||
|
@ -30,7 +29,7 @@ public:
|
|||
MOZ_COUNT_DTOR(RtspOmxDecoder);
|
||||
}
|
||||
|
||||
virtual MediaDecoder* Clone() override final;
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) override final;
|
||||
virtual MediaDecoderStateMachine* CreateStateMachine() override final;
|
||||
virtual void ChangeState(PlayState aState) override final;
|
||||
};
|
||||
|
|
|
@ -12,11 +12,12 @@ namespace mozilla {
|
|||
class RawDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
virtual MediaDecoder* Clone() {
|
||||
explicit RawDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) {
|
||||
if (!IsRawEnabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new RawDecoder();
|
||||
return new RawDecoder(aOwner);
|
||||
}
|
||||
virtual MediaDecoderStateMachine* CreateStateMachine();
|
||||
};
|
||||
|
|
|
@ -24,11 +24,12 @@ namespace mozilla {
|
|||
class WaveDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
virtual MediaDecoder* Clone() {
|
||||
explicit WaveDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) {
|
||||
if (!IsWaveEnabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new WaveDecoder();
|
||||
return new WaveDecoder(aOwner);
|
||||
}
|
||||
virtual MediaDecoderStateMachine* CreateStateMachine();
|
||||
};
|
||||
|
|
|
@ -13,11 +13,12 @@ namespace mozilla {
|
|||
class WebMDecoder : public MediaDecoder
|
||||
{
|
||||
public:
|
||||
virtual MediaDecoder* Clone() {
|
||||
explicit WebMDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
|
||||
virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) {
|
||||
if (!IsWebMEnabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new WebMDecoder();
|
||||
return new WebMDecoder(aOwner);
|
||||
}
|
||||
virtual MediaDecoderStateMachine* CreateStateMachine();
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ function queryIfBeaconSucceeded() {
|
|||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "beacon-preflight-handler.sjs?verify", true);
|
||||
xhr.onload = function() {
|
||||
is(xhr.responseText, "red", "SendBeacon should have failed because of a failed preflight!");
|
||||
is(xhr.responseText, "green", "SendBeacon should have failed because of a failed preflight!");
|
||||
SimpleTest.finish();
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
|
|
|
@ -906,5 +906,13 @@ CriticalLogger::OutputMessage(const std::string &aString,
|
|||
BasicLogger::OutputMessage(aString, aLevel, aNoNewline);
|
||||
}
|
||||
|
||||
void
|
||||
CriticalLogger::CrashAction(LogReason aReason)
|
||||
{
|
||||
if (Factory::GetLogForwarder()) {
|
||||
Factory::GetLogForwarder()->CrashAction(aReason);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -127,6 +127,17 @@ private:
|
|||
/// is further controlled by "gfx2d" PR logging module. However, in the case
|
||||
/// where such module would disable the output, in all but gfxDebug cases,
|
||||
/// we will still send a printf.
|
||||
|
||||
// The range is due to the values set in Histograms.json
|
||||
enum class LogReason : int {
|
||||
MustBeMoreThanThis = -1,
|
||||
// Start. Do not insert, always add at end. If you remove items,
|
||||
// make sure the other items retain their values.
|
||||
|
||||
// End
|
||||
MustBeLessThanThis = 101,
|
||||
};
|
||||
|
||||
struct BasicLogger
|
||||
{
|
||||
// For efficiency, this method exists and copies the logic of the
|
||||
|
@ -151,6 +162,9 @@ struct BasicLogger
|
|||
return false;
|
||||
}
|
||||
|
||||
// Only for really critical errors.
|
||||
static void CrashAction(LogReason aReason) {}
|
||||
|
||||
static void OutputMessage(const std::string &aString,
|
||||
int aLevel,
|
||||
bool aNoNewline) {
|
||||
|
@ -183,6 +197,7 @@ struct BasicLogger
|
|||
|
||||
struct CriticalLogger {
|
||||
static void OutputMessage(const std::string &aString, int aLevel, bool aNoNewline);
|
||||
static void CrashAction(LogReason aReason);
|
||||
};
|
||||
|
||||
// Implement this interface and init the Factory with an instance to
|
||||
|
@ -191,6 +206,7 @@ class LogForwarder {
|
|||
public:
|
||||
virtual ~LogForwarder() {}
|
||||
virtual void Log(const std::string &aString) = 0;
|
||||
virtual void CrashAction(LogReason aReason) = 0;
|
||||
|
||||
// Provide a copy of the logs to the caller. The int is the index
|
||||
// of the Log call, if the number of logs exceeds some preset capacity
|
||||
|
@ -215,7 +231,8 @@ public:
|
|||
enum class LogOptions : int {
|
||||
NoNewline = 0x01,
|
||||
AutoPrefix = 0x02,
|
||||
AssertOnCall = 0x04
|
||||
AssertOnCall = 0x04,
|
||||
CrashAction = 0x08,
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -241,8 +258,9 @@ public:
|
|||
// Logger::ShouldOutputMessage. Since we currently don't have a different
|
||||
// version of that method for different loggers, this is OK. Once we do,
|
||||
// change BasicLogger::ShouldOutputMessage to Logger::ShouldOutputMessage.
|
||||
explicit Log(int aOptions = Log::DefaultOptions(L == LOG_CRITICAL)) {
|
||||
Init(aOptions, BasicLogger::ShouldOutputMessage(L));
|
||||
explicit Log(int aOptions = Log::DefaultOptions(L == LOG_CRITICAL),
|
||||
LogReason aReason = LogReason::MustBeMoreThanThis) {
|
||||
Init(aOptions, BasicLogger::ShouldOutputMessage(L), aReason);
|
||||
}
|
||||
|
||||
~Log() {
|
||||
|
@ -451,22 +469,30 @@ public:
|
|||
inline bool LogIt() const { return mLogIt; }
|
||||
inline bool NoNewline() const { return mOptions & int(LogOptions::NoNewline); }
|
||||
inline bool AutoPrefix() const { return mOptions & int(LogOptions::AutoPrefix); }
|
||||
inline bool ValidReason() const { return (int)mReason > (int)LogReason::MustBeMoreThanThis && (int)mReason < (int)LogReason::MustBeLessThanThis; }
|
||||
|
||||
// We do not want this version to do any work, and stringstream can't be
|
||||
// copied anyway. It does come in handy for the "Once" macro defined below.
|
||||
MOZ_IMPLICIT Log(const Log& log) { Init(log.mOptions, false); }
|
||||
MOZ_IMPLICIT Log(const Log& log) { Init(log.mOptions, false, log.mReason); }
|
||||
|
||||
private:
|
||||
// Initialization common to two constructors
|
||||
void Init(int aOptions, bool aLogIt) {
|
||||
void Init(int aOptions, bool aLogIt, LogReason aReason) {
|
||||
mOptions = aOptions;
|
||||
mReason = aReason;
|
||||
mLogIt = aLogIt;
|
||||
if (mLogIt && AutoPrefix()) {
|
||||
if (mOptions & int(LogOptions::AssertOnCall)) {
|
||||
mMessage << "[GFX" << L << "]: ";
|
||||
} else {
|
||||
mMessage << "[GFX" << L << "-]: ";
|
||||
if (mLogIt) {
|
||||
if (AutoPrefix()) {
|
||||
if (mOptions & int(LogOptions::AssertOnCall)) {
|
||||
mMessage << "[GFX" << L;
|
||||
} else {
|
||||
mMessage << "[GFX" << L << "-";
|
||||
}
|
||||
}
|
||||
if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) {
|
||||
mMessage << " " << (int)mReason;
|
||||
}
|
||||
mMessage << "]: ";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -476,11 +502,15 @@ private:
|
|||
if (mOptions & int(LogOptions::AssertOnCall)) {
|
||||
MOZ_ASSERT(false, "An assert from the graphics logger");
|
||||
}
|
||||
if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) {
|
||||
Logger::CrashAction(mReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream mMessage;
|
||||
int mOptions;
|
||||
LogReason mReason;
|
||||
bool mLogIt;
|
||||
};
|
||||
|
||||
|
@ -531,6 +561,14 @@ typedef Log<LOG_CRITICAL, CriticalLogger> CriticalLog;
|
|||
#define gfxWarningOnce if (1) ; else mozilla::gfx::NoLog
|
||||
#endif
|
||||
|
||||
// In the debug build, this is equivalent to the default gfxCriticalError.
|
||||
// In the non-debug build, on nightly and dev edition, it will MOZ_CRASH.
|
||||
// On beta and release versions, it will telemetry count, but proceed.
|
||||
//
|
||||
// You should create a (new) enum in the LogReason and use it for the reason
|
||||
// parameter to ensure uniqueness.
|
||||
#define gfxCrash(reason) gfxCriticalError(int(LogOptions::AutoPrefix) | int(LogOptions::AssertOnCall) | int(LogOptions::CrashAction), (reason))
|
||||
|
||||
// See nsDebug.h and the NS_WARN_IF macro
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -172,6 +172,15 @@ _cairo_error (cairo_status_t status)
|
|||
CAIRO_ENSURE_UNIQUE;
|
||||
assert (_cairo_status_is_error (status));
|
||||
|
||||
#ifdef MOZILLA_VERSION
|
||||
static int abort_on_error = -1;
|
||||
if (abort_on_error < 0) {
|
||||
abort_on_error = (getenv("MOZ_CAIRO_ERROR_ABORT") != NULL) ? 1 : 0;
|
||||
}
|
||||
if (abort_on_error) {
|
||||
*(int*)0x0 = 10;
|
||||
}
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
#include "mozilla/layers/SharedBufferManagerChild.h"
|
||||
#include "mozilla/layers/ISurfaceAllocator.h" // for GfxMemoryImageReporter
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
@ -185,6 +186,7 @@ class CrashStatsLogForwarder: public mozilla::gfx::LogForwarder
|
|||
public:
|
||||
explicit CrashStatsLogForwarder(const char* aKey);
|
||||
virtual void Log(const std::string& aString) override;
|
||||
virtual void CrashAction(LogReason aReason) override;
|
||||
|
||||
virtual std::vector<std::pair<int32_t,std::string> > StringsVectorCopy() override;
|
||||
|
||||
|
@ -282,6 +284,55 @@ void CrashStatsLogForwarder::Log(const std::string& aString)
|
|||
}
|
||||
}
|
||||
|
||||
class CrashTelemetryEvent : public nsRunnable
|
||||
{
|
||||
virtual ~CrashTelemetryEvent() {}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
explicit CrashTelemetryEvent(uint32_t aReason) : mReason(aReason) {}
|
||||
|
||||
NS_IMETHOD Run() override {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
Telemetry::Accumulate(Telemetry::GFX_CRASH, mReason);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t mReason;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(CrashTelemetryEvent, nsRunnable);
|
||||
|
||||
void
|
||||
CrashStatsLogForwarder::CrashAction(LogReason aReason)
|
||||
{
|
||||
#ifndef RELEASE_BUILD
|
||||
// Non-release builds crash by default, but will use telemetry
|
||||
// if this environment variable is present.
|
||||
static bool useTelemetry = getenv("MOZ_GFX_CRASH_TELEMETRY") != 0;
|
||||
#else
|
||||
// Release builds use telemetry bu default, but will crash
|
||||
// if this environment variable is present. Double negative
|
||||
// to make the intent clear.
|
||||
static bool useTelemetry = !(getenv("MOZ_GFX_CRASH_MOZ_CRASH") != 0);
|
||||
#endif
|
||||
|
||||
if (useTelemetry) {
|
||||
// The callers need to assure that aReason is in the range
|
||||
// that the telemetry call below supports.
|
||||
if (NS_IsMainThread()) {
|
||||
Telemetry::Accumulate(Telemetry::GFX_CRASH, (uint32_t)aReason);
|
||||
} else {
|
||||
nsCOMPtr<nsIRunnable> r1 = new CrashTelemetryEvent((uint32_t)aReason);
|
||||
NS_DispatchToMainThread(r1);
|
||||
}
|
||||
} else {
|
||||
// ignoring aReason, we can get the information we need from the stack
|
||||
MOZ_CRASH("GFX_CRASH");
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(SRGBOverrideObserver, nsIObserver, nsISupportsWeakReference)
|
||||
|
||||
#define GFX_DOWNLOADABLE_FONTS_ENABLED "gfx.downloadable_fonts.enabled"
|
||||
|
@ -755,7 +806,7 @@ gfxPlatform::CreateDrawTargetForUpdateSurface(gfxASurface *aSurface, const IntSi
|
|||
return Factory::CreateDrawTargetForCairoCGContext(static_cast<gfxQuartzSurface*>(aSurface)->GetCGContext(), aSize);
|
||||
}
|
||||
#endif
|
||||
MOZ_CRASH();
|
||||
MOZ_CRASH("unused function");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -6352,8 +6352,7 @@ ParseFunction(ModuleValidator& m, ParseNode** fnOut)
|
|||
return false;
|
||||
|
||||
Directives newDirectives = directives;
|
||||
AsmJSParseContext funpc(&m.parser(), outerpc, fn, funbox, &newDirectives,
|
||||
/* blockScopeDepth = */ 0);
|
||||
AsmJSParseContext funpc(&m.parser(), outerpc, fn, funbox, &newDirectives);
|
||||
if (!funpc.init(m.parser()))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -77,14 +77,9 @@ class MOZ_STACK_CLASS BytecodeCompiler
|
|||
bool isEvalCompilationUnit();
|
||||
bool isNonGlobalEvalCompilationUnit();
|
||||
bool isNonSyntacticCompilationUnit();
|
||||
bool createParseContext(Maybe<ParseContext<FullParseHandler>>& parseContext,
|
||||
SharedContext& globalsc, uint32_t blockScopeDepth = 0);
|
||||
bool saveCallerFun(HandleScript evalCaller, ParseContext<FullParseHandler>& parseContext);
|
||||
bool handleStatementParseFailure(HandleObject scopeChain, HandleScript evalCaller,
|
||||
Maybe<ParseContext<FullParseHandler>>& parseContext,
|
||||
SharedContext& globalsc);
|
||||
bool saveCallerFun(HandleScript evalCaller);
|
||||
bool handleParseFailure(const Directives& newDirectives);
|
||||
bool prepareAndEmitTree(ParseNode** pn, ParseContext<FullParseHandler>& pc);
|
||||
bool prepareAndEmitTree(ParseNode** pn);
|
||||
bool checkArgumentsWithinEval(JSContext* cx, HandleFunction fun);
|
||||
bool maybeCheckEvalFreeVariables(HandleScript evalCaller, HandleObject scopeChain,
|
||||
ParseContext<FullParseHandler>& pc);
|
||||
|
@ -92,9 +87,7 @@ class MOZ_STACK_CLASS BytecodeCompiler
|
|||
bool maybeSetSourceMap(TokenStream& tokenStream);
|
||||
bool maybeSetSourceMapFromOptions();
|
||||
bool emitFinalReturn();
|
||||
bool initGlobalOrEvalBindings(ParseContext<FullParseHandler>& pc,
|
||||
Handle<TraceableVector<Binding>> vars,
|
||||
Handle<TraceableVector<Binding>> lexicals);
|
||||
bool initGlobalOrEvalBindings(ParseContext<FullParseHandler>& pc);
|
||||
bool maybeCompleteCompressSource();
|
||||
|
||||
AutoCompilationTraceLogger traceLogger;
|
||||
|
@ -303,17 +296,7 @@ BytecodeCompiler::isNonSyntacticCompilationUnit()
|
|||
}
|
||||
|
||||
bool
|
||||
BytecodeCompiler::createParseContext(Maybe<ParseContext<FullParseHandler>>& parseContext,
|
||||
SharedContext& globalsc, uint32_t blockScopeDepth)
|
||||
{
|
||||
parseContext.emplace(parser.ptr(), (GenericParseContext*) nullptr, (ParseNode*) nullptr,
|
||||
&globalsc, (Directives*) nullptr, blockScopeDepth);
|
||||
return parseContext->init(*parser);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeCompiler::saveCallerFun(HandleScript evalCaller,
|
||||
ParseContext<FullParseHandler>& parseContext)
|
||||
BytecodeCompiler::saveCallerFun(HandleScript evalCaller)
|
||||
{
|
||||
/*
|
||||
* An eval script in a caller frame needs to have its enclosing
|
||||
|
@ -325,8 +308,9 @@ BytecodeCompiler::saveCallerFun(HandleScript evalCaller,
|
|||
RootedFunction fun(cx, evalCaller->functionOrCallerFunction());
|
||||
MOZ_ASSERT_IF(fun->strict(), options.strictOption);
|
||||
Directives directives(/* strict = */ options.strictOption);
|
||||
ObjectBox* funbox = parser->newFunctionBox(/* fn = */ nullptr, fun, &parseContext,
|
||||
directives, fun->generatorKind());
|
||||
ObjectBox* funbox = parser->newFunctionBox(/* fn = */ nullptr, fun,
|
||||
directives, fun->generatorKind(),
|
||||
enclosingStaticScope);
|
||||
if (!funbox)
|
||||
return false;
|
||||
|
||||
|
@ -334,36 +318,6 @@ BytecodeCompiler::saveCallerFun(HandleScript evalCaller,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeCompiler::handleStatementParseFailure(HandleObject scopeChain, HandleScript evalCaller,
|
||||
Maybe<ParseContext<FullParseHandler>>& parseContext,
|
||||
SharedContext& globalsc)
|
||||
{
|
||||
if (!parser->hadAbortedSyntaxParse())
|
||||
return false;
|
||||
|
||||
// Parsing inner functions lazily may lead the parser into an
|
||||
// unrecoverable state and may require starting over on the top
|
||||
// level statement. Restart the parse; syntax parsing has
|
||||
// already been disabled for the parser and the result will not
|
||||
// be ambiguous.
|
||||
parser->clearAbortedSyntaxParse();
|
||||
parser->tokenStream.seek(startPosition);
|
||||
parser->blockScopes.clear();
|
||||
|
||||
// Destroying the parse context will destroy its free
|
||||
// variables, so check if any deoptimization is needed.
|
||||
if (!maybeCheckEvalFreeVariables(evalCaller, scopeChain, parseContext.ref()))
|
||||
return false;
|
||||
|
||||
parseContext.reset();
|
||||
if (!createParseContext(parseContext, globalsc, script->bindings.numBlockScoped()))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(parser->pc == parseContext.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeCompiler::handleParseFailure(const Directives& newDirectives)
|
||||
{
|
||||
|
@ -386,13 +340,8 @@ BytecodeCompiler::handleParseFailure(const Directives& newDirectives)
|
|||
}
|
||||
|
||||
bool
|
||||
BytecodeCompiler::prepareAndEmitTree(ParseNode** ppn, ParseContext<FullParseHandler>& pc)
|
||||
BytecodeCompiler::prepareAndEmitTree(ParseNode** ppn)
|
||||
{
|
||||
// Accumulate the maximum block scope depth, so that emitTree can assert
|
||||
// when emitting JSOP_GETLOCAL that the local is indeed within the fixed
|
||||
// part of the stack frame.
|
||||
script->bindings.updateNumBlockScoped(pc.blockScopeDepth);
|
||||
|
||||
if (!FoldConstants(cx, ppn, parser.ptr()) ||
|
||||
!NameFunctions(cx, *ppn) ||
|
||||
!emitter->updateLocalsToFrameSlots() ||
|
||||
|
@ -524,35 +473,11 @@ BytecodeCompiler::emitFinalReturn()
|
|||
}
|
||||
|
||||
bool
|
||||
BytecodeCompiler::initGlobalOrEvalBindings(ParseContext<FullParseHandler>& pc,
|
||||
Handle<TraceableVector<Binding>> vars,
|
||||
Handle<TraceableVector<Binding>> lexicals)
|
||||
BytecodeCompiler::initGlobalOrEvalBindings(ParseContext<FullParseHandler>& pc)
|
||||
{
|
||||
Rooted<Bindings> bindings(cx, script->bindings);
|
||||
Binding* packedBindings = alloc->newArrayUninitialized<Binding>(vars.length() +
|
||||
lexicals.length());
|
||||
if (!packedBindings) {
|
||||
ReportOutOfMemory(cx);
|
||||
if (!pc.generateBindings(cx, parser->tokenStream, *alloc, &bindings))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bindings for global and eval scripts are used solely for redeclaration
|
||||
// checks in the prologue. Neither 'true' nor 'false' accurately describe
|
||||
// their aliased-ness. These bindings don't live in CallObjects or the
|
||||
// frame, but either on the global object and the global lexical
|
||||
// scope. Force aliased to be false to avoid confusing other analyses in
|
||||
// the engine that assumes the frame has a call object if there are
|
||||
// aliased bindings.
|
||||
Binding* packedIter = packedBindings;
|
||||
for (const Binding& b: vars)
|
||||
*packedIter++ = Binding(b.name(), b.kind(), false);
|
||||
for (const Binding& b: lexicals)
|
||||
*packedIter++ = Binding(b.name(), b.kind(), false);
|
||||
|
||||
if (!Bindings::initWithTemporaryStorage(cx, &bindings, 0, vars.length(), lexicals.length(),
|
||||
pc.blockScopeDepth, 0, 0, packedBindings))
|
||||
return false;
|
||||
|
||||
script->bindings = bindings;
|
||||
return true;
|
||||
}
|
||||
|
@ -577,86 +502,46 @@ BytecodeCompiler::compileScript(HandleObject scopeChain, HandleScript evalCaller
|
|||
if (!createEmitter(&globalsc, evalCaller, isNonGlobalEvalCompilationUnit()))
|
||||
return nullptr;
|
||||
|
||||
Rooted<TraceableVector<Binding>> vars(cx, TraceableVector<Binding>(cx));
|
||||
Rooted<TraceableVector<Binding>> lexicals(cx, TraceableVector<Binding>(cx));
|
||||
|
||||
// Syntax parsing may cause us to restart processing of top level
|
||||
// statements in the script. Use Maybe<> so that the parse context can be
|
||||
// reset when this occurs.
|
||||
//
|
||||
// WARNING: ParseContext contains instances of Rooted and may be
|
||||
// reset(). Do not make any new Rooted instances below this point to avoid
|
||||
// violating the Rooted LIFO invariant.
|
||||
Maybe<ParseContext<FullParseHandler>> pc;
|
||||
if (!createParseContext(pc, globalsc))
|
||||
if (savedCallerFun && !saveCallerFun(evalCaller))
|
||||
return nullptr;
|
||||
|
||||
if (savedCallerFun && !saveCallerFun(evalCaller, pc.ref()))
|
||||
return nullptr;
|
||||
for (;;) {
|
||||
ParseContext<FullParseHandler> pc(parser.ptr(),
|
||||
/* parent = */ nullptr,
|
||||
/* maybeFunction = */ nullptr,
|
||||
&globalsc,
|
||||
/* newDirectives = */ nullptr);
|
||||
if (!pc.init(*parser))
|
||||
return nullptr;
|
||||
|
||||
// Global scripts are parsed incrementally, statement by statement.
|
||||
//
|
||||
// Eval scripts cannot be, as the block depth needs to be computed for all
|
||||
// lexical bindings in the entire eval script.
|
||||
if (isEvalCompilationUnit()) {
|
||||
ParseNode* pn;
|
||||
do {
|
||||
if (isEvalCompilationUnit())
|
||||
pn = parser->evalBody();
|
||||
if (!pn && !handleStatementParseFailure(scopeChain, evalCaller, pc, globalsc))
|
||||
else
|
||||
pn = parser->globalBody();
|
||||
|
||||
// Successfully parsed. Emit the script.
|
||||
if (pn) {
|
||||
if (!initGlobalOrEvalBindings(pc))
|
||||
return nullptr;
|
||||
} while (!pn);
|
||||
|
||||
if (!prepareAndEmitTree(&pn, *pc))
|
||||
return nullptr;
|
||||
|
||||
if (!pc->drainGlobalOrEvalBindings(cx, &vars, &lexicals))
|
||||
return nullptr;
|
||||
|
||||
parser->handler.freeTree(pn);
|
||||
} else {
|
||||
bool canHaveDirectives = true;
|
||||
for (;;) {
|
||||
TokenKind tt;
|
||||
if (!parser->tokenStream.peekToken(&tt, TokenStream::Operand))
|
||||
if (!maybeCheckEvalFreeVariables(evalCaller, scopeChain, pc))
|
||||
return nullptr;
|
||||
if (tt == TOK_EOF)
|
||||
break;
|
||||
|
||||
parser->tokenStream.tell(&startPosition);
|
||||
|
||||
ParseNode* pn = parser->statement(YieldIsName, canHaveDirectives);
|
||||
if (!pn) {
|
||||
if (!handleStatementParseFailure(scopeChain, evalCaller, pc, globalsc))
|
||||
return nullptr;
|
||||
|
||||
pn = parser->statement(YieldIsName);
|
||||
if (!pn) {
|
||||
MOZ_ASSERT(!parser->hadAbortedSyntaxParse());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (canHaveDirectives) {
|
||||
if (!parser->maybeParseDirective(/* stmtList = */ nullptr, pn, &canHaveDirectives))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!prepareAndEmitTree(&pn, *pc))
|
||||
if (!prepareAndEmitTree(&pn))
|
||||
return nullptr;
|
||||
|
||||
if (!pc->drainGlobalOrEvalBindings(cx, &vars, &lexicals))
|
||||
return nullptr;
|
||||
|
||||
parser->handler.freeTree(pn);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Maybe we aborted a syntax parse. See if we can try again.
|
||||
if (!handleParseFailure(directives))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!maybeCheckEvalFreeVariables(evalCaller, scopeChain, *pc) ||
|
||||
!maybeSetDisplayURL(parser->tokenStream) ||
|
||||
if (!maybeSetDisplayURL(parser->tokenStream) ||
|
||||
!maybeSetSourceMap(parser->tokenStream) ||
|
||||
!maybeSetSourceMapFromOptions() ||
|
||||
!emitFinalReturn() ||
|
||||
!initGlobalOrEvalBindings(pc.ref(), vars, lexicals) ||
|
||||
!JSScript::fullyInitFromEmitter(cx, script, emitter.ptr()))
|
||||
{
|
||||
return nullptr;
|
||||
|
|
|
@ -362,7 +362,13 @@ ParseContext<ParseHandler>::updateDecl(TokenStream& ts, JSAtom* atom, Node pn)
|
|||
// Terribly, deoptimized bindings may be updated with
|
||||
// optimized bindings due to hoisted function statements, so
|
||||
// give the new declaration a slot.
|
||||
if (oldDecl->isDeoptimized() && !newDecl->isDeoptimized()) {
|
||||
//
|
||||
// Global bindings are excluded as currently they are never
|
||||
// frame slots. The notion of being deoptimized is not
|
||||
// applicable to them.
|
||||
if (oldDecl->isDeoptimized() && !newDecl->isDeoptimized() &&
|
||||
!sc->isGlobalContext())
|
||||
{
|
||||
newDecl->pn_dflags |= PND_BOUND;
|
||||
newDecl->pn_scopecoord.setSlot(ts, i);
|
||||
newDecl->setOp(JSOP_GETLOCAL);
|
||||
|
@ -431,15 +437,27 @@ AppendPackedBindings(const ParseContext<ParseHandler>* pc, const DeclVector& vec
|
|||
MOZ_CRASH("unexpected dn->kind");
|
||||
}
|
||||
|
||||
/*
|
||||
* Bindings::init does not check for duplicates so we must ensure that
|
||||
* only one binding with a given name is marked aliased. pc->decls
|
||||
* maintains the canonical definition for each name, so use that.
|
||||
*/
|
||||
MOZ_ASSERT_IF(dn->isClosed(), pc->decls().lookupFirst(name) == dn);
|
||||
bool aliased = dn->isClosed() ||
|
||||
(pc->sc->allLocalsAliased() &&
|
||||
pc->decls().lookupFirst(name) == dn);
|
||||
bool aliased;
|
||||
if (pc->sc->isGlobalContext()) {
|
||||
// Bindings for global and eval scripts are used solely for redeclaration
|
||||
// checks in the prologue. Neither 'true' nor 'false' accurately describe
|
||||
// their aliased-ness. These bindings don't live in CallObjects or the
|
||||
// frame, but either on the global object and the global lexical
|
||||
// scope. Force aliased to be false to avoid confusing other analyses in
|
||||
// the engine that assumes the frame has a call object if there are
|
||||
// aliased bindings.
|
||||
aliased = false;
|
||||
} else {
|
||||
/*
|
||||
* Bindings::init does not check for duplicates so we must ensure that
|
||||
* only one binding with a given name is marked aliased. pc->decls
|
||||
* maintains the canonical definition for each name, so use that.
|
||||
*/
|
||||
MOZ_ASSERT_IF(dn->isClosed(), pc->decls().lookupFirst(name) == dn);
|
||||
aliased = dn->isClosed() ||
|
||||
(pc->sc->allLocalsAliased() &&
|
||||
pc->decls().lookupFirst(name) == dn);
|
||||
}
|
||||
|
||||
*dst = Binding(name, kind, aliased);
|
||||
if (!aliased && numUnaliased)
|
||||
|
@ -463,18 +481,23 @@ ParseContext<ParseHandler>::generateBindings(ExclusiveContext* cx, TokenStream&
|
|||
if (UINT32_MAX - args_.length() <= vars_.length() + bodyLevelLexicals_.length())
|
||||
return ts.reportError(JSMSG_TOO_MANY_LOCALS);
|
||||
|
||||
// Fix up the blockids of vars, whose static scope is always at the body
|
||||
// level. This could not be done up front in ParseContext::Define, as
|
||||
// the original blockids are used for redeclaration checks.
|
||||
for (size_t i = 0; i < vars_.length(); i++)
|
||||
vars_[i]->pn_blockid = bodyid;
|
||||
// Fix up slots in non-global contexts. In global contexts all body-level
|
||||
// names are dynamically defined and do not live in either frame or
|
||||
// CallObject slots.
|
||||
if (!sc->isGlobalContext()) {
|
||||
// Fix up the blockids of vars, whose static scope is always at the body
|
||||
// level. This could not be done up front in ParseContext::define, as
|
||||
// the original blockids are used for redeclaration checks.
|
||||
for (size_t i = 0; i < vars_.length(); i++)
|
||||
vars_[i]->pn_blockid = bodyid;
|
||||
|
||||
// Fix up the slots of body-level lets to come after the vars now that we
|
||||
// know how many vars there are.
|
||||
for (size_t i = 0; i < bodyLevelLexicals_.length(); i++) {
|
||||
Definition* dn = bodyLevelLexicals_[i];
|
||||
if (!dn->pn_scopecoord.setSlot(ts, vars_.length() + i))
|
||||
return false;
|
||||
// Fix up the slots of body-level lets to come after the vars now that we
|
||||
// know how many vars there are.
|
||||
for (size_t i = 0; i < bodyLevelLexicals_.length(); i++) {
|
||||
Definition* dn = bodyLevelLexicals_[i];
|
||||
if (!dn->pn_scopecoord.setSlot(ts, vars_.length() + i))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t count = args_.length() + vars_.length() + bodyLevelLexicals_.length();
|
||||
|
@ -498,32 +521,6 @@ ParseContext<ParseHandler>::generateBindings(ExclusiveContext* cx, TokenStream&
|
|||
packedBindings, sc->isModuleBox());
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
ParseContext<FullParseHandler>::drainGlobalOrEvalBindings(ExclusiveContext* cx,
|
||||
MutableHandle<TraceableVector<Binding>> vars,
|
||||
MutableHandle<TraceableVector<Binding>> lexicals)
|
||||
{
|
||||
MOZ_ASSERT(sc->isGlobalContext());
|
||||
|
||||
uint32_t newVarsPos = vars.length();
|
||||
uint32_t newLexicalsPos = lexicals.length();
|
||||
|
||||
if (!vars.growBy(vars_.length()))
|
||||
return false;
|
||||
AppendPackedBindings(this, vars_, vars.begin() + newVarsPos);
|
||||
vars_.clear();
|
||||
|
||||
if (!sc->staticScope()->is<StaticEvalObject>()) {
|
||||
if (!lexicals.growBy(bodyLevelLexicals_.length()))
|
||||
return false;
|
||||
AppendPackedBindings(this, bodyLevelLexicals_, lexicals.begin() + newLexicalsPos);
|
||||
}
|
||||
bodyLevelLexicals_.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
|
||||
|
@ -841,8 +838,7 @@ Parser<ParseHandler>::parse()
|
|||
GlobalSharedContext globalsc(context, staticLexical, directives,
|
||||
options().extraWarningsOption);
|
||||
ParseContext<ParseHandler> globalpc(this, /* parent = */ nullptr, ParseHandler::null(),
|
||||
&globalsc, /* newDirectives = */ nullptr,
|
||||
/* blockScopeDepth = */ 0);
|
||||
&globalsc, /* newDirectives = */ nullptr);
|
||||
if (!globalpc.init(*this))
|
||||
return null();
|
||||
|
||||
|
@ -919,7 +915,7 @@ Parser<ParseHandler>::standaloneModule(HandleModuleObject module)
|
|||
return null();
|
||||
handler.setModuleBox(mn, modulebox);
|
||||
|
||||
ParseContext<FullParseHandler> modulepc(this, pc, mn, modulebox, nullptr, 0);
|
||||
ParseContext<FullParseHandler> modulepc(this, pc, mn, modulebox, nullptr);
|
||||
if (!modulepc.init(*this))
|
||||
return null();
|
||||
|
||||
|
@ -960,6 +956,26 @@ Parser<SyntaxParseHandler>::standaloneModule(HandleModuleObject module)
|
|||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::checkStatementsEOF()
|
||||
{
|
||||
// This is designed to be paired with parsing a statement list at the top
|
||||
// level.
|
||||
//
|
||||
// The statements() call breaks on TOK_RC, so make sure we've
|
||||
// reached EOF here.
|
||||
TokenKind tt;
|
||||
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
|
||||
return false;
|
||||
if (tt != TOK_EOF) {
|
||||
report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
|
||||
"expression", TokenKindToDesc(tt));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode*
|
||||
Parser<FullParseHandler>::evalBody()
|
||||
|
@ -977,22 +993,30 @@ Parser<FullParseHandler>::evalBody()
|
|||
if (!body)
|
||||
return nullptr;
|
||||
|
||||
// The statements() call above breaks on TOK_RC, so make sure we've
|
||||
// reached EOF here.
|
||||
TokenKind tt;
|
||||
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
|
||||
if (!checkStatementsEOF())
|
||||
return nullptr;
|
||||
if (tt != TOK_EOF) {
|
||||
report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
|
||||
"expression", TokenKindToDesc(tt));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
block->pn_expr = body;
|
||||
block->pn_pos = body->pn_pos;
|
||||
return block;
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode*
|
||||
Parser<FullParseHandler>::globalBody()
|
||||
{
|
||||
MOZ_ASSERT(pc->atGlobalLevel());
|
||||
|
||||
ParseNode* body = statements(YieldIsName);
|
||||
if (!body)
|
||||
return nullptr;
|
||||
|
||||
if (!checkStatementsEOF())
|
||||
return nullptr;
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode*
|
||||
Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
|
||||
|
@ -1020,8 +1044,7 @@ Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
|
|||
funbox->length = fun->nargs() - fun->hasRest();
|
||||
handler.setFunctionBox(fn, funbox);
|
||||
|
||||
ParseContext<FullParseHandler> funpc(this, pc, fn, funbox, newDirectives,
|
||||
/* blockScopeDepth = */ 0);
|
||||
ParseContext<FullParseHandler> funpc(this, pc, fn, funbox, newDirectives);
|
||||
if (!funpc.init(*this))
|
||||
return null();
|
||||
|
||||
|
@ -2643,8 +2666,7 @@ Parser<FullParseHandler>::functionArgsAndBody(InHandling inHandling, ParseNode*
|
|||
return false;
|
||||
|
||||
ParseContext<SyntaxParseHandler> funpc(parser, outerpc, SyntaxParseHandler::null(),
|
||||
funbox, newDirectives,
|
||||
/* blockScopeDepth = */ 0);
|
||||
funbox, newDirectives);
|
||||
if (!funpc.init(*parser))
|
||||
return false;
|
||||
|
||||
|
@ -2681,8 +2703,7 @@ Parser<FullParseHandler>::functionArgsAndBody(InHandling inHandling, ParseNode*
|
|||
blockScopes.resize(oldBlockScopesLength);
|
||||
|
||||
// Continue doing a full parse for this inner function.
|
||||
ParseContext<FullParseHandler> funpc(this, pc, pn, funbox, newDirectives,
|
||||
/* blockScopeDepth = */ 0);
|
||||
ParseContext<FullParseHandler> funpc(this, pc, pn, funbox, newDirectives);
|
||||
if (!funpc.init(*this))
|
||||
return false;
|
||||
|
||||
|
@ -2720,8 +2741,7 @@ Parser<SyntaxParseHandler>::functionArgsAndBody(InHandling inHandling, Node pn,
|
|||
return false;
|
||||
|
||||
// Initialize early for possible flags mutation via destructuringExpr.
|
||||
ParseContext<SyntaxParseHandler> funpc(this, pc, handler.null(), funbox, newDirectives,
|
||||
/* blockScopeDepth = */ 0);
|
||||
ParseContext<SyntaxParseHandler> funpc(this, pc, handler.null(), funbox, newDirectives);
|
||||
if (!funpc.init(*this))
|
||||
return false;
|
||||
|
||||
|
@ -2785,7 +2805,7 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
|
|||
|
||||
Directives newDirectives = directives;
|
||||
ParseContext<FullParseHandler> funpc(this, /* parent = */ nullptr, pn, funbox,
|
||||
&newDirectives, /* blockScopeDepth = */ 0);
|
||||
&newDirectives);
|
||||
if (!funpc.init(*this))
|
||||
return null();
|
||||
|
||||
|
@ -8101,8 +8121,7 @@ Parser<ParseHandler>::generatorComprehensionLambda(GeneratorKind comprehensionKi
|
|||
return null();
|
||||
|
||||
ParseContext<ParseHandler> genpc(this, outerpc, genfn, genFunbox,
|
||||
/* newDirectives = */ nullptr,
|
||||
/* blockScopeDepth = */ 0);
|
||||
/* newDirectives = */ nullptr);
|
||||
if (!genpc.init(*this))
|
||||
return null();
|
||||
|
||||
|
|
|
@ -197,38 +197,29 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
|||
/* See the sad story in MakeDefIntoUse. */
|
||||
void updateDecl(TokenStream& ts, JSAtom* atom, Node newDecl);
|
||||
|
||||
/*
|
||||
* After a function body or module has been parsed, the parser generates the
|
||||
* code's "bindings". Bindings are a data-structure, ultimately stored in
|
||||
* the compiled JSScript, that serve three purposes:
|
||||
* - After parsing, the ParseContext is destroyed and 'decls' along with
|
||||
* it. Mostly, the emitter just uses the binding information stored in
|
||||
* the use/def nodes, but the emitter occasionally needs 'bindings' for
|
||||
* various scope-related queries.
|
||||
* - Bindings provide the initial js::Shape to use when creating a dynamic
|
||||
* scope object (js::CallObject). This shape is used during dynamic name
|
||||
* lookup.
|
||||
* - Sometimes a script's bindings are accessed at runtime to retrieve the
|
||||
* contents of the lexical scope (e.g., from the debugger).
|
||||
*/
|
||||
// After a script has been parsed, the parser generates the code's
|
||||
// "bindings". Bindings are a data-structure, ultimately stored in the
|
||||
// compiled JSScript, that serve three purposes:
|
||||
//
|
||||
// - After parsing, the ParseContext is destroyed and 'decls' along with
|
||||
// it. Mostly, the emitter just uses the binding information stored in
|
||||
// the use/def nodes, but the emitter occasionally needs 'bindings' for
|
||||
// various scope-related queries.
|
||||
//
|
||||
// - For functions, bindings provide the initial js::Shape to use when
|
||||
// creating a dynamic scope object (js::CallObject). This shape is used
|
||||
// during dynamic name lookup.
|
||||
//
|
||||
// - Sometimes a script's bindings are accessed at runtime to retrieve the
|
||||
// contents of the lexical scope (e.g., from the debugger).
|
||||
//
|
||||
// - For global and eval scripts, ES6 15.1.8 specifies that if there are
|
||||
// name conflicts in the script, *no* bindings from the script are
|
||||
// instantiated. So, record the vars and lexical bindings to check for
|
||||
// redeclarations in the prologue.
|
||||
bool generateBindings(ExclusiveContext* cx, TokenStream& ts, LifoAlloc& alloc,
|
||||
MutableHandle<Bindings> bindings) const;
|
||||
|
||||
// All global names in global scripts are added to the scope dynamically
|
||||
// via JSOP_DEF{FUN,VAR,LET,CONST}, but ES6 15.1.8 specifies that if there
|
||||
// are name conflicts in the script, *no* bindings from the script are
|
||||
// instantiated. So, record the vars and lexical bindings to check for
|
||||
// redeclarations in the prologue.
|
||||
//
|
||||
// Eval scripts do not need this mechanism as they always have a
|
||||
// non-extensible lexical scope.
|
||||
//
|
||||
// Global and eval scripts may have block-scoped locals, however, which
|
||||
// are allocated to the fixed part of the stack frame.
|
||||
bool drainGlobalOrEvalBindings(ExclusiveContext* cx,
|
||||
MutableHandle<TraceableVector<Binding>> vars,
|
||||
MutableHandle<TraceableVector<Binding>> lexicals);
|
||||
|
||||
private:
|
||||
ParseContext** parserPC; /* this points to the Parser's active pc
|
||||
and holds either |this| or one of
|
||||
|
@ -268,14 +259,13 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
|||
bool inDeclDestructuring:1;
|
||||
|
||||
ParseContext(Parser<ParseHandler>* prs, GenericParseContext* parent,
|
||||
Node maybeFunction, SharedContext* sc, Directives* newDirectives,
|
||||
uint32_t blockScopeDepth)
|
||||
Node maybeFunction, SharedContext* sc, Directives* newDirectives)
|
||||
: GenericParseContext(parent, sc),
|
||||
bodyid(0), // initialized in init()
|
||||
stmtStack(prs->context),
|
||||
maybeFunction(maybeFunction),
|
||||
lastYieldOffset(NoYieldOffset),
|
||||
blockScopeDepth(blockScopeDepth),
|
||||
blockScopeDepth(0),
|
||||
blockNode(ParseHandler::null()),
|
||||
decls_(prs->context, prs->alloc),
|
||||
args_(prs->context),
|
||||
|
@ -581,6 +571,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
|||
bool appendToCallSiteObj(Node callSiteObj);
|
||||
bool addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList,
|
||||
TokenKind* ttp);
|
||||
bool checkStatementsEOF();
|
||||
|
||||
inline Node newName(PropertyName* name);
|
||||
inline Node newYieldExpression(uint32_t begin, Node expr, bool isYieldStar = false);
|
||||
|
@ -600,6 +591,9 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
|||
// lexical scope.
|
||||
Node evalBody();
|
||||
|
||||
// Parse the body of a global script.
|
||||
Node globalBody();
|
||||
|
||||
// Parse a module.
|
||||
Node standaloneModule(Handle<ModuleObject*> module);
|
||||
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
// Let a few threads hammer on memory with atomics to provoke errors
|
||||
// in exclusion work. This test is not 100% fail-safe: the test may
|
||||
// pass despite a bug, but this is unlikely.
|
||||
|
||||
if (!(this.SharedArrayBuffer && this.getSharedArrayBuffer && this.setSharedArrayBuffer && this.evalInWorker))
|
||||
quit(0);
|
||||
|
||||
try {
|
||||
// This will fail with --no-threads.
|
||||
evalInWorker("37");
|
||||
}
|
||||
catch (e) {
|
||||
quit(0);
|
||||
}
|
||||
|
||||
// Map an Int32Array on shared memory. The first location is used as
|
||||
// a counter, each worker counts up on exit and the main thread will
|
||||
// wait until the counter reaches the number of workers. The other
|
||||
// elements are contended accumulators where we count up and down very
|
||||
// rapidly and for a long time, any failure in mutual exclusion should
|
||||
// lead to errors in the result. (For example, the test fails almost
|
||||
// immediately when I disable simulation of mutual exclusion in the
|
||||
// ARM simulator.)
|
||||
|
||||
const numWorkers = 4; // You're not meant to change this
|
||||
const iterCount = 255; // Nor this
|
||||
const sabLength = 1024; // Nor this
|
||||
|
||||
const oddResult = (function () {
|
||||
var v = 0;
|
||||
for ( var j=0 ; j < numWorkers ; j++ )
|
||||
v |= (iterCount << (8 * j));
|
||||
return v;
|
||||
})();
|
||||
|
||||
const evenResult = 0;
|
||||
|
||||
const sab = new SharedArrayBuffer(sabLength);
|
||||
|
||||
setSharedArrayBuffer(sab);
|
||||
|
||||
const iab = new SharedInt32Array(sab);
|
||||
|
||||
function testRun(limit) {
|
||||
console.log("Limit = " + limit);
|
||||
|
||||
// Fork off workers to hammer on memory.
|
||||
for ( var i=0 ; i < numWorkers ; i++ ) {
|
||||
evalInWorker(`
|
||||
const iab = new SharedInt32Array(getSharedArrayBuffer());
|
||||
const v = 1 << (8 * ${i});
|
||||
for ( var i=0 ; i < ${limit} ; i++ ) {
|
||||
for ( var k=0 ; k < ${iterCount} ; k++ ) {
|
||||
if (i & 1) {
|
||||
for ( var j=1 ; j < iab.length ; j++ )
|
||||
Atomics.sub(iab, j, v);
|
||||
}
|
||||
else {
|
||||
for ( var j=1 ; j < iab.length ; j++ )
|
||||
Atomics.add(iab, j, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
Atomics.add(iab, 0, 1);
|
||||
`);
|
||||
}
|
||||
|
||||
// Wait...
|
||||
while (Atomics.load(iab, 0) != numWorkers)
|
||||
;
|
||||
Atomics.store(iab, 0, 0);
|
||||
|
||||
// Check the results and clear the array again.
|
||||
const v = (limit & 1) ? oddResult : evenResult;
|
||||
for ( var i=1 ; i < iab.length ; i++ ) {
|
||||
assertEq(iab[i], v);
|
||||
iab[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Under some configurations the test can take a while to run (and may
|
||||
// saturate the CPU since it runs four workers); try not to time out.
|
||||
|
||||
var then = new Date();
|
||||
testRun(1);
|
||||
if (new Date() - then < 20000) {
|
||||
testRun(2);
|
||||
if (new Date() - then < 30000) {
|
||||
testRun(3);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
// |jit-test| error: TypeError
|
||||
|
||||
var hits = 0;
|
||||
with(f_arg => constructor.f_arg([3, 4, 5], null)) var length = 257751;
|
||||
let get = () => 4,
|
||||
hits = new Intl.Proxy([f_arg]),
|
||||
y = ($ERROR < 1970) ? 1969 : 1970;
|
|
@ -0,0 +1,6 @@
|
|||
eval(`
|
||||
with ({}) {
|
||||
var f = function() {};
|
||||
}
|
||||
function f() {}
|
||||
`);
|
|
@ -38,7 +38,9 @@
|
|||
#include "asmjs/AsmJSValidate.h"
|
||||
#include "jit/arm/Assembler-arm.h"
|
||||
#include "jit/arm/disasm/Constants-arm.h"
|
||||
#include "jit/AtomicOperations.h"
|
||||
#include "vm/Runtime.h"
|
||||
#include "vm/SharedMem.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
|
@ -1127,6 +1129,8 @@ Simulator::Simulator()
|
|||
cacheLockHolder_ = nullptr;
|
||||
#endif
|
||||
redirection_ = nullptr;
|
||||
exclusiveMonitorHeld_ = false;
|
||||
exclusiveMonitor_ = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -1478,6 +1482,27 @@ Simulator::setCallResult(int64_t res)
|
|||
set_register(r1, static_cast<int32_t>(res >> 32));
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::exclusiveMonitorSet(uint64_t value)
|
||||
{
|
||||
exclusiveMonitor_ = value;
|
||||
exclusiveMonitorHeld_ = true;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
Simulator::exclusiveMonitorGetAndClear(bool* held)
|
||||
{
|
||||
*held = exclusiveMonitorHeld_;
|
||||
exclusiveMonitorHeld_ = false;
|
||||
return *held ? exclusiveMonitor_ : 0;
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::exclusiveMonitorClear()
|
||||
{
|
||||
exclusiveMonitorHeld_ = false;
|
||||
}
|
||||
|
||||
int
|
||||
Simulator::readW(int32_t addr, SimInstruction* instr)
|
||||
{
|
||||
|
@ -1504,14 +1529,66 @@ Simulator::writeW(int32_t addr, int value, SimInstruction* instr)
|
|||
}
|
||||
}
|
||||
|
||||
// For the time being, define Relaxed operations in terms of SeqCst
|
||||
// operations - we don't yet need Relaxed operations anywhere else in
|
||||
// the system, and the distinction is not important to the simulation
|
||||
// at the level where we're operating.
|
||||
|
||||
template<typename T>
|
||||
static
|
||||
T loadRelaxed(SharedMem<T*> addr)
|
||||
{
|
||||
return AtomicOperations::loadSeqCst(addr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static
|
||||
T compareExchangeRelaxed(SharedMem<T*> addr, T oldval, T newval)
|
||||
{
|
||||
return AtomicOperations::compareExchangeSeqCst(addr, oldval, newval);
|
||||
}
|
||||
|
||||
int
|
||||
Simulator::readExW(int32_t addr, SimInstruction* instr)
|
||||
{
|
||||
// The regexp engine emits unaligned loads, so we don't check for them here
|
||||
// like most of the other methods do.
|
||||
if ((addr & 3) == 0 || !HasAlignmentFault()) {
|
||||
SharedMem<int32_t*> ptr = SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));
|
||||
int32_t value = loadRelaxed(ptr);
|
||||
exclusiveMonitorSet(value);
|
||||
return value;
|
||||
} else {
|
||||
printf("Unaligned write at 0x%08x, pc=%p\n", addr, instr);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
int32_t
|
||||
Simulator::writeExW(int32_t addr, int value, SimInstruction* instr)
|
||||
{
|
||||
if ((addr & 3) == 0) {
|
||||
SharedMem<int32_t*> ptr = SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));
|
||||
bool held;
|
||||
int32_t expected = int32_t(exclusiveMonitorGetAndClear(&held));
|
||||
if (!held)
|
||||
return 1;
|
||||
int32_t old = compareExchangeRelaxed(ptr, expected, int32_t(value));
|
||||
return old != expected;
|
||||
} else {
|
||||
printf("Unaligned write at 0x%08x, pc=%p\n", addr, instr);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t
|
||||
Simulator::readHU(int32_t addr, SimInstruction* instr)
|
||||
{
|
||||
// The regexp engine emits unaligned loads, so we don't check for them here
|
||||
// like most of the other methods do.
|
||||
if ((addr & 1) == 0 || !HasAlignmentFault()) {
|
||||
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
|
||||
return *ptr;
|
||||
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
|
||||
return *ptr;
|
||||
}
|
||||
printf("Unaligned unsigned halfword read at 0x%08x, pc=%p\n", addr, instr);
|
||||
MOZ_CRASH();
|
||||
|
@ -1554,6 +1631,39 @@ Simulator::writeH(int32_t addr, int16_t value, SimInstruction* instr)
|
|||
}
|
||||
}
|
||||
|
||||
uint16_t
|
||||
Simulator::readExHU(int32_t addr, SimInstruction* instr)
|
||||
{
|
||||
// The regexp engine emits unaligned loads, so we don't check for them here
|
||||
// like most of the other methods do.
|
||||
if ((addr & 1) == 0 || !HasAlignmentFault()) {
|
||||
SharedMem<uint16_t*> ptr = SharedMem<uint16_t*>::shared(reinterpret_cast<uint16_t*>(addr));
|
||||
uint16_t value = loadRelaxed(ptr);
|
||||
exclusiveMonitorSet(value);
|
||||
return value;
|
||||
}
|
||||
printf("Unaligned atomic unsigned halfword read at 0x%08x, pc=%p\n", addr, instr);
|
||||
MOZ_CRASH();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t
|
||||
Simulator::writeExH(int32_t addr, uint16_t value, SimInstruction* instr)
|
||||
{
|
||||
if ((addr & 1) == 0) {
|
||||
SharedMem<uint16_t*> ptr = SharedMem<uint16_t*>::shared(reinterpret_cast<uint16_t*>(addr));
|
||||
bool held;
|
||||
uint16_t expected = uint16_t(exclusiveMonitorGetAndClear(&held));
|
||||
if (!held)
|
||||
return 1;
|
||||
uint16_t old = compareExchangeRelaxed(ptr, expected, value);
|
||||
return old != expected;
|
||||
} else {
|
||||
printf("Unaligned atomic unsigned halfword write at 0x%08x, pc=%p\n", addr, instr);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t
|
||||
Simulator::readBU(int32_t addr)
|
||||
{
|
||||
|
@ -1561,6 +1671,27 @@ Simulator::readBU(int32_t addr)
|
|||
return *ptr;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
Simulator::readExBU(int32_t addr)
|
||||
{
|
||||
SharedMem<uint8_t*> ptr = SharedMem<uint8_t*>::shared(reinterpret_cast<uint8_t*>(addr));
|
||||
uint8_t value = loadRelaxed(ptr);
|
||||
exclusiveMonitorSet(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
int32_t
|
||||
Simulator::writeExB(int32_t addr, uint8_t value)
|
||||
{
|
||||
SharedMem<uint8_t*> ptr = SharedMem<uint8_t*>::shared(reinterpret_cast<uint8_t*>(addr));
|
||||
bool held;
|
||||
uint8_t expected = uint8_t(exclusiveMonitorGetAndClear(&held));
|
||||
if (!held)
|
||||
return 1;
|
||||
uint8_t old = compareExchangeRelaxed(ptr, expected, value);
|
||||
return old != expected;
|
||||
}
|
||||
|
||||
int8_t
|
||||
Simulator::readB(int32_t addr)
|
||||
{
|
||||
|
@ -1607,6 +1738,49 @@ Simulator::writeDW(int32_t addr, int32_t value1, int32_t value2)
|
|||
}
|
||||
}
|
||||
|
||||
int32_t
|
||||
Simulator::readExDW(int32_t addr, int32_t* hibits)
|
||||
{
|
||||
#if defined(__clang__) && defined(__i386)
|
||||
// This is OK for now, we don't yet generate LDREXD.
|
||||
MOZ_CRASH("Unimplemented - 8-byte atomics are unsupported in Clang on i386");
|
||||
#else
|
||||
if ((addr & 3) == 0) {
|
||||
SharedMem<uint64_t*> ptr = SharedMem<uint64_t*>::shared(reinterpret_cast<uint64_t*>(addr));
|
||||
uint64_t value = loadRelaxed(ptr);
|
||||
exclusiveMonitorSet(value);
|
||||
*hibits = int32_t(value);
|
||||
return int32_t(value >> 32);
|
||||
}
|
||||
printf("Unaligned read at 0x%08x\n", addr);
|
||||
MOZ_CRASH();
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int32_t
|
||||
Simulator::writeExDW(int32_t addr, int32_t value1, int32_t value2)
|
||||
{
|
||||
#if defined(__clang__) && defined(__i386)
|
||||
// This is OK for now, we don't yet generate STREXD.
|
||||
MOZ_CRASH("Unimplemented - 8-byte atomics are unsupported in Clang on i386");
|
||||
#else
|
||||
if ((addr & 3) == 0) {
|
||||
SharedMem<uint64_t*> ptr = SharedMem<uint64_t*>::shared(reinterpret_cast<uint64_t*>(addr));
|
||||
uint64_t value = (uint64_t(value1) << 32) | uint32_t(value2);
|
||||
bool held;
|
||||
uint64_t expected = exclusiveMonitorGetAndClear(&held);
|
||||
if (!held)
|
||||
return 1;
|
||||
uint64_t old = compareExchangeRelaxed(ptr, expected, value);
|
||||
return old != expected;
|
||||
} else {
|
||||
printf("Unaligned write at 0x%08x\n", addr);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
Simulator::stackLimit() const
|
||||
{
|
||||
|
@ -2554,33 +2728,27 @@ Simulator::decodeType01(SimInstruction* instr)
|
|||
} else {
|
||||
if (instr->bits(disasm::ExclusiveOpHi, disasm::ExclusiveOpLo) == disasm::ExclusiveOpcode) {
|
||||
// Load-exclusive / store-exclusive.
|
||||
//
|
||||
// Bare-bones simulation: the store always succeeds, and we
|
||||
// do not execute any fences. Also, we allow readDW and
|
||||
// writeDW to split the memory transaction.
|
||||
//
|
||||
// The next step up would involve remembering the value
|
||||
// that was read with load-exclusive so that we could use
|
||||
// compareExchange for the store-exclusive, and to
|
||||
// implement atomic doubleword read and write.
|
||||
//
|
||||
// Also see DMB/DSB/ISB below.
|
||||
if (instr->bit(disasm::ExclusiveLoad)) {
|
||||
int rn = instr->rnValue();
|
||||
int rt = instr->rtValue();
|
||||
int32_t address = get_register(rn);
|
||||
switch (instr->bits(disasm::ExclusiveSizeHi, disasm::ExclusiveSizeLo)) {
|
||||
case disasm::ExclusiveWord:
|
||||
set_register(rt, readW(address, instr));
|
||||
set_register(rt, readExW(address, instr));
|
||||
break;
|
||||
case disasm::ExclusiveDouble:
|
||||
set_dw_register(rt, readDW(address));
|
||||
case disasm::ExclusiveDouble: {
|
||||
MOZ_ASSERT((rt % 2) == 0);
|
||||
int32_t hibits;
|
||||
int32_t lobits = readExDW(address, &hibits);
|
||||
set_register(rt, lobits);
|
||||
set_register(rt+1, hibits);
|
||||
break;
|
||||
}
|
||||
case disasm::ExclusiveByte:
|
||||
set_register(rt, readBU(address));
|
||||
set_register(rt, readExBU(address));
|
||||
break;
|
||||
case disasm::ExclusiveHalf:
|
||||
set_register(rt, readHU(address, instr));
|
||||
set_register(rt, readExHU(address, instr));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
@ -2589,24 +2757,25 @@ Simulator::decodeType01(SimInstruction* instr)
|
|||
int rt = instr->bits(3,0);
|
||||
int32_t address = get_register(rn);
|
||||
int32_t value = get_register(rt);
|
||||
int32_t result = 0;
|
||||
switch (instr->bits(disasm::ExclusiveSizeHi, disasm::ExclusiveSizeLo)) {
|
||||
case disasm::ExclusiveWord:
|
||||
writeW(address, value, instr);
|
||||
result = writeExW(address, value, instr);
|
||||
break;
|
||||
case disasm::ExclusiveDouble: {
|
||||
MOZ_ASSERT((rt % 2) == 0);
|
||||
int32_t value2 = get_register(rt+1);
|
||||
writeDW(address, value, value2);
|
||||
break;
|
||||
MOZ_ASSERT((rt % 2) == 0);
|
||||
int32_t value2 = get_register(rt+1);
|
||||
result = writeExDW(address, value, value2);
|
||||
break;
|
||||
}
|
||||
case disasm::ExclusiveByte:
|
||||
writeB(address, (uint8_t)value);
|
||||
result = writeExB(address, (uint8_t)value);
|
||||
break;
|
||||
case disasm::ExclusiveHalf:
|
||||
writeH(address, (uint16_t)value, instr);
|
||||
result = writeExH(address, (uint16_t)value, instr);
|
||||
break;
|
||||
}
|
||||
set_register(rd, 0);
|
||||
set_register(rd, result);
|
||||
}
|
||||
} else {
|
||||
MOZ_CRASH(); // Not used atm
|
||||
|
@ -3329,12 +3498,15 @@ Simulator::decodeType7CoprocessorIns(SimInstruction* instr)
|
|||
int CRn = instr->bits(19,16);
|
||||
int CRm = instr->bits(3,0);
|
||||
if (opc1 == 0 && opc2 == 4 && CRn == 7 && CRm == 10) {
|
||||
// ARMv6 DSB instruction - do nothing now, see comments above
|
||||
// ARMv6 DSB instruction. We do not use DSB.
|
||||
MOZ_CRASH("DSB not implemented");
|
||||
} else if (opc1 == 0 && opc2 == 5 && CRn == 7 && CRm == 10) {
|
||||
// ARMv6 DMB instruction - do nothing now, see comments above
|
||||
// ARMv6 DMB instruction.
|
||||
AtomicOperations::fenceSeqCst();
|
||||
}
|
||||
else if (opc1 == 0 && opc2 == 4 && CRn == 7 && CRm == 5) {
|
||||
// ARMv6 ISB instruction - do nothing now, see comments above
|
||||
// ARMv6 ISB instruction. We do not use ISB.
|
||||
MOZ_CRASH("ISB not implemented");
|
||||
}
|
||||
else {
|
||||
MOZ_CRASH();
|
||||
|
@ -4156,16 +4328,16 @@ Simulator::decodeSpecialCondition(SimInstruction* instr)
|
|||
break;
|
||||
case 0xA:
|
||||
if (instr->bits(31,20) == 0xf57) {
|
||||
// Minimal simulation: do nothing.
|
||||
//
|
||||
// If/when we upgrade load-exclusive and store-exclusive (above) to
|
||||
// do something useful concurrency-wise, we should also upgrade
|
||||
// these instructions.
|
||||
switch (instr->bits(7,4)) {
|
||||
case 5: // DMB
|
||||
case 4: // DSB
|
||||
case 6: // ISB
|
||||
AtomicOperations::fenceSeqCst();
|
||||
break;
|
||||
case 4: // DSB
|
||||
// We do not use DSB.
|
||||
MOZ_CRASH("DSB unimplemented");
|
||||
case 6: // ISB
|
||||
// We do not use ISB.
|
||||
MOZ_CRASH("ISB unimplemented");
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
|
|
@ -248,18 +248,30 @@ class Simulator
|
|||
inline void writeB(int32_t addr, uint8_t value);
|
||||
inline void writeB(int32_t addr, int8_t value);
|
||||
|
||||
inline uint8_t readExBU(int32_t addr);
|
||||
inline int32_t writeExB(int32_t addr, uint8_t value);
|
||||
|
||||
inline uint16_t readHU(int32_t addr, SimInstruction* instr);
|
||||
inline int16_t readH(int32_t addr, SimInstruction* instr);
|
||||
// Note: Overloaded on the sign of the value.
|
||||
inline void writeH(int32_t addr, uint16_t value, SimInstruction* instr);
|
||||
inline void writeH(int32_t addr, int16_t value, SimInstruction* instr);
|
||||
|
||||
inline uint16_t readExHU(int32_t addr, SimInstruction* instr);
|
||||
inline int32_t writeExH(int32_t addr, uint16_t value, SimInstruction* instr);
|
||||
|
||||
inline int readW(int32_t addr, SimInstruction* instr);
|
||||
inline void writeW(int32_t addr, int value, SimInstruction* instr);
|
||||
|
||||
inline int readExW(int32_t addr, SimInstruction* instr);
|
||||
inline int writeExW(int32_t addr, int value, SimInstruction* instr);
|
||||
|
||||
int32_t* readDW(int32_t addr);
|
||||
void writeDW(int32_t addr, int32_t value1, int32_t value2);
|
||||
|
||||
int32_t readExDW(int32_t addr, int32_t* hibits);
|
||||
int32_t writeExDW(int32_t addr, int32_t value1, int32_t value2);
|
||||
|
||||
// Executing is handled based on the instruction type.
|
||||
// Both type 0 and type 1 rolled into one.
|
||||
void decodeType01(SimInstruction* instr);
|
||||
|
@ -432,6 +444,15 @@ class Simulator
|
|||
MOZ_ASSERT(cacheLockHolder_);
|
||||
redirection_ = redirection;
|
||||
}
|
||||
|
||||
private:
|
||||
// Exclusive access monitor
|
||||
void exclusiveMonitorSet(uint64_t value);
|
||||
uint64_t exclusiveMonitorGetAndClear(bool* held);
|
||||
void exclusiveMonitorClear();
|
||||
|
||||
bool exclusiveMonitorHeld_;
|
||||
uint64_t exclusiveMonitor_;
|
||||
};
|
||||
|
||||
#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror) \
|
||||
|
|
|
@ -324,7 +324,7 @@ struct FloatRegister
|
|||
k_(k)
|
||||
{ }
|
||||
|
||||
constexpr FloatRegister(uint32_t code)
|
||||
explicit constexpr FloatRegister(uint32_t code)
|
||||
: code_(FloatRegisters::Code(code & 31)),
|
||||
k_(FloatRegisters::Kind(code >> 5))
|
||||
{ }
|
||||
|
|
|
@ -34,14 +34,14 @@ static constexpr ARMRegister ScratchReg64 = { ScratchReg, 64 };
|
|||
static constexpr Register ScratchReg2 = { Registers::ip1 };
|
||||
static constexpr ARMRegister ScratchReg2_64 = { ScratchReg2, 64 };
|
||||
|
||||
static constexpr FloatRegister ScratchDoubleReg = { FloatRegisters::d31 };
|
||||
static constexpr FloatRegister ReturnDoubleReg = { FloatRegisters::d0 };
|
||||
static constexpr FloatRegister ScratchDoubleReg = { FloatRegisters::d31, FloatRegisters::Double };
|
||||
static constexpr FloatRegister ReturnDoubleReg = { FloatRegisters::d0, FloatRegisters::Double };
|
||||
|
||||
static constexpr FloatRegister ReturnFloat32Reg = { FloatRegisters::s0 , FloatRegisters::Single };
|
||||
static constexpr FloatRegister ScratchFloat32Reg = { FloatRegisters::s31 , FloatRegisters::Single };
|
||||
static constexpr FloatRegister ReturnFloat32Reg = { FloatRegisters::s0, FloatRegisters::Single };
|
||||
static constexpr FloatRegister ScratchFloat32Reg = { FloatRegisters::s31, FloatRegisters::Single };
|
||||
|
||||
static constexpr Register InvalidReg = { Registers::invalid_reg };
|
||||
static constexpr FloatRegister InvalidFloatReg = { FloatRegisters::invalid_fpreg };
|
||||
static constexpr FloatRegister InvalidFloatReg = { FloatRegisters::invalid_fpreg, FloatRegisters::Single };
|
||||
|
||||
static constexpr FloatRegister ReturnInt32x4Reg = InvalidFloatReg;
|
||||
static constexpr FloatRegister ReturnFloat32x4Reg = InvalidFloatReg;
|
||||
|
@ -64,8 +64,8 @@ static constexpr Register ZeroRegister = { Registers::sp };
|
|||
static constexpr ARMRegister ZeroRegister64 = { Registers::sp, 64 };
|
||||
static constexpr ARMRegister ZeroRegister32 = { Registers::sp, 32 };
|
||||
|
||||
static constexpr FloatRegister ReturnFloatReg = { FloatRegisters::d0 };
|
||||
static constexpr FloatRegister ScratchFloatReg = { FloatRegisters::d31 };
|
||||
static constexpr FloatRegister ReturnFloatReg = { FloatRegisters::d0, FloatRegisters::Single };
|
||||
static constexpr FloatRegister ScratchFloatReg = { FloatRegisters::d31, FloatRegisters::Single };
|
||||
|
||||
static constexpr FloatRegister ReturnSimdReg = InvalidFloatReg;
|
||||
static constexpr FloatRegister ScratchSimdReg = InvalidFloatReg;
|
||||
|
@ -142,7 +142,7 @@ static constexpr Register AsmJSIonExitRegD2 = r4;
|
|||
static constexpr Register JSReturnReg_Type = r3;
|
||||
static constexpr Register JSReturnReg_Data = r2;
|
||||
|
||||
static constexpr FloatRegister NANReg = { FloatRegisters::d14 };
|
||||
static constexpr FloatRegister NANReg = { FloatRegisters::d14, FloatRegisters::Single };
|
||||
// N.B. r8 isn't listed as an aapcs temp register, but we can use it as such because we never
|
||||
// use return-structs.
|
||||
static constexpr Register CallTempNonArgRegs[] = { r8, r9, r10, r11, r12, r13, r14, r15 };
|
||||
|
|
|
@ -50,8 +50,8 @@ static constexpr Register ExtractTemp1 = r25;
|
|||
// since we keep the return address for calls there.
|
||||
|
||||
// FloatReg0 must be equal to ReturnFloatReg.
|
||||
static constexpr FloatRegister FloatReg0 = { FloatRegisters::v0 };
|
||||
static constexpr FloatRegister FloatReg1 = { FloatRegisters::v1 };
|
||||
static constexpr FloatRegister FloatReg0 = { FloatRegisters::v0, FloatRegisters::Single };
|
||||
static constexpr FloatRegister FloatReg1 = { FloatRegisters::v1, FloatRegisters::Single };
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
|
|
@ -312,24 +312,43 @@ Shape::fixupAfterMovingGC()
|
|||
void
|
||||
Shape::fixupGetterSetterForBarrier(JSTracer* trc)
|
||||
{
|
||||
// Relocating the getterObj or setterObj will change our location in our
|
||||
// parent's KidsHash, so remove ourself first if we're going to get moved.
|
||||
if (parent && !parent->inDictionary() && parent->kids.isHash()) {
|
||||
KidsHash* kh = parent->kids.toHash();
|
||||
MOZ_ASSERT(kh->lookup(StackShape(this)));
|
||||
kh->remove(StackShape(this));
|
||||
}
|
||||
if (!hasGetterValue() && !hasSetterValue())
|
||||
return;
|
||||
|
||||
if (hasGetterObject())
|
||||
TraceManuallyBarrieredEdge(trc, &asAccessorShape().getterObj, "getterObj");
|
||||
if (hasSetterObject())
|
||||
TraceManuallyBarrieredEdge(trc, &asAccessorShape().setterObj, "setterObj");
|
||||
JSObject* priorGetter = asAccessorShape().getterObj;
|
||||
JSObject* priorSetter = asAccessorShape().setterObj;
|
||||
if (!priorGetter && !priorSetter)
|
||||
return;
|
||||
|
||||
JSObject* postGetter = priorGetter;
|
||||
JSObject* postSetter = priorSetter;
|
||||
if (priorGetter)
|
||||
TraceManuallyBarrieredEdge(trc, &postGetter, "getterObj");
|
||||
if (priorSetter)
|
||||
TraceManuallyBarrieredEdge(trc, &postSetter, "setterObj");
|
||||
if (priorGetter == postGetter && priorSetter == postSetter)
|
||||
return;
|
||||
|
||||
if (parent && !parent->inDictionary() && parent->kids.isHash()) {
|
||||
// Relocating the getterObj or setterObj will have changed our location
|
||||
// in our parent's KidsHash, so take care to update it. We must do this
|
||||
// before we update the shape itself, since the shape is used to match
|
||||
// the original entry in the hash set.
|
||||
|
||||
StackShape original(this);
|
||||
StackShape updated(this);
|
||||
updated.rawGetter = reinterpret_cast<GetterOp>(postGetter);
|
||||
updated.rawSetter = reinterpret_cast<SetterOp>(postSetter);
|
||||
|
||||
KidsHash* kh = parent->kids.toHash();
|
||||
MOZ_ASSERT(!kh->lookup(StackShape(this)));
|
||||
MOZ_ALWAYS_TRUE(kh->putNew(StackShape(this), this));
|
||||
MOZ_ALWAYS_TRUE(kh->rekeyAs(original, updated, this));
|
||||
}
|
||||
|
||||
asAccessorShape().getterObj = postGetter;
|
||||
asAccessorShape().setterObj = postSetter;
|
||||
|
||||
MOZ_ASSERT_IF(parent && !parent->inDictionary() && parent->kids.isHash(),
|
||||
parent->kids.toHash()->has(StackShape(this)));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -396,8 +396,8 @@ inline bool
|
|||
CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script,
|
||||
HandleObject scopeChain, HandleObject varObj)
|
||||
{
|
||||
MOZ_ASSERT(script->bindings.numBodyLevelLexicals() == 0);
|
||||
|
||||
// We don't need to check body-level lexical bindings for conflict. Eval
|
||||
// scripts always execute under their own lexical scope.
|
||||
if (script->bindings.numVars() == 0)
|
||||
return true;
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ skip-if = buildapp == 'mulet'
|
|||
[test_bug793433.xul]
|
||||
[test_bug795275.xul]
|
||||
[test_bug799348.xul]
|
||||
skip-if = os == 'win' && debug #Bug 1210876
|
||||
[test_bug801241.xul]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[test_bug812415.xul]
|
||||
|
|
|
@ -1126,6 +1126,7 @@ protected:
|
|||
enum GradientParsingFlags {
|
||||
eGradient_Repeating = 1 << 0, // repeating-{linear|radial}-gradient
|
||||
eGradient_MozLegacy = 1 << 1, // -moz-{linear|radial}-gradient
|
||||
eGradient_WebkitLegacy = 1 << 2, // -webkit-{linear|radial}-gradient
|
||||
};
|
||||
bool ParseLinearGradient(nsCSSValue& aValue, uint8_t aFlags);
|
||||
bool ParseRadialGradient(nsCSSValue& aValue, uint8_t aFlags);
|
||||
|
@ -7481,6 +7482,10 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue,
|
|||
StringBeginsWith(tmp, NS_LITERAL_STRING("-moz-"))) {
|
||||
tmp.Rebind(tmp, 5);
|
||||
gradientFlags |= eGradient_MozLegacy;
|
||||
} else if (sWebkitPrefixedAliasesEnabled &&
|
||||
StringBeginsWith(tmp, NS_LITERAL_STRING("-webkit-"))) {
|
||||
tmp.Rebind(tmp, 8);
|
||||
gradientFlags |= eGradient_WebkitLegacy;
|
||||
}
|
||||
if (StringBeginsWith(tmp, NS_LITERAL_STRING("repeating-"))) {
|
||||
tmp.Rebind(tmp, 10);
|
||||
|
@ -10681,10 +10686,12 @@ CSSParserImpl::IsFunctionTokenValidForBackgroundImage(
|
|||
funcName.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") ||
|
||||
funcName.LowerCaseEqualsLiteral("-moz-image-rect") ||
|
||||
funcName.LowerCaseEqualsLiteral("-moz-element") ||
|
||||
(ShouldUseUnprefixingService() &&
|
||||
((sWebkitPrefixedAliasesEnabled || ShouldUseUnprefixingService()) &&
|
||||
(funcName.LowerCaseEqualsLiteral("-webkit-gradient") ||
|
||||
funcName.LowerCaseEqualsLiteral("-webkit-linear-gradient") ||
|
||||
funcName.LowerCaseEqualsLiteral("-webkit-radial-gradient")));
|
||||
funcName.LowerCaseEqualsLiteral("-webkit-radial-gradient") ||
|
||||
funcName.LowerCaseEqualsLiteral("-webkit-repeating-linear-gradient") ||
|
||||
funcName.LowerCaseEqualsLiteral("-webkit-repeating-radial-gradient")));
|
||||
}
|
||||
|
||||
// Parse one item of the background shorthand property.
|
||||
|
|
|
@ -616,6 +616,49 @@ var unbalancedGradientAndElementValues = [
|
|||
"-moz-element(#a()",
|
||||
];
|
||||
|
||||
if (IsCSSPropertyPrefEnabled("layout.css.prefixes.webkit")) {
|
||||
// Extend gradient lists with valid/invalid webkit-prefixed expressions:
|
||||
validGradientAndElementValues.push(
|
||||
// Basic linear-gradient syntax (valid when prefixed or unprefixed):
|
||||
"-webkit-linear-gradient(red, green, blue)",
|
||||
|
||||
// Angled linear-gradients (valid when prefixed or unprefixed):
|
||||
"-webkit-linear-gradient(135deg, red, blue)",
|
||||
"-webkit-linear-gradient(280deg, red 60%, blue)",
|
||||
|
||||
// Basic radial-gradient syntax (valid when prefixed or unprefixed):
|
||||
"-webkit-radial-gradient(circle, white, black)",
|
||||
"-webkit-radial-gradient(circle, white, black)",
|
||||
"-webkit-radial-gradient(ellipse closest-side, white, black)",
|
||||
"-webkit-radial-gradient(circle farthest-corner, white, black)",
|
||||
|
||||
// Repeating examples:
|
||||
"-webkit-repeating-linear-gradient(red 10%, blue 30%)",
|
||||
"-webkit-repeating-linear-gradient(30deg, pink 20px, orange 70px)",
|
||||
"-webkit-repeating-radial-gradient(circle, red, blue 10%, red 20%)",
|
||||
"-webkit-repeating-radial-gradient(circle farthest-corner, gray 10px, yellow 20px)"
|
||||
);
|
||||
|
||||
invalidGradientAndElementValues.push(
|
||||
// Syntax that's invalid for all types of gradients:
|
||||
// * empty gradient expressions:
|
||||
"-webkit-linear-gradient()",
|
||||
"-webkit-radial-gradient()",
|
||||
"-webkit-repeating-linear-gradient()",
|
||||
"-webkit-repeating-radial-gradient()",
|
||||
|
||||
// Syntax that's invalid for both -webkit & -moz, but valid for unprefixed:
|
||||
// XXXdholbert (populated in a later patch)
|
||||
|
||||
// Syntax that's invalid for both -webkit & unprefixed, but valid for -moz:
|
||||
// * initial length
|
||||
"-webkit-linear-gradient(10px, red, blue)"
|
||||
|
||||
// Syntax that's invalid for -webkit, but valid for -moz & unprefixed:
|
||||
// XXXdholbert (populated in a later patch)
|
||||
);
|
||||
}
|
||||
|
||||
var gCSSProperties = {
|
||||
"animation": {
|
||||
domProp: "animation",
|
||||
|
|
|
@ -727,9 +727,9 @@ Http2Stream::AdjustInitialWindow()
|
|||
return;
|
||||
}
|
||||
|
||||
uint8_t *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
|
||||
EnsureBuffer(mTxInlineFrame, mTxInlineFrameUsed + Http2Session::kFrameHeaderBytes + 4,
|
||||
mTxInlineFrameUsed, mTxInlineFrameSize);
|
||||
uint8_t *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
|
||||
mTxInlineFrameUsed += Http2Session::kFrameHeaderBytes + 4;
|
||||
|
||||
mSession->CreateFrameHeader(packet, 4,
|
||||
|
@ -757,9 +757,9 @@ Http2Stream::AdjustPushedPriority()
|
|||
if (mPushSource->RecvdFin() || mPushSource->RecvdReset())
|
||||
return;
|
||||
|
||||
uint8_t *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
|
||||
EnsureBuffer(mTxInlineFrame, mTxInlineFrameUsed + Http2Session::kFrameHeaderBytes + 5,
|
||||
mTxInlineFrameUsed, mTxInlineFrameSize);
|
||||
uint8_t *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
|
||||
mTxInlineFrameUsed += Http2Session::kFrameHeaderBytes + 5;
|
||||
|
||||
mSession->CreateFrameHeader(packet, 5,
|
||||
|
|
|
@ -443,35 +443,10 @@ nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
|
|||
mOriginHeaderPrincipal(aRequestingPrincipal),
|
||||
mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
|
||||
mRequestApproved(false),
|
||||
mHasBeenCrossSite(false),
|
||||
mIsPreflight(false)
|
||||
mHasBeenCrossSite(false)
|
||||
{
|
||||
}
|
||||
|
||||
nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
|
||||
nsIPrincipal* aRequestingPrincipal,
|
||||
bool aWithCredentials,
|
||||
const nsCString& aPreflightMethod,
|
||||
const nsTArray<nsCString>& aPreflightHeaders)
|
||||
: mOuterListener(aOuter),
|
||||
mRequestingPrincipal(aRequestingPrincipal),
|
||||
mOriginHeaderPrincipal(aRequestingPrincipal),
|
||||
mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
|
||||
mRequestApproved(false),
|
||||
mHasBeenCrossSite(false),
|
||||
mIsPreflight(true),
|
||||
#ifdef DEBUG
|
||||
mInited(false),
|
||||
#endif
|
||||
mPreflightMethod(aPreflightMethod),
|
||||
mPreflightHeaders(aPreflightHeaders)
|
||||
{
|
||||
for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) {
|
||||
ToLowerCase(mPreflightHeaders[i]);
|
||||
}
|
||||
mPreflightHeaders.Sort();
|
||||
}
|
||||
|
||||
nsCORSListenerProxy::~nsCORSListenerProxy()
|
||||
{
|
||||
}
|
||||
|
@ -570,7 +545,6 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
// For synthesized responses, we don't need to perform any checks.
|
||||
// Note: This would be unsafe if we ever changed our behavior to allow
|
||||
// service workers to intercept CORS preflights.
|
||||
MOZ_ASSERT(!mIsPreflight);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -606,68 +580,6 @@ nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
|
|||
}
|
||||
}
|
||||
|
||||
if (mIsPreflight) {
|
||||
bool succeedded;
|
||||
rv = http->GetRequestSucceeded(&succeedded);
|
||||
if (NS_FAILED(rv) || !succeedded) {
|
||||
LogBlockedRequest(aRequest, "CORSPreflightDidNotSucceed", nullptr);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
nsAutoCString headerVal;
|
||||
// The "Access-Control-Allow-Methods" header contains a comma separated
|
||||
// list of method names.
|
||||
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
|
||||
headerVal);
|
||||
bool foundMethod = mPreflightMethod.EqualsLiteral("GET") ||
|
||||
mPreflightMethod.EqualsLiteral("HEAD") ||
|
||||
mPreflightMethod.EqualsLiteral("POST");
|
||||
nsCCharSeparatedTokenizer methodTokens(headerVal, ',');
|
||||
while(methodTokens.hasMoreTokens()) {
|
||||
const nsDependentCSubstring& method = methodTokens.nextToken();
|
||||
if (method.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (!NS_IsValidHTTPToken(method)) {
|
||||
LogBlockedRequest(aRequest, "CORSInvalidAllowMethod",
|
||||
NS_ConvertUTF8toUTF16(method).get());
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
foundMethod |= mPreflightMethod.Equals(method);
|
||||
}
|
||||
if (!foundMethod) {
|
||||
LogBlockedRequest(aRequest, "CORSMethodNotFound", nullptr);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
// The "Access-Control-Allow-Headers" header contains a comma separated
|
||||
// list of header names.
|
||||
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
|
||||
headerVal);
|
||||
nsTArray<nsCString> headers;
|
||||
nsCCharSeparatedTokenizer headerTokens(headerVal, ',');
|
||||
while(headerTokens.hasMoreTokens()) {
|
||||
const nsDependentCSubstring& header = headerTokens.nextToken();
|
||||
if (header.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (!NS_IsValidHTTPToken(header)) {
|
||||
LogBlockedRequest(aRequest, "CORSInvalidAllowHeader",
|
||||
NS_ConvertUTF8toUTF16(header).get());
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
headers.AppendElement(header);
|
||||
}
|
||||
for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) {
|
||||
if (!headers.Contains(mPreflightHeaders[i],
|
||||
nsCaseInsensitiveCStringArrayComparator())) {
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowHeaderFromPreflight",
|
||||
NS_ConvertUTF8toUTF16(mPreflightHeaders[i]).get());
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -959,11 +871,7 @@ nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel,
|
|||
// can't return early on failure.
|
||||
nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aChannel);
|
||||
if (internal) {
|
||||
if (mIsPreflight) {
|
||||
rv = internal->SetCorsMode(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT);
|
||||
} else {
|
||||
rv = internal->SetCorsMode(nsIHttpChannelInternal::CORS_MODE_CORS);
|
||||
}
|
||||
rv = internal->SetCorsMode(nsIHttpChannelInternal::CORS_MODE_CORS);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = internal->SetCorsIncludeCredentials(mWithCredentials);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -1019,30 +927,8 @@ nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel,
|
|||
rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), origin, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Add preflight headers if this is a preflight request
|
||||
if (mIsPreflight) {
|
||||
rv = http->
|
||||
SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"),
|
||||
mPreflightMethod, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!mPreflightHeaders.IsEmpty()) {
|
||||
nsAutoCString headers;
|
||||
for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) {
|
||||
if (i != 0) {
|
||||
headers += ',';
|
||||
}
|
||||
headers += mPreflightHeaders[i];
|
||||
}
|
||||
rv = http->
|
||||
SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
|
||||
headers, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
// Make cookie-less if needed
|
||||
if (mIsPreflight || !mWithCredentials) {
|
||||
if (!mWithCredentials) {
|
||||
nsLoadFlags flags;
|
||||
rv = http->GetLoadFlags(&flags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -1065,16 +951,18 @@ class nsCORSPreflightListener final : public nsIStreamListener,
|
|||
public nsIChannelEventSink
|
||||
{
|
||||
public:
|
||||
nsCORSPreflightListener(nsIChannel* aOuterChannel,
|
||||
nsIStreamListener* aOuterListener,
|
||||
nsISupports* aOuterContext,
|
||||
nsIPrincipal* aReferrerPrincipal,
|
||||
nsCORSPreflightListener(nsIPrincipal* aReferrerPrincipal,
|
||||
nsICorsPreflightCallback* aCallback,
|
||||
bool aWithCredentials)
|
||||
: mOuterChannel(aOuterChannel), mOuterListener(aOuterListener),
|
||||
mOuterContext(aOuterContext), mReferrerPrincipal(aReferrerPrincipal),
|
||||
mCallback(aCallback), mWithCredentials(aWithCredentials)
|
||||
{ }
|
||||
bool aWithCredentials,
|
||||
const nsCString& aPreflightMethod,
|
||||
const nsTArray<nsCString>& aPreflightHeaders)
|
||||
: mPreflightMethod(aPreflightMethod),
|
||||
mPreflightHeaders(aPreflightHeaders),
|
||||
mReferrerPrincipal(aReferrerPrincipal),
|
||||
mCallback(aCallback),
|
||||
mWithCredentials(aWithCredentials)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
|
@ -1082,14 +970,15 @@ public:
|
|||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
|
||||
nsresult CheckPreflightRequestApproved(nsIRequest* aRequest);
|
||||
|
||||
private:
|
||||
~nsCORSPreflightListener() {}
|
||||
|
||||
void AddResultToCache(nsIRequest* aRequest);
|
||||
|
||||
nsCOMPtr<nsIChannel> mOuterChannel;
|
||||
nsCOMPtr<nsIStreamListener> mOuterListener;
|
||||
nsCOMPtr<nsISupports> mOuterContext;
|
||||
nsCString mPreflightMethod;
|
||||
nsTArray<nsCString> mPreflightHeaders;
|
||||
nsCOMPtr<nsIPrincipal> mReferrerPrincipal;
|
||||
nsCOMPtr<nsICorsPreflightCallback> mCallback;
|
||||
bool mWithCredentials;
|
||||
|
@ -1216,12 +1105,21 @@ NS_IMETHODIMP
|
|||
nsCORSPreflightListener::OnStartRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext)
|
||||
{
|
||||
nsresult status;
|
||||
nsresult rv = aRequest->GetStatus(&status);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = status;
|
||||
#ifdef DEBUG
|
||||
{
|
||||
nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
|
||||
bool responseSynthesized = false;
|
||||
if (internal &&
|
||||
NS_SUCCEEDED(internal->GetResponseSynthesized(&responseSynthesized))) {
|
||||
// For synthesized responses, we don't need to perform any checks.
|
||||
// This would be unsafe if we ever changed our behavior to allow
|
||||
// service workers to intercept CORS preflights.
|
||||
MOZ_ASSERT(!responseSynthesized);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult rv = CheckPreflightRequestApproved(aRequest);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Everything worked, try to cache and then fire off the actual request.
|
||||
|
@ -1240,9 +1138,6 @@ nsCORSPreflightListener::OnStopRequest(nsIRequest *aRequest,
|
|||
nsISupports *aContext,
|
||||
nsresult aStatus)
|
||||
{
|
||||
mOuterChannel = nullptr;
|
||||
mOuterListener = nullptr;
|
||||
mOuterContext = nullptr;
|
||||
mCallback = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1275,6 +1170,82 @@ nsCORSPreflightListener::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCORSPreflightListener::CheckPreflightRequestApproved(nsIRequest* aRequest)
|
||||
{
|
||||
nsresult status;
|
||||
nsresult rv = aRequest->GetStatus(&status);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS(status, status);
|
||||
|
||||
// Test that things worked on a HTTP level
|
||||
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
|
||||
nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
|
||||
NS_ENSURE_STATE(internal);
|
||||
|
||||
bool succeedded;
|
||||
rv = http->GetRequestSucceeded(&succeedded);
|
||||
if (NS_FAILED(rv) || !succeedded) {
|
||||
LogBlockedRequest(aRequest, "CORSPreflightDidNotSucceed", nullptr);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
nsAutoCString headerVal;
|
||||
// The "Access-Control-Allow-Methods" header contains a comma separated
|
||||
// list of method names.
|
||||
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
|
||||
headerVal);
|
||||
bool foundMethod = mPreflightMethod.EqualsLiteral("GET") ||
|
||||
mPreflightMethod.EqualsLiteral("HEAD") ||
|
||||
mPreflightMethod.EqualsLiteral("POST");
|
||||
nsCCharSeparatedTokenizer methodTokens(headerVal, ',');
|
||||
while(methodTokens.hasMoreTokens()) {
|
||||
const nsDependentCSubstring& method = methodTokens.nextToken();
|
||||
if (method.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (!NS_IsValidHTTPToken(method)) {
|
||||
LogBlockedRequest(aRequest, "CORSInvalidAllowMethod",
|
||||
NS_ConvertUTF8toUTF16(method).get());
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
foundMethod |= mPreflightMethod.Equals(method);
|
||||
}
|
||||
if (!foundMethod) {
|
||||
LogBlockedRequest(aRequest, "CORSMethodNotFound", nullptr);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
// The "Access-Control-Allow-Headers" header contains a comma separated
|
||||
// list of header names.
|
||||
http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
|
||||
headerVal);
|
||||
nsTArray<nsCString> headers;
|
||||
nsCCharSeparatedTokenizer headerTokens(headerVal, ',');
|
||||
while(headerTokens.hasMoreTokens()) {
|
||||
const nsDependentCSubstring& header = headerTokens.nextToken();
|
||||
if (header.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (!NS_IsValidHTTPToken(header)) {
|
||||
LogBlockedRequest(aRequest, "CORSInvalidAllowHeader",
|
||||
NS_ConvertUTF8toUTF16(header).get());
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
headers.AppendElement(header);
|
||||
}
|
||||
for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) {
|
||||
if (!headers.Contains(mPreflightHeaders[i],
|
||||
nsCaseInsensitiveCStringArrayComparator())) {
|
||||
LogBlockedRequest(aRequest, "CORSMissingAllowHeaderFromPreflight",
|
||||
NS_ConvertUTF8toUTF16(mPreflightHeaders[i]).get());
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCORSPreflightListener::GetInterface(const nsIID & aIID, void **aResult)
|
||||
{
|
||||
|
@ -1293,7 +1264,6 @@ nsCORSListenerProxy::RemoveFromCorsPreflightCache(nsIURI* aURI,
|
|||
|
||||
nsresult
|
||||
nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
|
||||
nsIStreamListener* aListener,
|
||||
nsIPrincipal* aPrincipal,
|
||||
nsICorsPreflightCallback* aCallback,
|
||||
bool aWithCredentials,
|
||||
|
@ -1302,6 +1272,11 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
|
|||
{
|
||||
*aPreflightChannel = nullptr;
|
||||
|
||||
if (gDisableCORS) {
|
||||
LogBlockedRequest(aRequestChannel, "CORSDisabled", nullptr);
|
||||
return NS_ERROR_DOM_BAD_URI;
|
||||
}
|
||||
|
||||
nsAutoCString method;
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequestChannel));
|
||||
NS_ENSURE_TRUE(httpChannel, NS_ERROR_UNEXPECTED);
|
||||
|
@ -1347,13 +1322,15 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
|
|||
rv = aRequestChannel->GetLoadFlags(&loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Preflight requests should never be intercepted by service workers.
|
||||
// Preflight requests should never be intercepted by service workers and
|
||||
// are always anonymous.
|
||||
// NOTE: We ignore CORS checks on synthesized responses (see the CORS
|
||||
// preflights, then we need to extend the GetResponseSynthesized() check in
|
||||
// nsCORSListenerProxy::CheckRequestApproved()). If we change our behavior
|
||||
// here and allow service workers to intercept CORS preflights, then that
|
||||
// check won't be safe any more.
|
||||
loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
|
||||
loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER |
|
||||
nsIRequest::LOAD_ANONYMOUS;
|
||||
|
||||
nsCOMPtr<nsIChannel> preflightChannel;
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(preflightChannel),
|
||||
|
@ -1364,17 +1341,45 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
|
|||
loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set method and headers
|
||||
nsCOMPtr<nsIHttpChannel> preHttp = do_QueryInterface(preflightChannel);
|
||||
NS_ASSERTION(preHttp, "Failed to QI to nsIHttpChannel!");
|
||||
|
||||
rv = preHttp->SetRequestMethod(NS_LITERAL_CSTRING("OPTIONS"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = preHttp->
|
||||
SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"),
|
||||
method, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsTArray<nsCString> preflightHeaders;
|
||||
if (!aUnsafeHeaders.IsEmpty()) {
|
||||
for (uint32_t i = 0; i < aUnsafeHeaders.Length(); ++i) {
|
||||
preflightHeaders.AppendElement();
|
||||
ToLowerCase(aUnsafeHeaders[i], preflightHeaders[i]);
|
||||
}
|
||||
preflightHeaders.Sort();
|
||||
nsAutoCString headers;
|
||||
for (uint32_t i = 0; i < preflightHeaders.Length(); ++i) {
|
||||
if (i != 0) {
|
||||
headers += ',';
|
||||
}
|
||||
headers += preflightHeaders[i];
|
||||
}
|
||||
rv = preHttp->
|
||||
SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
|
||||
headers, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Set up listener which will start the original channel
|
||||
nsCOMPtr<nsIStreamListener> preflightListener =
|
||||
new nsCORSPreflightListener(aRequestChannel, aListener, nullptr, aPrincipal,
|
||||
aCallback, aWithCredentials);
|
||||
NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
|
||||
nsRefPtr<nsCORSPreflightListener> preflightListener =
|
||||
new nsCORSPreflightListener(aPrincipal, aCallback, aWithCredentials,
|
||||
method, preflightHeaders);
|
||||
|
||||
rv = preflightChannel->SetNotificationCallbacks(preflightListener);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Start preflight
|
||||
if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
|
||||
|
@ -1383,8 +1388,7 @@ nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
|
|||
else {
|
||||
nsRefPtr<nsCORSListenerProxy> corsListener =
|
||||
new nsCORSListenerProxy(preflightListener, aPrincipal,
|
||||
aWithCredentials, method,
|
||||
aUnsafeHeaders);
|
||||
aWithCredentials);
|
||||
rv = corsListener->Init(preflightChannel, DataURIHandling::Disallow);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = preflightChannel->AsyncOpen(corsListener, nullptr);
|
||||
|
|
|
@ -73,15 +73,7 @@ private:
|
|||
|
||||
static void RemoveFromCorsPreflightCache(nsIURI* aURI,
|
||||
nsIPrincipal* aRequestingPrincipal);
|
||||
|
||||
nsCORSListenerProxy(nsIStreamListener* aOuter,
|
||||
nsIPrincipal* aRequestingPrincipal,
|
||||
bool aWithCredentials,
|
||||
const nsCString& aPreflightMethod,
|
||||
const nsTArray<nsCString>& aPreflightHeaders);
|
||||
|
||||
static nsresult StartCORSPreflight(nsIChannel* aRequestChannel,
|
||||
nsIStreamListener* aListener,
|
||||
nsIPrincipal* aPrincipal,
|
||||
nsICorsPreflightCallback* aCallback,
|
||||
bool aWithCredentials,
|
||||
|
@ -108,12 +100,9 @@ private:
|
|||
// an http: request to https: in nsHttpChannel::Connect() and hence
|
||||
// a request might not be marked as cross site request based on that promise.
|
||||
bool mHasBeenCrossSite;
|
||||
bool mIsPreflight;
|
||||
#ifdef DEBUG
|
||||
bool mInited;
|
||||
#endif
|
||||
nsCString mPreflightMethod;
|
||||
nsTArray<nsCString> mPreflightHeaders;
|
||||
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
|
||||
nsCOMPtr<nsIChannel> mOldRedirectChannel;
|
||||
nsCOMPtr<nsIChannel> mNewRedirectChannel;
|
||||
|
|
|
@ -456,7 +456,7 @@ nsHttpChannel::ContinueConnect()
|
|||
mInterceptCache != INTERCEPTED) {
|
||||
MOZ_ASSERT(!mPreflightChannel);
|
||||
nsresult rv =
|
||||
nsCORSListenerProxy::StartCORSPreflight(this, mListener,
|
||||
nsCORSListenerProxy::StartCORSPreflight(this,
|
||||
mPreflightPrincipal, this,
|
||||
mWithCredentials,
|
||||
mUnsafeHeaders,
|
||||
|
|
|
@ -9851,6 +9851,13 @@
|
|||
"kind": "count",
|
||||
"description": "Number of times the D3D11 compositor failed to get a texture sync handle."
|
||||
},
|
||||
"GFX_CRASH": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "enumerated",
|
||||
"n_values": 100,
|
||||
"releaseChannelCollection": "opt-out",
|
||||
"description": "Graphics Crash Reason (...)"
|
||||
},
|
||||
"PLUGIN_ACTIVATION_COUNT": {
|
||||
"alert_emails": ["cpeterson@mozilla.com"],
|
||||
"expires_in_version": "48",
|
||||
|
|
|
@ -15,7 +15,7 @@ DEFINES['CFLAGS'] = CONFIG['OS_CFLAGS']
|
|||
if CONFIG['OS_TARGET'] == 'Android':
|
||||
DEFINES['ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME']
|
||||
|
||||
if 'stlport' in CONFIG['STLPORT_LIBS']:
|
||||
if CONFIG['MOZ_ANDROID_CXX_STL'] == 'mozstlport':
|
||||
DEFINES['USE_STLPORT'] = True
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
|
|
@ -66,9 +66,9 @@ static nsWindow *gFocusedWindow = nullptr;
|
|||
NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget)
|
||||
|
||||
nsWindow::nsWindow()
|
||||
: mFramebuffer(nullptr)
|
||||
, mMappedBuffer(nullptr)
|
||||
{
|
||||
mFramebuffer = nullptr;
|
||||
|
||||
nsRefPtr<nsScreenManagerGonk> screenManager = nsScreenManagerGonk::GetInstance();
|
||||
screenManager->Initialize();
|
||||
|
||||
|
@ -630,20 +630,18 @@ gralloc_module()
|
|||
}
|
||||
|
||||
static SurfaceFormat
|
||||
HalFormatToSurfaceFormat(int aHalFormat, int* bytepp)
|
||||
HalFormatToSurfaceFormat(int aHalFormat)
|
||||
{
|
||||
switch (aHalFormat) {
|
||||
case HAL_PIXEL_FORMAT_RGBA_8888:
|
||||
*bytepp = 4;
|
||||
return SurfaceFormat::R8G8B8A8;
|
||||
// Needs RB swap
|
||||
return SurfaceFormat::B8G8R8A8;
|
||||
case HAL_PIXEL_FORMAT_RGBX_8888:
|
||||
*bytepp = 4;
|
||||
return SurfaceFormat::R8G8B8X8;
|
||||
// Needs RB swap
|
||||
return SurfaceFormat::B8G8R8X8;
|
||||
case HAL_PIXEL_FORMAT_BGRA_8888:
|
||||
*bytepp = 4;
|
||||
return SurfaceFormat::B8G8R8A8;
|
||||
case HAL_PIXEL_FORMAT_RGB_565:
|
||||
*bytepp = 2;
|
||||
return SurfaceFormat::R5G6B5;
|
||||
default:
|
||||
MOZ_CRASH("Unhandled HAL pixel format");
|
||||
|
@ -651,26 +649,46 @@ HalFormatToSurfaceFormat(int aHalFormat, int* bytepp)
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
NeedsRBSwap(int aHalFormat)
|
||||
{
|
||||
switch (aHalFormat) {
|
||||
case HAL_PIXEL_FORMAT_RGBA_8888:
|
||||
return true;
|
||||
case HAL_PIXEL_FORMAT_RGBX_8888:
|
||||
return true;
|
||||
case HAL_PIXEL_FORMAT_BGRA_8888:
|
||||
return false;
|
||||
case HAL_PIXEL_FORMAT_RGB_565:
|
||||
return false;
|
||||
default:
|
||||
MOZ_CRASH("Unhandled HAL pixel format");
|
||||
return false; // not reached
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
already_AddRefed<DrawTarget>
|
||||
nsWindow::StartRemoteDrawing()
|
||||
{
|
||||
mFramebuffer = mScreen->DequeueBuffer();
|
||||
int width = mFramebuffer->width, height = mFramebuffer->height;
|
||||
void *vaddr;
|
||||
if (gralloc_module()->lock(gralloc_module(), mFramebuffer->handle,
|
||||
GRALLOC_USAGE_SW_READ_NEVER |
|
||||
GRALLOC_USAGE_SW_WRITE_OFTEN |
|
||||
GRALLOC_USAGE_HW_FB,
|
||||
0, 0, width, height, &vaddr)) {
|
||||
0, 0, width, height,
|
||||
reinterpret_cast<void**>(&mMappedBuffer))) {
|
||||
EndRemoteDrawing();
|
||||
return nullptr;
|
||||
}
|
||||
int bytepp;
|
||||
SurfaceFormat format = HalFormatToSurfaceFormat(mScreen->GetSurfaceFormat(),
|
||||
&bytepp);
|
||||
SurfaceFormat format = HalFormatToSurfaceFormat(mScreen->GetSurfaceFormat());
|
||||
mFramebufferTarget = Factory::CreateDrawTargetForData(
|
||||
BackendType::CAIRO, (uint8_t*)vaddr,
|
||||
IntSize(width, height), mFramebuffer->stride * bytepp, format);
|
||||
BackendType::CAIRO,
|
||||
mMappedBuffer,
|
||||
IntSize(width, height),
|
||||
mFramebuffer->stride * gfx::BytesPerPixel(format),
|
||||
format);
|
||||
if (!mFramebufferTarget) {
|
||||
MOZ_CRASH("nsWindow::StartRemoteDrawing failed in CreateDrawTargetForData");
|
||||
}
|
||||
|
@ -687,12 +705,24 @@ nsWindow::StartRemoteDrawing()
|
|||
void
|
||||
nsWindow::EndRemoteDrawing()
|
||||
{
|
||||
if (mFramebufferTarget) {
|
||||
if (mFramebufferTarget && mFramebuffer) {
|
||||
IntSize size = mFramebufferTarget->GetSize();
|
||||
Rect rect(0, 0, size.width, size.height);
|
||||
RefPtr<SourceSurface> source = mBackBuffer->Snapshot();
|
||||
mFramebufferTarget->DrawSurface(source, rect, rect);
|
||||
gralloc_module()->unlock(gralloc_module(), mFramebuffer->handle);
|
||||
|
||||
// Convert from BGR to RGB
|
||||
// XXX this is a temporary solution. It consumes extra cpu cycles,
|
||||
// it should not be used on product device.
|
||||
if (NeedsRBSwap(mScreen->GetSurfaceFormat())) {
|
||||
LOGE("Very slow composition path, it should not be used on product!!!");
|
||||
SurfaceFormat format = HalFormatToSurfaceFormat(mScreen->GetSurfaceFormat());
|
||||
gfxUtils::ConvertBGRAtoRGBA(
|
||||
mMappedBuffer,
|
||||
mFramebuffer->stride * mFramebuffer->height * gfx::BytesPerPixel(format));
|
||||
mMappedBuffer = nullptr;
|
||||
gralloc_module()->unlock(gralloc_module(), mFramebuffer->handle);
|
||||
}
|
||||
}
|
||||
if (mFramebuffer) {
|
||||
mScreen->QueueBuffer(mFramebuffer);
|
||||
|
|
|
@ -142,6 +142,11 @@ protected:
|
|||
// framebuffer.
|
||||
mozilla::RefPtr<mozilla::gfx::DrawTarget> mFramebufferTarget;
|
||||
ANativeWindowBuffer* mFramebuffer;
|
||||
/**
|
||||
* Points to a mapped gralloc buffer between calls to lock and unlock.
|
||||
* Should be null outside of the lock-unlock pair.
|
||||
*/
|
||||
uint8_t* mMappedBuffer;
|
||||
// If we're using a BasicCompositor, this is our window back
|
||||
// buffer. The gralloc framebuffer driver expects us to draw the
|
||||
// entire framebuffer on every frame, but gecko expects the
|
||||
|
|
Загрузка…
Ссылка в новой задаче