зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b2g-inbound. a=merge
This commit is contained in:
Коммит
97acbd9776
|
@ -40,10 +40,12 @@ ifndef MOZ_PROFILE_USE
|
|||
$(TIERS) binaries:: CLOBBER $(configure_dir)/configure config.status backend.RecursiveMakeBackend
|
||||
ifndef JS_STANDALONE
|
||||
ifndef LIBXUL_SDK
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
$(TIERS) binaries:: $(topsrcdir)/js/src/configure js/src/config.status
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef JS_STANDALONE
|
||||
.PHONY: CLOBBER
|
||||
|
@ -105,12 +107,14 @@ install_manifest_depends = \
|
|||
|
||||
ifndef JS_STANDALONE
|
||||
ifndef LIBXUL_SDK
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
install_manifest_depends += \
|
||||
$(topsrcdir)/js/src/configure \
|
||||
js/src/config.status \
|
||||
$(NULL)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
.PHONY: install-manifests
|
||||
install-manifests: $(addprefix install-,$(install_manifests))
|
||||
|
|
|
@ -195,9 +195,17 @@ xpcAccessibleDocument::GetAccessible(Accessible* aAccessible)
|
|||
return xpcAcc;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
ShutdownAndRemove(const Accessible* aKey, nsRefPtr<xpcAccessibleGeneric>& aValue,
|
||||
void* aUnused)
|
||||
{
|
||||
aValue->Shutdown();
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
void
|
||||
xpcAccessibleDocument::Shutdown()
|
||||
{
|
||||
mCache.Clear();
|
||||
mCache.Enumerate(ShutdownAndRemove, nullptr);
|
||||
xpcAccessibleGeneric::Shutdown();
|
||||
}
|
||||
|
|
|
@ -867,12 +867,6 @@ bin/libfreebl_32int64_3.so
|
|||
@RESPATH@/crashreporter-override.ini
|
||||
#endif
|
||||
|
||||
; [Extensions]
|
||||
;
|
||||
#ifdef MOZ_ENABLE_GNOMEVFS
|
||||
bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
|
||||
#endif
|
||||
|
||||
[b2g]
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
#ifdef XP_WIN32
|
||||
|
|
|
@ -496,10 +496,7 @@
|
|||
#ifdef MOZ_ENABLE_GNOME_COMPONENT
|
||||
@RESPATH@/components/@DLL_PREFIX@mozgnome@DLL_SUFFIX@
|
||||
#endif
|
||||
#ifdef MOZ_ENABLE_GNOMEVFS
|
||||
@RESPATH@/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
|
||||
#endif
|
||||
#if defined(MOZ_ENABLE_DBUS) || defined(MOZ_ENABLE_GNOME_COMPONENT) || defined(MOZ_ENABLE_GNOMEVFS)
|
||||
#if defined(MOZ_ENABLE_DBUS) || defined(MOZ_ENABLE_GNOME_COMPONENT)
|
||||
@RESPATH@/components/components.manifest
|
||||
#endif
|
||||
@RESPATH@/components/nsINIProcessor.manifest
|
||||
|
|
|
@ -18,10 +18,10 @@ include $(topsrcdir)/config/config.mk
|
|||
|
||||
# L10n jobs are doing make -C config manually before anything else,
|
||||
# and need nsinstall to be built as a consequence.
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
export:: host
|
||||
|
||||
ifneq (WINNT,$(HOST_OS_ARCH))
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
# Ensure nsinstall is atomically created
|
||||
nsinstall$(HOST_BIN_SUFFIX): $(HOST_PROGRAM)
|
||||
cp $^ $@.tmp
|
||||
|
|
|
@ -96,10 +96,12 @@ $(addsuffix /$(CURRENT_TIER),$(filter-out config,$(CURRENT_DIRS))): config/$(CUR
|
|||
# is done with the config/host target. Note the config/host target only exists if
|
||||
# nsinstall is actually built, which it is not on Windows, because we use
|
||||
# nsinstall.py there.
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
ifneq (,$(filter config/host, $(compile_targets)))
|
||||
$(addsuffix /$(CURRENT_TIER),$(CURRENT_DIRS)): config/host
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
endif # ifeq ($(CURRENT_TIER),compile)
|
||||
|
||||
|
|
|
@ -627,14 +627,6 @@ libgnome/libgnome.h
|
|||
libgnomeui/gnome-icon-lookup.h
|
||||
libgnomeui/gnome-icon-theme.h
|
||||
libgnomeui/gnome-ui-init.h
|
||||
libgnomevfs/gnome-vfs-file-info.h
|
||||
libgnomevfs/gnome-vfs.h
|
||||
libgnomevfs/gnome-vfs-init.h
|
||||
libgnomevfs/gnome-vfs-mime.h
|
||||
libgnomevfs/gnome-vfs-mime-handlers.h
|
||||
libgnomevfs/gnome-vfs-mime-utils.h
|
||||
libgnomevfs/gnome-vfs-ops.h
|
||||
libgnomevfs/gnome-vfs-standard-callbacks.h
|
||||
lib$routines.h
|
||||
limits
|
||||
limits.h
|
||||
|
|
47
configure.in
47
configure.in
|
@ -69,7 +69,6 @@ GTK2_VERSION=2.18.0
|
|||
GTK3_VERSION=3.4.0
|
||||
WINDRES_VERSION=2.14.90
|
||||
W32API_VERSION=3.14
|
||||
GNOMEVFS_VERSION=2.0
|
||||
GNOMEUI_VERSION=2.2.0
|
||||
GCONF_VERSION=1.2.1
|
||||
STARTUP_NOTIFICATION_VERSION=0.8
|
||||
|
@ -4768,7 +4767,7 @@ then
|
|||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = GnomeVFS, GIO and GConf support module
|
||||
dnl = GIO and GConf support module
|
||||
dnl ========================================================
|
||||
|
||||
if test "$MOZ_X11"
|
||||
|
@ -4781,31 +4780,6 @@ then
|
|||
MOZ_ENABLE_GCONF=1
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = GnomeVFS support module
|
||||
dnl ========================================================
|
||||
MOZ_ARG_ENABLE_BOOL(gnomevfs,
|
||||
[ --enable-gnomevfs Enable GnomeVFS support (default: disabled)],
|
||||
MOZ_ENABLE_GNOMEVFS=force,
|
||||
MOZ_ENABLE_GNOMEVFS=)
|
||||
|
||||
if test "$MOZ_ENABLE_GNOMEVFS"
|
||||
then
|
||||
PKG_CHECK_MODULES(MOZ_GNOMEVFS, gnome-vfs-2.0 >= $GNOMEVFS_VERSION gnome-vfs-module-2.0 >= $GNOMEVFS_VERSION,[
|
||||
MOZ_GNOMEVFS_LIBS=`echo $MOZ_GNOMEVFS_LIBS | sed 's/-llinc\>//'`
|
||||
MOZ_ENABLE_GNOMEVFS=1
|
||||
AC_DEFINE(MOZ_ENABLE_GNOMEVFS)
|
||||
],[
|
||||
if test "$MOZ_ENABLE_GNOMEVFS" = "force"
|
||||
then
|
||||
AC_MSG_ERROR([* * * Could not find gnome-vfs-module-2.0 >= $GNOMEVFS_VERSION])
|
||||
fi
|
||||
MOZ_ENABLE_GNOMEVFS=
|
||||
])
|
||||
fi
|
||||
|
||||
AC_SUBST(MOZ_ENABLE_GNOMEVFS)
|
||||
|
||||
dnl ========================================================
|
||||
dnl = GIO support module
|
||||
dnl ========================================================
|
||||
|
@ -6266,19 +6240,6 @@ MOZ_ARG_ENABLE_STRING(extensions,
|
|||
done],
|
||||
MOZ_EXTENSIONS="$MOZ_EXTENSIONS_DEFAULT")
|
||||
|
||||
if test -z "$MOZ_ENABLE_GNOMEVFS" -a `echo "$MOZ_EXTENSIONS" | grep -c gnomevfs` -ne 0; then
|
||||
# Suppress warning on non-X11 platforms
|
||||
if test -n "$MOZ_X11"; then
|
||||
AC_MSG_WARN([Removing gnomevfs from MOZ_EXTENSIONS due to no --enable-gnomevfs.])
|
||||
fi
|
||||
MOZ_EXTENSIONS=`echo $MOZ_EXTENSIONS | sed -e 's|gnomevfs||'`
|
||||
fi
|
||||
|
||||
dnl Do not build gnomevfs with libxul based apps
|
||||
if test -n "$LIBXUL_SDK_DIR" -a `echo "$MOZ_EXTENSIONS" | grep -c gnomevfs` -ne 0; then
|
||||
MOZ_EXTENSIONS=`echo $MOZ_EXTENSIONS | sed -e 's|gnomevfs||'`
|
||||
fi
|
||||
|
||||
if test -z "$MOZ_ENABLE_GIO" -a `echo "$MOZ_EXTENSIONS" | grep -c gio` -ne 0; then
|
||||
# Suppress warning on non-X11 platforms
|
||||
if test -n "$MOZ_X11"; then
|
||||
|
@ -9092,9 +9053,11 @@ esac
|
|||
|
||||
if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -o -n "$MOZ_REPLACE_MALLOC"; then
|
||||
ac_configure_args="--build=$build --host=$target --enable-stats --with-jemalloc-prefix=je_ --disable-valgrind"
|
||||
# We're using memalign for _aligned_malloc in memory/build/mozmemory_wrap.c
|
||||
# on Windows, so just export memalign on all platforms.
|
||||
ac_configure_args="$ac_configure_args ac_cv_func_memalign=yes"
|
||||
if test -n "$MOZ_REPLACE_MALLOC"; then
|
||||
# When using replace_malloc, we always want memalign and valloc exported from jemalloc.
|
||||
ac_configure_args="$ac_configure_args ac_cv_func_memalign=yes"
|
||||
# When using replace_malloc, we always want valloc exported from jemalloc.
|
||||
ac_configure_args="$ac_configure_args ac_cv_func_valloc=yes"
|
||||
fi
|
||||
if test -n "$MOZ_JEMALLOC3"; then
|
||||
|
|
|
@ -11,5 +11,23 @@ DIRS += [
|
|||
'resources/content',
|
||||
]
|
||||
|
||||
TEST_DIRS += ['test']
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
'test/unit/xpcshell.ini',
|
||||
'test/unit_ipc/xpcshell.ini',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += [
|
||||
'test/chrome/mochitest.ini',
|
||||
'test/iframesandbox/mochitest.ini',
|
||||
'test/mochitest.ini',
|
||||
'test/navigation/mochitest.ini',
|
||||
]
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += [
|
||||
'test/chrome/chrome.ini',
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'test/browser/browser.ini',
|
||||
'test/navigation/browser.ini',
|
||||
]
|
||||
|
|
|
@ -48,7 +48,7 @@ skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') #Bug 9
|
|||
[test_bug404548.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
|
||||
[test_bug413310.html]
|
||||
skip-if = true || toolkit == 'android' || buildapp == 'b2g'
|
||||
skip-if = true
|
||||
# Disabled for too many intermittent failures (bug 719186)
|
||||
[test_bug475636.html]
|
||||
[test_bug509055.html]
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
'unit/xpcshell.ini',
|
||||
'unit_ipc/xpcshell.ini',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += [
|
||||
'chrome/mochitest.ini',
|
||||
'iframesandbox/mochitest.ini',
|
||||
'mochitest.ini',
|
||||
'navigation/mochitest.ini',
|
||||
]
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'browser/browser.ini',
|
||||
'navigation/browser.ini',
|
||||
]
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini']
|
|
@ -2,7 +2,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>Tests for the effect of setting a CSS animation's AnimationPlayer.startTime</title>
|
||||
<title>Tests for the effect of setting a CSS animation's
|
||||
AnimationPlayer.startTime</title>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
|
@ -39,7 +40,8 @@
|
|||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
|
||||
|
||||
|
||||
const CSS_ANIM_EVENTS = ['animationstart', 'animationiteration', 'animationend'];
|
||||
const CSS_ANIM_EVENTS =
|
||||
['animationstart', 'animationiteration', 'animationend'];
|
||||
const ANIM_DELAY_MS = 1000000; // 1000s
|
||||
const ANIM_DUR_MS = 1000000; // 1000s
|
||||
const ANIM_PROPERTY_VAL = 'anim ' + ANIM_DUR_MS + 'ms ' + ANIM_DELAY_MS + 'ms';
|
||||
|
@ -64,9 +66,6 @@ function startTimeForStartOfActiveInterval(timeline) {
|
|||
function startTimeForFiftyPercentThroughActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.5;
|
||||
}
|
||||
function startTimeForNinetyPercentThroughActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.9;
|
||||
}
|
||||
function startTimeForEndOfActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS;
|
||||
}
|
||||
|
@ -82,7 +81,6 @@ const UNANIMATED_POSITION = 10;
|
|||
const INITIAL_POSITION = 100;
|
||||
const TEN_PCT_POSITION = 110;
|
||||
const FIFTY_PCT_POSITION = 150;
|
||||
const NINETY_PCT_POSITION = 190;
|
||||
const END_POSITION = 200;
|
||||
|
||||
/**
|
||||
|
@ -275,17 +273,6 @@ function checkStateAtFiftyPctOfActiveInterval(player)
|
|||
'animation at the midpoint of the active interval');
|
||||
}
|
||||
|
||||
function checkStateAtNinetyPctOfActiveInterval(player)
|
||||
{
|
||||
// We don't test player.startTime since our caller just set it.
|
||||
|
||||
var div = player.source.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_between_inclusive(marginLeft, NINETY_PCT_POSITION, END_POSITION,
|
||||
'the computed value of margin-left should be close to the value at the ' +
|
||||
'end of the animation');
|
||||
}
|
||||
|
||||
// Called when startTime is set to the time the active interval ends.
|
||||
function checkStateAtActiveIntervalEndTime(player)
|
||||
{
|
||||
|
@ -303,7 +290,8 @@ function checkStateAtActiveIntervalEndTime(player)
|
|||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'by the animation at the end of the active duration');
|
||||
'by the animation at the end of the active duration when the ' +
|
||||
'animation-fill-mode is none');
|
||||
}
|
||||
|
||||
|
||||
|
@ -340,7 +328,8 @@ test(function(t)
|
|||
'Check setting of startTime actually works');
|
||||
|
||||
checkStateOnSettingStartTimeToAnimationCreationTime(player);
|
||||
}, 'Examine newly created Animation');
|
||||
}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
|
||||
'startTime');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
|
@ -359,12 +348,10 @@ async_test(function(t) {
|
|||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalStartTime(player);
|
||||
|
||||
player.startTime = startTimeForFiftyPercentThroughActiveInterval(player.timeline);
|
||||
player.startTime =
|
||||
startTimeForFiftyPercentThroughActiveInterval(player.timeline);
|
||||
checkStateAtFiftyPctOfActiveInterval(player);
|
||||
|
||||
player.startTime = startTimeForNinetyPercentThroughActiveInterval(player.timeline);
|
||||
checkStateAtNinetyPctOfActiveInterval(player);
|
||||
|
||||
player.startTime = startTimeForEndOfActiveInterval(player.timeline);
|
||||
return eventWatcher.waitForEvent('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
|
@ -389,27 +376,39 @@ async_test(function(t) {
|
|||
|
||||
player.startTime = startTimeForEndOfActiveInterval(player.timeline);
|
||||
|
||||
var previousTimelineTime = player.timeline.currentTime;
|
||||
|
||||
// Skipping over the active interval will dispatch an 'animationstart' then
|
||||
// an 'animationend' event. We need to wait for these events before we start
|
||||
// testing going backwards since EventWatcher will fail the test if it gets
|
||||
// an event that we haven't told it about.
|
||||
eventWatcher.waitForEvents(['animationstart', 'animationend']).then(t.step_func(function() {
|
||||
eventWatcher.waitForEvents(['animationstart',
|
||||
'animationend']).then(t.step_func(function() {
|
||||
assert_true(document.timeline.currentTime - previousTimelineTime <
|
||||
ANIM_DUR_MS,
|
||||
'Sanity check that seeking worked rather than the events ' +
|
||||
'firing after normal playback through the very long ' +
|
||||
'animation duration');
|
||||
|
||||
// Now we can start the tests for skipping backwards, but first we check
|
||||
// that after the events we're still in the same end time state:
|
||||
checkStateAtActiveIntervalEndTime(player);
|
||||
|
||||
player.startTime = startTimeForNinetyPercentThroughActiveInterval(player.timeline);
|
||||
player.startTime =
|
||||
startTimeForFiftyPercentThroughActiveInterval(player.timeline);
|
||||
|
||||
// Despite going backwards from after the end of the animation to just
|
||||
// before the end of the animation, we now expect an animationstart event
|
||||
// because we went from outside to inside the active interval.
|
||||
return eventWatcher.waitForEvent('animationstart');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtNinetyPctOfActiveInterval(player);
|
||||
|
||||
player.startTime = startTimeForFiftyPercentThroughActiveInterval(player.timeline);
|
||||
// Despite going backwards from after the end of the animation (to being
|
||||
// in the active interval), we now expect an 'animationstart' event
|
||||
// because the animation should go from being inactive to active.
|
||||
//
|
||||
// Calling checkStateAtFiftyPctOfActiveInterval will check computed style,
|
||||
// causing computed style to be updated and the 'animationstart' event to
|
||||
// be dispatched synchronously. We need to call waitForEvent first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
var promise = eventWatcher.waitForEvent('animationstart');
|
||||
checkStateAtFiftyPctOfActiveInterval(player);
|
||||
|
||||
return promise;
|
||||
})).then(t.step_func(function() {
|
||||
player.startTime = startTimeForStartOfActiveInterval(player.timeline);
|
||||
checkStateAtActiveIntervalStartTime(player);
|
||||
|
||||
|
@ -519,7 +518,8 @@ async_test(function(t) {
|
|||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
eventWatcher.waitForEvents(['animationstart', 'animationend']).then(function() {
|
||||
eventWatcher.waitForEvents(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
player.startTime = startTimeForBeforePhase(player.timeline);
|
||||
player.startTime = startTimeForAfterPhase(player.timeline);
|
||||
|
||||
|
@ -538,7 +538,8 @@ async_test(function(t) {
|
|||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
eventWatcher.waitForEvents(['animationstart', 'animationend']).then(function() {
|
||||
eventWatcher.waitForEvents(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
player.startTime = startTimeForActivePhase(player.timeline);
|
||||
player.startTime = startTimeForAfterPhase(player.timeline);
|
||||
|
||||
|
@ -558,12 +559,17 @@ async_test(function(t) {
|
|||
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
var storedCurrentTime;
|
||||
|
||||
player.ready.then(t.step_func(function() {
|
||||
storedCurrentTime = player.currentTime;
|
||||
player.startTime = null;
|
||||
return player.ready;
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
assert_equals(player.currentTime, storedCurrentTime,
|
||||
'Test that hold time is correct');
|
||||
t.done();
|
||||
});
|
||||
}, 'Setting startTime to null');
|
||||
|
@ -582,6 +588,8 @@ async_test(function(t) {
|
|||
'AnimationPlayer.startTime not null on ready Promise resolve');
|
||||
|
||||
player.pause();
|
||||
// After bug 1109390 we will need to wait here for the ready promise again
|
||||
|
||||
assert_equals(player.startTime, null,
|
||||
'AnimationPlayer.startTime is null after paused');
|
||||
assert_equals(player.playState, 'paused',
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<img src="foo" srcset="bar">
|
||||
<script>
|
||||
document.querySelector("img").removeAttribute('src');
|
||||
</script>
|
|
@ -72,3 +72,4 @@ load 903106.html
|
|||
load 916322-1.html
|
||||
load 916322-2.html
|
||||
load 1032654.html
|
||||
pref(dom.image.srcset.enabled,true) load 1141260.html
|
|
@ -4459,12 +4459,15 @@ ContentParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd)
|
|||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvOpenAnonymousTemporaryFile(FileDescriptor *aFD)
|
||||
ContentParent::RecvOpenAnonymousTemporaryFile(FileDescOrError *aFD)
|
||||
{
|
||||
PRFileDesc *prfd;
|
||||
nsresult rv = NS_OpenAnonymousTemporaryFile(&prfd);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
// Returning false will kill the child process; instead
|
||||
// propagate the error and let the child handle it.
|
||||
*aFD = rv;
|
||||
return true;
|
||||
}
|
||||
*aFD = FileDescriptor(FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prfd)));
|
||||
// The FileDescriptor object owns a duplicate of the file handle; we
|
||||
|
|
|
@ -762,7 +762,7 @@ private:
|
|||
RecvBackUpXResources(const FileDescriptor& aXSocketFd) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
RecvOpenAnonymousTemporaryFile(FileDescriptor* aFD) MOZ_OVERRIDE;
|
||||
RecvOpenAnonymousTemporaryFile(FileDescOrError* aFD) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
RecvKeygenProcessValue(const nsString& oldValue, const nsString& challenge,
|
||||
|
|
|
@ -335,6 +335,11 @@ union MaybeFileDesc {
|
|||
void_t;
|
||||
};
|
||||
|
||||
union FileDescOrError {
|
||||
FileDescriptor;
|
||||
nsresult;
|
||||
};
|
||||
|
||||
union OptionalContentId
|
||||
{
|
||||
ContentParentId;
|
||||
|
@ -827,7 +832,7 @@ parent:
|
|||
*/
|
||||
BackUpXResources(FileDescriptor aXSocketFd);
|
||||
|
||||
sync OpenAnonymousTemporaryFile() returns (FileDescriptor aFD);
|
||||
sync OpenAnonymousTemporaryFile() returns (FileDescOrError aFD);
|
||||
|
||||
/**
|
||||
* Keygen requires us to call it after a <keygen> element is parsed and
|
||||
|
|
|
@ -62,7 +62,10 @@ public:
|
|||
GMPAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallbackProxy* aCallback)
|
||||
: GMPAudioDecoder(aConfig, aTaskQueue, aCallback, new AudioCallbackAdapter(aCallback))
|
||||
: mConfig(aConfig)
|
||||
, mCallback(aCallback)
|
||||
, mGMP(nullptr)
|
||||
, mAdapter(new AudioCallbackAdapter(aCallback))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -71,11 +71,14 @@ public:
|
|||
layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallbackProxy* aCallback)
|
||||
: GMPVideoDecoder(aConfig, aLayersBackend, aImageContainer, aTaskQueue, aCallback,
|
||||
new VideoCallbackAdapter(aCallback,
|
||||
VideoInfo(aConfig.display_width,
|
||||
aConfig.display_height),
|
||||
aImageContainer))
|
||||
: mConfig(aConfig)
|
||||
, mCallback(aCallback)
|
||||
, mGMP(nullptr)
|
||||
, mHost(nullptr)
|
||||
, mAdapter(new VideoCallbackAdapter(aCallback,
|
||||
VideoInfo(aConfig.display_width,
|
||||
aConfig.display_height),
|
||||
aImageContainer))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -139,12 +139,14 @@ WMFMediaDataDecoder::Flush()
|
|||
void
|
||||
WMFMediaDataDecoder::ProcessDrain()
|
||||
{
|
||||
// Order the decoder to drain...
|
||||
if (FAILED(mDecoder->SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0))) {
|
||||
NS_WARNING("Failed to send DRAIN command to MFT");
|
||||
if (mDecoder) {
|
||||
// Order the decoder to drain...
|
||||
if (FAILED(mDecoder->SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0))) {
|
||||
NS_WARNING("Failed to send DRAIN command to MFT");
|
||||
}
|
||||
// Then extract all available output.
|
||||
ProcessOutput();
|
||||
}
|
||||
// Then extract all available output.
|
||||
ProcessOutput();
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,10 @@ endif
|
|||
testdir = $(abspath $(DEPTH)/_tests/xpcshell/dom/plugins/test/unit/)
|
||||
addonpath = $(testdir)/$(addon_file_name)
|
||||
|
||||
ifdef COMPILE_ENVIRONMENT
|
||||
libs::
|
||||
$(NSINSTALL) -D $(testdir)
|
||||
rm -f $(addonpath)
|
||||
cd $(srcdir) && zip -rD $(addonpath) install.rdf
|
||||
cd $(DIST) && zip -rD $(addonpath) $(foreach name,$(plugin_file_names),plugins/$(name))
|
||||
endif
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
SOURCES += [
|
||||
'nsGnomeVFSProtocolHandler.cpp',
|
||||
]
|
||||
|
||||
XPCOMBinaryComponent('nkgnomevfs')
|
||||
|
||||
# make sure this component is never statically linked into the main
|
||||
# application. this is necessary since we don't want to force users
|
||||
# to install gnome-vfs2 in order to use the rest of mozilla ;-)
|
||||
|
||||
CXXFLAGS += CONFIG['MOZ_GNOMEVFS_CFLAGS']
|
||||
|
||||
OS_LIBS += CONFIG['MOZ_GNOMEVFS_LIBS']
|
|
@ -1,988 +0,0 @@
|
|||
/* vim:set ts=2 sw=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// GnomeVFS v2.2.2 is missing G_BEGIN_DECLS in gnome-vfs-module-callback.h
|
||||
extern "C" {
|
||||
#include <libgnomevfs/gnome-vfs.h>
|
||||
#include <libgnomevfs/gnome-vfs-standard-callbacks.h>
|
||||
#include <libgnomevfs/gnome-vfs-mime-utils.h>
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsIAuthPrompt.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsIStandardURL.h"
|
||||
#include "nsIURL.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsINetUtil.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsError.h"
|
||||
#include "prlog.h"
|
||||
#include "prtime.h"
|
||||
#include "prprf.h"
|
||||
#include "plstr.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#define MOZ_GNOMEVFS_SCHEME "moz-gnomevfs"
|
||||
#define MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS "network.gnomevfs.supported-protocols"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// NSPR_LOG_MODULES=gnomevfs:5
|
||||
#ifdef PR_LOGGING
|
||||
static PRLogModuleInfo *sGnomeVFSLog;
|
||||
#define LOG(args) PR_LOG(sGnomeVFSLog, PR_LOG_DEBUG, args)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static nsresult
|
||||
MapGnomeVFSResult(GnomeVFSResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case GNOME_VFS_OK: return NS_OK;
|
||||
case GNOME_VFS_ERROR_NOT_FOUND: return NS_ERROR_FILE_NOT_FOUND;
|
||||
case GNOME_VFS_ERROR_INTERNAL: return NS_ERROR_UNEXPECTED;
|
||||
case GNOME_VFS_ERROR_BAD_PARAMETERS: return NS_ERROR_INVALID_ARG;
|
||||
case GNOME_VFS_ERROR_NOT_SUPPORTED: return NS_ERROR_NOT_AVAILABLE;
|
||||
case GNOME_VFS_ERROR_CORRUPTED_DATA: return NS_ERROR_FILE_CORRUPTED;
|
||||
case GNOME_VFS_ERROR_TOO_BIG: return NS_ERROR_FILE_TOO_BIG;
|
||||
case GNOME_VFS_ERROR_NO_SPACE: return NS_ERROR_FILE_NO_DEVICE_SPACE;
|
||||
case GNOME_VFS_ERROR_READ_ONLY:
|
||||
case GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM: return NS_ERROR_FILE_READ_ONLY;
|
||||
case GNOME_VFS_ERROR_INVALID_URI:
|
||||
case GNOME_VFS_ERROR_INVALID_HOST_NAME: return NS_ERROR_MALFORMED_URI;
|
||||
case GNOME_VFS_ERROR_ACCESS_DENIED:
|
||||
case GNOME_VFS_ERROR_NOT_PERMITTED:
|
||||
case GNOME_VFS_ERROR_LOGIN_FAILED: return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
case GNOME_VFS_ERROR_EOF: return NS_BASE_STREAM_CLOSED;
|
||||
case GNOME_VFS_ERROR_NOT_A_DIRECTORY: return NS_ERROR_FILE_NOT_DIRECTORY;
|
||||
case GNOME_VFS_ERROR_IN_PROGRESS: return NS_ERROR_IN_PROGRESS;
|
||||
case GNOME_VFS_ERROR_FILE_EXISTS: return NS_ERROR_FILE_ALREADY_EXISTS;
|
||||
case GNOME_VFS_ERROR_IS_DIRECTORY: return NS_ERROR_FILE_IS_DIRECTORY;
|
||||
case GNOME_VFS_ERROR_NO_MEMORY: return NS_ERROR_OUT_OF_MEMORY;
|
||||
case GNOME_VFS_ERROR_HOST_NOT_FOUND:
|
||||
case GNOME_VFS_ERROR_HOST_HAS_NO_ADDRESS: return NS_ERROR_UNKNOWN_HOST;
|
||||
case GNOME_VFS_ERROR_CANCELLED:
|
||||
case GNOME_VFS_ERROR_INTERRUPTED: return NS_ERROR_ABORT;
|
||||
case GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY: return NS_ERROR_FILE_DIR_NOT_EMPTY;
|
||||
case GNOME_VFS_ERROR_NAME_TOO_LONG: return NS_ERROR_FILE_NAME_TOO_LONG;
|
||||
case GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE: return NS_ERROR_UNKNOWN_PROTOCOL;
|
||||
|
||||
/* No special mapping for these error codes...
|
||||
|
||||
case GNOME_VFS_ERROR_GENERIC:
|
||||
case GNOME_VFS_ERROR_IO:
|
||||
case GNOME_VFS_ERROR_WRONG_FORMAT:
|
||||
case GNOME_VFS_ERROR_BAD_FILE:
|
||||
case GNOME_VFS_ERROR_NOT_OPEN:
|
||||
case GNOME_VFS_ERROR_INVALID_OPEN_MODE:
|
||||
case GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES:
|
||||
case GNOME_VFS_ERROR_LOOP:
|
||||
case GNOME_VFS_ERROR_DIRECTORY_BUSY:
|
||||
case GNOME_VFS_ERROR_TOO_MANY_LINKS:
|
||||
case GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM:
|
||||
case GNOME_VFS_ERROR_SERVICE_OBSOLETE:
|
||||
case GNOME_VFS_ERROR_PROTOCOL_ERROR:
|
||||
case GNOME_VFS_ERROR_NO_MASTER_BROWSER:
|
||||
|
||||
*/
|
||||
|
||||
// Make GCC happy
|
||||
default:
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
ProxiedAuthCallback(gconstpointer in,
|
||||
gsize in_size,
|
||||
gpointer out,
|
||||
gsize out_size,
|
||||
gpointer callback_data)
|
||||
{
|
||||
GnomeVFSModuleCallbackAuthenticationIn *authIn =
|
||||
(GnomeVFSModuleCallbackAuthenticationIn *) in;
|
||||
GnomeVFSModuleCallbackAuthenticationOut *authOut =
|
||||
(GnomeVFSModuleCallbackAuthenticationOut *) out;
|
||||
|
||||
LOG(("gnomevfs: ProxiedAuthCallback [uri=%s]\n", authIn->uri));
|
||||
|
||||
// Without a channel, we have no way of getting a prompter.
|
||||
nsIChannel *channel = (nsIChannel *) callback_data;
|
||||
if (!channel)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIAuthPrompt> prompt;
|
||||
NS_QueryNotificationCallbacks(channel, prompt);
|
||||
|
||||
// If no auth prompt, then give up. We could failover to using the
|
||||
// WindowWatcher service, but that might defeat a consumer's purposeful
|
||||
// attempt to disable authentication (for whatever reason).
|
||||
if (!prompt)
|
||||
return;
|
||||
|
||||
// Parse out the host and port...
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
channel->GetURI(getter_AddRefs(uri));
|
||||
if (!uri)
|
||||
return;
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
//
|
||||
// Make sure authIn->uri is consistent with the channel's URI.
|
||||
//
|
||||
// XXX This check is probably not IDN safe, and it might incorrectly
|
||||
// fire as a result of escaping differences. It's unclear what
|
||||
// kind of transforms GnomeVFS might have applied to the URI spec
|
||||
// that we originally gave to it. In spite of the likelihood of
|
||||
// false hits, this check is probably still valuable.
|
||||
//
|
||||
nsAutoCString spec;
|
||||
uri->GetSpec(spec);
|
||||
int uriLen = strlen(authIn->uri);
|
||||
if (!StringHead(spec, uriLen).Equals(nsDependentCString(authIn->uri, uriLen)))
|
||||
{
|
||||
LOG(("gnomevfs: [spec=%s authIn->uri=%s]\n", spec.get(), authIn->uri));
|
||||
NS_ERROR("URI mismatch");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
nsAutoCString scheme, hostPort;
|
||||
uri->GetScheme(scheme);
|
||||
uri->GetHostPort(hostPort);
|
||||
|
||||
// It doesn't make sense for either of these strings to be empty. What kind
|
||||
// of funky URI is this?
|
||||
if (scheme.IsEmpty() || hostPort.IsEmpty())
|
||||
return;
|
||||
|
||||
// Construct the single signon key. Altering the value of this key will
|
||||
// cause people's remembered passwords to be forgotten. Think carefully
|
||||
// before changing the way this key is constructed.
|
||||
nsAutoString key, realm;
|
||||
|
||||
NS_ConvertUTF8toUTF16 dispHost(scheme);
|
||||
dispHost.AppendLiteral("://");
|
||||
dispHost.Append(NS_ConvertUTF8toUTF16(hostPort));
|
||||
|
||||
key = dispHost;
|
||||
if (authIn->realm)
|
||||
{
|
||||
// We assume the realm string is ASCII. That might be a bogus assumption,
|
||||
// but we have no idea what encoding GnomeVFS is using, so for now we'll
|
||||
// limit ourselves to ISO-Latin-1. XXX What is a better solution?
|
||||
realm.Append('"');
|
||||
realm.Append(NS_ConvertASCIItoUTF16(authIn->realm));
|
||||
realm.Append('"');
|
||||
key.Append(' ');
|
||||
key.Append(realm);
|
||||
}
|
||||
|
||||
// Construct the message string...
|
||||
//
|
||||
// We use Necko's string bundle here. This code really should be encapsulated
|
||||
// behind some Necko API, after all this code is based closely on the code in
|
||||
// nsHttpChannel.cpp.
|
||||
|
||||
nsCOMPtr<nsIStringBundleService> bundleSvc =
|
||||
do_GetService(NS_STRINGBUNDLE_CONTRACTID);
|
||||
if (!bundleSvc)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIStringBundle> bundle;
|
||||
bundleSvc->CreateBundle("chrome://global/locale/commonDialogs.properties",
|
||||
getter_AddRefs(bundle));
|
||||
if (!bundle)
|
||||
return;
|
||||
|
||||
nsString message;
|
||||
if (!realm.IsEmpty())
|
||||
{
|
||||
const char16_t *strings[] = { realm.get(), dispHost.get() };
|
||||
bundle->FormatStringFromName(MOZ_UTF16("EnterUserPasswordForRealm"),
|
||||
strings, 2, getter_Copies(message));
|
||||
}
|
||||
else
|
||||
{
|
||||
const char16_t *strings[] = { dispHost.get() };
|
||||
bundle->FormatStringFromName(MOZ_UTF16("EnterUserPasswordFor"),
|
||||
strings, 1, getter_Copies(message));
|
||||
}
|
||||
if (message.IsEmpty())
|
||||
return;
|
||||
|
||||
// Prompt the user...
|
||||
nsresult rv;
|
||||
bool retval = false;
|
||||
char16_t *user = nullptr, *pass = nullptr;
|
||||
|
||||
rv = prompt->PromptUsernameAndPassword(nullptr, message.get(),
|
||||
key.get(),
|
||||
nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY,
|
||||
&user, &pass, &retval);
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
if (!retval || !user || !pass)
|
||||
return;
|
||||
|
||||
// XXX We need to convert the UTF-16 username and password from our dialog to
|
||||
// strings that GnomeVFS can understand. It's unclear what encoding GnomeVFS
|
||||
// expects, so for now we assume 7-bit ASCII. Hopefully, we can get a better
|
||||
// solution at some point.
|
||||
|
||||
// One copy is never enough...
|
||||
authOut->username = g_strdup(NS_LossyConvertUTF16toASCII(user).get());
|
||||
authOut->password = g_strdup(NS_LossyConvertUTF16toASCII(pass).get());
|
||||
|
||||
nsMemory::Free(user);
|
||||
nsMemory::Free(pass);
|
||||
}
|
||||
|
||||
struct nsGnomeVFSAuthCallbackEvent : public nsRunnable
|
||||
{
|
||||
gconstpointer in;
|
||||
gsize in_size;
|
||||
gpointer out;
|
||||
gsize out_size;
|
||||
gpointer callback_data;
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
ProxiedAuthCallback(in, in_size, out, out_size, callback_data);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
AuthCallback(gconstpointer in,
|
||||
gsize in_size,
|
||||
gpointer out,
|
||||
gsize out_size,
|
||||
gpointer callback_data)
|
||||
{
|
||||
// Need to proxy this callback over to the main thread. Synchronous dispatch
|
||||
// is required in order to provide data to the GnomeVFS callback.
|
||||
|
||||
nsRefPtr<nsGnomeVFSAuthCallbackEvent> ev = new nsGnomeVFSAuthCallbackEvent();
|
||||
if (!ev)
|
||||
return; // OOM
|
||||
|
||||
ev->in = in;
|
||||
ev->in_size = in_size;
|
||||
ev->out = out;
|
||||
ev->out_size = out_size;
|
||||
ev->callback_data = callback_data;
|
||||
|
||||
NS_DispatchToMainThread(ev, NS_DISPATCH_SYNC);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static gint
|
||||
FileInfoComparator(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const GnomeVFSFileInfo *ia = (const GnomeVFSFileInfo *) a;
|
||||
const GnomeVFSFileInfo *ib = (const GnomeVFSFileInfo *) b;
|
||||
|
||||
return strcasecmp(ia->name, ib->name);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsGnomeVFSInputStream MOZ_FINAL : public nsIInputStream
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
|
||||
nsGnomeVFSInputStream(const nsCString &uriSpec)
|
||||
: mSpec(uriSpec)
|
||||
, mChannel(nullptr)
|
||||
, mHandle(nullptr)
|
||||
, mBytesRemaining(UINT64_MAX)
|
||||
, mStatus(NS_OK)
|
||||
, mDirList(nullptr)
|
||||
, mDirListPtr(nullptr)
|
||||
, mDirBufCursor(0)
|
||||
, mDirOpen(false) {}
|
||||
|
||||
~nsGnomeVFSInputStream() { Close(); }
|
||||
|
||||
void SetChannel(nsIChannel *channel)
|
||||
{
|
||||
// We need to hold an owning reference to our channel. This is done
|
||||
// so we can access the channel's notification callbacks to acquire
|
||||
// a reference to a nsIAuthPrompt if we need to handle a GnomeVFS
|
||||
// authentication callback.
|
||||
//
|
||||
// However, the channel can only be accessed on the main thread, so
|
||||
// we have to be very careful with ownership. Moreover, it doesn't
|
||||
// support threadsafe addref/release, so proxying is the answer.
|
||||
//
|
||||
// Also, it's important to note that this likely creates a reference
|
||||
// cycle since the channel likely owns this stream. This reference
|
||||
// cycle is broken in our Close method.
|
||||
|
||||
NS_ADDREF(mChannel = channel);
|
||||
}
|
||||
|
||||
private:
|
||||
GnomeVFSResult DoOpen();
|
||||
GnomeVFSResult DoRead(char *aBuf, uint32_t aCount, uint32_t *aCountRead);
|
||||
nsresult SetContentTypeOfChannel(const char *contentType);
|
||||
|
||||
private:
|
||||
nsCString mSpec;
|
||||
nsIChannel *mChannel; // manually refcounted
|
||||
GnomeVFSHandle *mHandle;
|
||||
uint64_t mBytesRemaining;
|
||||
nsresult mStatus;
|
||||
GList *mDirList;
|
||||
GList *mDirListPtr;
|
||||
nsCString mDirBuf;
|
||||
uint32_t mDirBufCursor;
|
||||
bool mDirOpen;
|
||||
};
|
||||
|
||||
GnomeVFSResult
|
||||
nsGnomeVFSInputStream::DoOpen()
|
||||
{
|
||||
GnomeVFSResult rv;
|
||||
|
||||
NS_ASSERTION(mHandle == nullptr, "already open");
|
||||
|
||||
// Push a callback handler on the stack for this thread, so we can intercept
|
||||
// authentication requests from GnomeVFS. We'll use the channel to get a
|
||||
// nsIAuthPrompt instance.
|
||||
|
||||
gnome_vfs_module_callback_push(GNOME_VFS_MODULE_CALLBACK_AUTHENTICATION,
|
||||
AuthCallback, mChannel, nullptr);
|
||||
|
||||
// Query the mime type first (this could return nullptr).
|
||||
//
|
||||
// XXX We need to do this up-front in order to determine how to open the URI.
|
||||
// Unfortunately, the error code GNOME_VFS_ERROR_IS_DIRECTORY is not
|
||||
// always returned by gnome_vfs_open when we pass it a URI to a directory!
|
||||
// Otherwise, we could have used that as a way to failover to opening the
|
||||
// URI as a directory. Also, it would have been ideal if
|
||||
// gnome_vfs_get_file_info_from_handle were actually implemented by the
|
||||
// smb:// module, since that would have allowed us to potentially save a
|
||||
// round trip to the server to discover the mime type of the document in
|
||||
// the case where gnome_vfs_open would have been used. (Oh well! /me
|
||||
// throws hands up in the air and moves on...)
|
||||
|
||||
GnomeVFSFileInfo info = {0};
|
||||
rv = gnome_vfs_get_file_info(mSpec.get(), &info, GnomeVFSFileInfoOptions(
|
||||
GNOME_VFS_FILE_INFO_DEFAULT |
|
||||
GNOME_VFS_FILE_INFO_FOLLOW_LINKS));
|
||||
if (rv == GNOME_VFS_OK)
|
||||
{
|
||||
if (info.type == GNOME_VFS_FILE_TYPE_DIRECTORY)
|
||||
{
|
||||
rv = gnome_vfs_directory_list_load(&mDirList, mSpec.get(),
|
||||
GNOME_VFS_FILE_INFO_DEFAULT);
|
||||
|
||||
LOG(("gnomevfs: gnome_vfs_directory_list_load returned %d (%s) [spec=\"%s\"]\n",
|
||||
rv, gnome_vfs_result_to_string(rv), mSpec.get()));
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = gnome_vfs_open(&mHandle, mSpec.get(), GNOME_VFS_OPEN_READ);
|
||||
|
||||
LOG(("gnomevfs: gnome_vfs_open returned %d (%s) [spec=\"%s\"]\n",
|
||||
rv, gnome_vfs_result_to_string(rv), mSpec.get()));
|
||||
}
|
||||
}
|
||||
|
||||
gnome_vfs_module_callback_pop(GNOME_VFS_MODULE_CALLBACK_AUTHENTICATION);
|
||||
|
||||
if (rv == GNOME_VFS_OK)
|
||||
{
|
||||
if (mHandle)
|
||||
{
|
||||
// Here we set the content type of the channel to the value of the mime
|
||||
// type determined by GnomeVFS. However, if GnomeVFS is telling us that
|
||||
// the document is binary, we'll ignore that and keep the channel's
|
||||
// content type unspecified. That will enable our content type sniffing
|
||||
// algorithms. This should provide more consistent mime type handling.
|
||||
|
||||
if (info.mime_type && (strcmp(info.mime_type, APPLICATION_OCTET_STREAM) != 0))
|
||||
SetContentTypeOfChannel(info.mime_type);
|
||||
|
||||
mBytesRemaining = info.size;
|
||||
|
||||
// Update the content length attribute on the channel. We do this
|
||||
// synchronously without proxying. This hack is not as bad as it looks!
|
||||
if (mBytesRemaining > INT64_MAX) {
|
||||
mChannel->SetContentLength(-1);
|
||||
} else {
|
||||
mChannel->SetContentLength(mBytesRemaining);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mDirOpen = true;
|
||||
|
||||
// Sort mDirList
|
||||
mDirList = g_list_sort(mDirList, FileInfoComparator);
|
||||
mDirListPtr = mDirList;
|
||||
|
||||
// Write base URL (make sure it ends with a '/')
|
||||
mDirBuf.AppendLiteral("300: ");
|
||||
mDirBuf.Append(mSpec);
|
||||
if (mSpec.get()[mSpec.Length() - 1] != '/')
|
||||
mDirBuf.Append('/');
|
||||
mDirBuf.Append('\n');
|
||||
|
||||
// Write column names
|
||||
mDirBuf.AppendLiteral("200: filename content-length last-modified file-type\n");
|
||||
|
||||
// Write charset (assume UTF-8)
|
||||
// XXX is this correct?
|
||||
mDirBuf.AppendLiteral("301: UTF-8\n");
|
||||
|
||||
SetContentTypeOfChannel(APPLICATION_HTTP_INDEX_FORMAT);
|
||||
}
|
||||
}
|
||||
|
||||
gnome_vfs_file_info_clear(&info);
|
||||
return rv;
|
||||
}
|
||||
|
||||
GnomeVFSResult
|
||||
nsGnomeVFSInputStream::DoRead(char *aBuf, uint32_t aCount, uint32_t *aCountRead)
|
||||
{
|
||||
GnomeVFSResult rv;
|
||||
|
||||
if (mHandle)
|
||||
{
|
||||
GnomeVFSFileSize bytesRead;
|
||||
rv = gnome_vfs_read(mHandle, aBuf, aCount, &bytesRead);
|
||||
if (rv == GNOME_VFS_OK)
|
||||
{
|
||||
// aCount is 32-bit, so aCountRead is under 32-bit value.
|
||||
*aCountRead = (uint32_t) bytesRead;
|
||||
mBytesRemaining -= *aCountRead;
|
||||
}
|
||||
}
|
||||
else if (mDirOpen)
|
||||
{
|
||||
rv = GNOME_VFS_OK;
|
||||
|
||||
while (aCount && rv != GNOME_VFS_ERROR_EOF)
|
||||
{
|
||||
// Copy data out of our buffer
|
||||
uint32_t bufLen = mDirBuf.Length() - mDirBufCursor;
|
||||
if (bufLen)
|
||||
{
|
||||
uint32_t n = std::min(bufLen, aCount);
|
||||
memcpy(aBuf, mDirBuf.get() + mDirBufCursor, n);
|
||||
*aCountRead += n;
|
||||
aBuf += n;
|
||||
aCount -= n;
|
||||
mDirBufCursor += n;
|
||||
}
|
||||
|
||||
if (!mDirListPtr) // Are we at the end of the directory list?
|
||||
{
|
||||
rv = GNOME_VFS_ERROR_EOF;
|
||||
}
|
||||
else if (aCount) // Do we need more data?
|
||||
{
|
||||
GnomeVFSFileInfo *info = (GnomeVFSFileInfo *) mDirListPtr->data;
|
||||
|
||||
// Prune '.' and '..' from directory listing.
|
||||
if (info->name[0] == '.' &&
|
||||
(info->name[1] == '\0' ||
|
||||
(info->name[1] == '.' && info->name[2] == '\0')))
|
||||
{
|
||||
mDirListPtr = mDirListPtr->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
mDirBuf.AssignLiteral("201: ");
|
||||
|
||||
// The "filename" field
|
||||
nsCString escName;
|
||||
nsCOMPtr<nsINetUtil> nu = do_GetService(NS_NETUTIL_CONTRACTID);
|
||||
if (nu) {
|
||||
nu->EscapeString(nsDependentCString(info->name),
|
||||
nsINetUtil::ESCAPE_URL_PATH, escName);
|
||||
|
||||
mDirBuf.Append(escName);
|
||||
mDirBuf.Append(' ');
|
||||
}
|
||||
|
||||
// The "content-length" field
|
||||
// XXX truncates size from 64-bit to 32-bit
|
||||
mDirBuf.AppendInt(int32_t(info->size));
|
||||
mDirBuf.Append(' ');
|
||||
|
||||
// The "last-modified" field
|
||||
//
|
||||
// NSPR promises: PRTime is compatible with time_t
|
||||
// we just need to convert from seconds to microseconds
|
||||
PRExplodedTime tm;
|
||||
PRTime pt = ((PRTime) info->mtime) * 1000000;
|
||||
PR_ExplodeTime(pt, PR_GMTParameters, &tm);
|
||||
{
|
||||
char buf[64];
|
||||
PR_FormatTimeUSEnglish(buf, sizeof(buf),
|
||||
"%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm);
|
||||
mDirBuf.Append(buf);
|
||||
}
|
||||
|
||||
// The "file-type" field
|
||||
switch (info->type)
|
||||
{
|
||||
case GNOME_VFS_FILE_TYPE_REGULAR:
|
||||
mDirBuf.AppendLiteral("FILE ");
|
||||
break;
|
||||
case GNOME_VFS_FILE_TYPE_DIRECTORY:
|
||||
mDirBuf.AppendLiteral("DIRECTORY ");
|
||||
break;
|
||||
case GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK:
|
||||
mDirBuf.AppendLiteral("SYMBOLIC-LINK ");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mDirBuf.Append('\n');
|
||||
|
||||
mDirBufCursor = 0;
|
||||
mDirListPtr = mDirListPtr->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NS_NOTREACHED("reading from what?");
|
||||
rv = GNOME_VFS_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// This class is used to implement SetContentTypeOfChannel.
|
||||
class nsGnomeVFSSetContentTypeEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
nsGnomeVFSSetContentTypeEvent(nsIChannel *channel, const char *contentType)
|
||||
: mChannel(channel), mContentType(contentType)
|
||||
{
|
||||
// stash channel reference in mChannel. no AddRef here! see note
|
||||
// in SetContentTypeOfchannel.
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
mChannel->SetContentType(mContentType);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsIChannel *mChannel;
|
||||
nsCString mContentType;
|
||||
};
|
||||
|
||||
nsresult
|
||||
nsGnomeVFSInputStream::SetContentTypeOfChannel(const char *contentType)
|
||||
{
|
||||
// We need to proxy this call over to the main thread. We post an
|
||||
// asynchronous event in this case so that we don't delay reading data, and
|
||||
// we know that this is safe to do since the channel's reference will be
|
||||
// released asynchronously as well. We trust the ordering of the main
|
||||
// thread's event queue to protect us against memory corruption.
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIRunnable> ev =
|
||||
new nsGnomeVFSSetContentTypeEvent(mChannel, contentType);
|
||||
if (!ev)
|
||||
{
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = NS_DispatchToMainThread(ev);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsGnomeVFSInputStream, nsIInputStream)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGnomeVFSInputStream::Close()
|
||||
{
|
||||
if (mHandle)
|
||||
{
|
||||
gnome_vfs_close(mHandle);
|
||||
mHandle = nullptr;
|
||||
}
|
||||
|
||||
if (mDirList)
|
||||
{
|
||||
// Destroy the list of GnomeVFSFileInfo objects...
|
||||
g_list_foreach(mDirList, (GFunc) gnome_vfs_file_info_unref, nullptr);
|
||||
g_list_free(mDirList);
|
||||
mDirList = nullptr;
|
||||
mDirListPtr = nullptr;
|
||||
}
|
||||
|
||||
if (mChannel)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
nsCOMPtr<nsIThread> thread = do_GetMainThread();
|
||||
if (thread)
|
||||
rv = NS_ProxyRelease(thread, mChannel);
|
||||
|
||||
NS_ASSERTION(thread && NS_SUCCEEDED(rv), "leaking channel reference");
|
||||
mChannel = nullptr;
|
||||
}
|
||||
|
||||
mSpec.Truncate(); // free memory
|
||||
|
||||
// Prevent future reads from re-opening the handle.
|
||||
if (NS_SUCCEEDED(mStatus))
|
||||
mStatus = NS_BASE_STREAM_CLOSED;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGnomeVFSInputStream::Available(uint64_t *aResult)
|
||||
{
|
||||
if (NS_FAILED(mStatus))
|
||||
return mStatus;
|
||||
|
||||
*aResult = mBytesRemaining;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGnomeVFSInputStream::Read(char *aBuf,
|
||||
uint32_t aCount,
|
||||
uint32_t *aCountRead)
|
||||
{
|
||||
*aCountRead = 0;
|
||||
|
||||
if (mStatus == NS_BASE_STREAM_CLOSED)
|
||||
return NS_OK;
|
||||
if (NS_FAILED(mStatus))
|
||||
return mStatus;
|
||||
|
||||
GnomeVFSResult rv = GNOME_VFS_OK;
|
||||
|
||||
// If this is our first-time through here, then open the URI.
|
||||
if (!mHandle && !mDirOpen)
|
||||
rv = DoOpen();
|
||||
|
||||
if (rv == GNOME_VFS_OK)
|
||||
rv = DoRead(aBuf, aCount, aCountRead);
|
||||
|
||||
if (rv != GNOME_VFS_OK)
|
||||
{
|
||||
// If we reach here, we hit some kind of error. EOF is not an error.
|
||||
mStatus = MapGnomeVFSResult(rv);
|
||||
if (mStatus == NS_BASE_STREAM_CLOSED)
|
||||
return NS_OK;
|
||||
|
||||
LOG(("gnomevfs: result %d [%s] mapped to 0x%x\n",
|
||||
rv, gnome_vfs_result_to_string(rv), mStatus));
|
||||
}
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGnomeVFSInputStream::ReadSegments(nsWriteSegmentFun aWriter,
|
||||
void *aClosure,
|
||||
uint32_t aCount,
|
||||
uint32_t *aResult)
|
||||
{
|
||||
// There is no way to implement this using GnomeVFS, but fortunately
|
||||
// that doesn't matter. Because we are a blocking input stream, Necko
|
||||
// isn't going to call our ReadSegments method.
|
||||
NS_NOTREACHED("nsGnomeVFSInputStream::ReadSegments");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGnomeVFSInputStream::IsNonBlocking(bool *aResult)
|
||||
{
|
||||
*aResult = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsGnomeVFSProtocolHandler MOZ_FINAL : public nsIProtocolHandler
|
||||
, public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPROTOCOLHANDLER
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
nsresult Init();
|
||||
|
||||
private:
|
||||
void InitSupportedProtocolsPref(nsIPrefBranch *prefs);
|
||||
bool IsSupportedProtocol(const nsCString &spec);
|
||||
|
||||
nsCString mSupportedProtocols;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsGnomeVFSProtocolHandler, nsIProtocolHandler, nsIObserver)
|
||||
|
||||
nsresult
|
||||
nsGnomeVFSProtocolHandler::Init()
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
sGnomeVFSLog = PR_NewLogModule("gnomevfs");
|
||||
#endif
|
||||
|
||||
if (!gnome_vfs_initialized())
|
||||
{
|
||||
if (!gnome_vfs_init())
|
||||
{
|
||||
NS_WARNING("gnome_vfs_init failed");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
if (prefs)
|
||||
{
|
||||
InitSupportedProtocolsPref(prefs);
|
||||
prefs->AddObserver(MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS, this, false);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsGnomeVFSProtocolHandler::InitSupportedProtocolsPref(nsIPrefBranch *prefs)
|
||||
{
|
||||
// read preferences
|
||||
nsresult rv = prefs->GetCharPref(MOZ_GNOMEVFS_SUPPORTED_PROTOCOLS,
|
||||
getter_Copies(mSupportedProtocols));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mSupportedProtocols.StripWhitespace();
|
||||
ToLowerCase(mSupportedProtocols);
|
||||
}
|
||||
else
|
||||
mSupportedProtocols.AssignLiteral("smb:,sftp:"); // use defaults
|
||||
|
||||
LOG(("gnomevfs: supported protocols \"%s\"\n", mSupportedProtocols.get()));
|
||||
}
|
||||
|
||||
bool
|
||||
nsGnomeVFSProtocolHandler::IsSupportedProtocol(const nsCString &aSpec)
|
||||
{
|
||||
const char *specString = aSpec.get();
|
||||
const char *colon = strchr(specString, ':');
|
||||
if (!colon)
|
||||
return false;
|
||||
|
||||
uint32_t length = colon - specString + 1;
|
||||
|
||||
// <scheme> + ':'
|
||||
nsCString scheme(specString, length);
|
||||
|
||||
char *found = PL_strcasestr(mSupportedProtocols.get(), scheme.get());
|
||||
if (!found)
|
||||
return false;
|
||||
|
||||
if (found[length] != ',' && found[length] != '\0')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGnomeVFSProtocolHandler::GetScheme(nsACString &aScheme)
|
||||
{
|
||||
aScheme.Assign(MOZ_GNOMEVFS_SCHEME);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGnomeVFSProtocolHandler::GetDefaultPort(int32_t *aDefaultPort)
|
||||
{
|
||||
*aDefaultPort = -1;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGnomeVFSProtocolHandler::GetProtocolFlags(uint32_t *aProtocolFlags)
|
||||
{
|
||||
// Is URI_STD true of all GnomeVFS URI types?
|
||||
*aProtocolFlags = URI_STD | URI_DANGEROUS_TO_LOAD;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGnomeVFSProtocolHandler::NewURI(const nsACString &aSpec,
|
||||
const char *aOriginCharset,
|
||||
nsIURI *aBaseURI,
|
||||
nsIURI **aResult)
|
||||
{
|
||||
const nsCString flatSpec(aSpec);
|
||||
LOG(("gnomevfs: NewURI [spec=%s]\n", flatSpec.get()));
|
||||
|
||||
if (!aBaseURI)
|
||||
{
|
||||
//
|
||||
// XXX This check is used to limit the gnome-vfs protocols we support. For
|
||||
// security reasons, it is best that we limit the protocols we support to
|
||||
// those with known characteristics. We might want to lessen this
|
||||
// restriction if it proves to be too heavy handed. A black list of
|
||||
// protocols we don't want to support might be better. For example, we
|
||||
// probably don't want to try to load "start-here:" inside the browser.
|
||||
// There are others that fall into this category, which are best handled
|
||||
// externally by Nautilus (or another app like it).
|
||||
//
|
||||
if (!IsSupportedProtocol(flatSpec))
|
||||
return NS_ERROR_UNKNOWN_PROTOCOL;
|
||||
|
||||
// Verify that GnomeVFS supports this URI scheme.
|
||||
GnomeVFSURI *uri = gnome_vfs_uri_new(flatSpec.get());
|
||||
if (!uri)
|
||||
return NS_ERROR_UNKNOWN_PROTOCOL;
|
||||
}
|
||||
|
||||
//
|
||||
// XXX Can we really assume that all gnome-vfs URIs can be parsed using
|
||||
// nsStandardURL? We probably really need to implement nsIURI/nsIURL
|
||||
// in terms of the gnome_vfs_uri_XXX methods, but at least this works
|
||||
// correctly for smb:// URLs ;-)
|
||||
//
|
||||
// Also, it might not be possible to fully implement nsIURI/nsIURL in
|
||||
// terms of GnomeVFSURI since some Necko methods have no GnomeVFS
|
||||
// equivalent.
|
||||
//
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIStandardURL> url =
|
||||
do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
rv = url->Init(nsIStandardURL::URLTYPE_STANDARD, -1, flatSpec,
|
||||
aOriginCharset, aBaseURI);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = CallQueryInterface(url, aResult);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGnomeVFSProtocolHandler::NewChannel2(nsIURI* aURI,
|
||||
nsILoadInfo* aLoadInfo,
|
||||
nsIChannel** aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aURI);
|
||||
nsresult rv;
|
||||
|
||||
nsAutoCString spec;
|
||||
rv = aURI->GetSpec(spec);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsRefPtr<nsGnomeVFSInputStream> stream = new nsGnomeVFSInputStream(spec);
|
||||
if (!stream)
|
||||
{
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
else
|
||||
{
|
||||
// start out assuming an unknown content-type. we'll set the content-type
|
||||
// to something better once we open the URI.
|
||||
rv = NS_NewInputStreamChannel(aResult, aURI, stream,
|
||||
NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE));
|
||||
if (NS_SUCCEEDED(rv))
|
||||
stream->SetChannel(*aResult);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGnomeVFSProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **aResult)
|
||||
{
|
||||
return NewChannel2(aURI, nullptr, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGnomeVFSProtocolHandler::AllowPort(int32_t aPort,
|
||||
const char *aScheme,
|
||||
bool *aResult)
|
||||
{
|
||||
// Don't override anything.
|
||||
*aResult = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGnomeVFSProtocolHandler::Observe(nsISupports *aSubject,
|
||||
const char *aTopic,
|
||||
const char16_t *aData)
|
||||
{
|
||||
if (strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
|
||||
InitSupportedProtocolsPref(prefs);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define NS_GNOMEVFSPROTOCOLHANDLER_CID \
|
||||
{ /* 9b6dc177-a2e4-49e1-9c98-0a8384de7f6c */ \
|
||||
0x9b6dc177, \
|
||||
0xa2e4, \
|
||||
0x49e1, \
|
||||
{0x9c, 0x98, 0x0a, 0x83, 0x84, 0xde, 0x7f, 0x6c} \
|
||||
}
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGnomeVFSProtocolHandler, Init)
|
||||
NS_DEFINE_NAMED_CID(NS_GNOMEVFSPROTOCOLHANDLER_CID);
|
||||
|
||||
static const mozilla::Module::CIDEntry kVFSCIDs[] = {
|
||||
{ &kNS_GNOMEVFSPROTOCOLHANDLER_CID, false, nullptr, nsGnomeVFSProtocolHandlerConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::ContractIDEntry kVFSContracts[] = {
|
||||
{ NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MOZ_GNOMEVFS_SCHEME, &kNS_GNOMEVFSPROTOCOLHANDLER_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module kVFSModule = {
|
||||
mozilla::Module::kVersion,
|
||||
kVFSCIDs,
|
||||
kVFSContracts
|
||||
};
|
||||
|
||||
NSMODULE_DEFN(nsGnomeVFSModule) = &kVFSModule;
|
|
@ -386,7 +386,6 @@ CanvasClientSharedSurface::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
|
|||
// Add the new TexClient.
|
||||
MOZ_ALWAYS_TRUE( AddTextureClient(newTex) );
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Remove the old TexClient.
|
||||
if (mFrontTex) {
|
||||
// remove old buffer from CompositableHost
|
||||
|
@ -399,7 +398,6 @@ CanvasClientSharedSurface::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
|
|||
|
||||
mFrontTex = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Use the new TexClient.
|
||||
mFrontTex = newTex;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/layers/CompositorChild.h"
|
||||
#include "mozilla/layers/CompositorParent.h"
|
||||
#include <stddef.h> // for size_t
|
||||
#include "ClientLayerManager.h" // for ClientLayerManager
|
||||
#include "base/message_loop.h" // for MessageLoop
|
||||
|
@ -40,19 +41,53 @@ Atomic<int32_t> CompositableForwarder::sSerialCounter(0);
|
|||
|
||||
CompositorChild::CompositorChild(ClientLayerManager *aLayerManager)
|
||||
: mLayerManager(aLayerManager)
|
||||
, mCanSend(true)
|
||||
, mCanSend(false)
|
||||
{
|
||||
}
|
||||
|
||||
CompositorChild::~CompositorChild()
|
||||
{
|
||||
if (mCanSend) {
|
||||
gfxCriticalError() << "CompositorChild was not deinitialized";
|
||||
}
|
||||
}
|
||||
|
||||
static void DeferredDestroyCompositor(nsRefPtr<CompositorParent> aCompositorParent,
|
||||
nsRefPtr<CompositorChild> aCompositorChild)
|
||||
{
|
||||
// Bug 848949 needs to be fixed before
|
||||
// we can close the channel properly
|
||||
//aCompositorChild->Close();
|
||||
}
|
||||
|
||||
void
|
||||
CompositorChild::Destroy()
|
||||
{
|
||||
mLayerManager->Destroy();
|
||||
mLayerManager = nullptr;
|
||||
// This must not be called from the destructor!
|
||||
MOZ_ASSERT(mRefCnt != 0);
|
||||
|
||||
if (!mCanSend) {
|
||||
NS_WARNING("Trying to deinitialize a CompositorChild twice");
|
||||
return;
|
||||
}
|
||||
|
||||
SendWillStop();
|
||||
// The call just made to SendWillStop can result in IPC from the
|
||||
// CompositorParent to the CompositorChild (e.g. caused by the destruction
|
||||
// of shared memory). We need to ensure this gets processed by the
|
||||
// CompositorChild before it gets destroyed. It suffices to ensure that
|
||||
// events already in the MessageLoop get processed before the
|
||||
// CompositorChild is destroyed, so we add a task to the MessageLoop to
|
||||
// handle compositor desctruction.
|
||||
|
||||
// From now on the only message we can send is Stop.
|
||||
mCanSend = false;
|
||||
|
||||
if (mLayerManager) {
|
||||
mLayerManager->Destroy();
|
||||
mLayerManager = nullptr;
|
||||
}
|
||||
|
||||
// start from the end of the array because Destroy() can cause the
|
||||
// LayerTransactionChild to be removed from the array.
|
||||
for (int i = ManagedPLayerTransactionChild().Length() - 1; i >= 0; --i) {
|
||||
|
@ -60,8 +95,14 @@ CompositorChild::Destroy()
|
|||
static_cast<LayerTransactionChild*>(ManagedPLayerTransactionChild()[i]);
|
||||
layers->Destroy();
|
||||
}
|
||||
MOZ_ASSERT(!mCanSend);
|
||||
|
||||
SendStop();
|
||||
|
||||
// The DeferredDestroyCompositor task takes ownership of compositorParent and
|
||||
// will release them when it runs.
|
||||
nsRefPtr<CompositorChild> selfRef = this;
|
||||
MessageLoop::current()->PostTask(FROM_HERE,
|
||||
NewRunnableFunction(DeferredDestroyCompositor, mCompositorParent, selfRef));
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -94,6 +135,8 @@ CompositorChild::Create(Transport* aTransport, ProcessId aOtherProcess)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
child->mCanSend = true;
|
||||
|
||||
// We release this ref in ActorDestroy().
|
||||
sCompositor = child.forget().take();
|
||||
|
||||
|
@ -106,6 +149,18 @@ CompositorChild::Create(Transport* aTransport, ProcessId aOtherProcess)
|
|||
return sCompositor;
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorChild::OpenSameProcess(CompositorParent* aParent)
|
||||
{
|
||||
MOZ_ASSERT(aParent);
|
||||
|
||||
mCompositorParent = aParent;
|
||||
mCanSend = Open(mCompositorParent->GetIPCChannel(),
|
||||
CompositorParent::CompositorLoop(),
|
||||
ipc::ChildSide);
|
||||
return mCanSend;
|
||||
}
|
||||
|
||||
/*static*/ CompositorChild*
|
||||
CompositorChild::Get()
|
||||
{
|
||||
|
@ -482,8 +537,6 @@ bool
|
|||
CompositorChild::SendWillStop()
|
||||
{
|
||||
MOZ_ASSERT(mCanSend);
|
||||
// From now on the only two messages we can send are WillStop and Stop.
|
||||
mCanSend = false;
|
||||
return PCompositorChild::SendWillStop();
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,12 @@ public:
|
|||
static PCompositorChild*
|
||||
Create(Transport* aTransport, ProcessId aOtherProcess);
|
||||
|
||||
/**
|
||||
* Initialize the CompositorChild and open the connection in the non-multi-process
|
||||
* case.
|
||||
*/
|
||||
bool OpenSameProcess(CompositorParent* aParent);
|
||||
|
||||
static CompositorChild* Get();
|
||||
|
||||
static bool ChildProcessHasCompositor() { return sCompositor != nullptr; }
|
||||
|
@ -168,6 +174,9 @@ private:
|
|||
void* aLayerTransactionChild);
|
||||
|
||||
nsRefPtr<ClientLayerManager> mLayerManager;
|
||||
// When not multi-process, hold a reference to the CompositorParent to keep it
|
||||
// alive. This reference should be null in multi-process.
|
||||
nsRefPtr<CompositorParent> mCompositorParent;
|
||||
|
||||
// The ViewID of the FrameMetrics is used as the key for this hash table.
|
||||
// While this should be safe to use since the ViewID is unique
|
||||
|
|
|
@ -105,6 +105,13 @@ LayerTransactionChild::RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessa
|
|||
TransactionCompleteted(op.transactionId());
|
||||
break;
|
||||
}
|
||||
case AsyncParentMessageData::TOpReplyRemoveTexture: {
|
||||
const OpReplyRemoveTexture& op = message.get_OpReplyRemoveTexture();
|
||||
|
||||
AsyncTransactionTrackersHolder::TransactionCompleteted(op.holderId(),
|
||||
op.transactionId());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NS_ERROR("unknown AsyncParentMessageData type");
|
||||
return false;
|
||||
|
|
|
@ -937,6 +937,7 @@ LayerTransactionParent::RecvChildAsyncMessages(InfallibleTArray<AsyncChildMessag
|
|||
MOZ_ASSERT(tex.get());
|
||||
compositable->RemoveTextureHost(tex);
|
||||
|
||||
MOZ_ASSERT(ImageBridgeParent::GetInstance(GetChildProcessId()));
|
||||
if (ImageBridgeParent::GetInstance(GetChildProcessId())) {
|
||||
// send FenceHandle if present via ImageBridge.
|
||||
ImageBridgeParent::SendFenceHandleToTrackerIfPresent(
|
||||
|
@ -1026,5 +1027,13 @@ LayerTransactionParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessa
|
|||
mozilla::unused << SendParentAsyncMessages(aMessage);
|
||||
}
|
||||
|
||||
void
|
||||
LayerTransactionParent::ReplyRemoveTexture(const OpReplyRemoveTexture& aReply)
|
||||
{
|
||||
InfallibleTArray<AsyncParentMessageData> messages;
|
||||
messages.AppendElement(aReply);
|
||||
mozilla::unused << SendParentAsyncMessages(messages);
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -101,6 +101,8 @@ public:
|
|||
return mChildProcessId;
|
||||
}
|
||||
|
||||
virtual void ReplyRemoveTexture(const OpReplyRemoveTexture& aReply) MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
virtual bool RecvShutdown() MOZ_OVERRIDE;
|
||||
|
||||
|
|
|
@ -3743,9 +3743,9 @@ gfxFontStyle::AdjustForSubSuperscript(int32_t aAppUnitsPerDevPixel)
|
|||
// calculate reduced size, roughly mimicing behavior of font-size: smaller
|
||||
float cssSize = size * aAppUnitsPerDevPixel / AppUnitsPerCSSPixel();
|
||||
if (cssSize < NS_FONT_SUB_SUPER_SMALL_SIZE) {
|
||||
cssSize *= NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL;
|
||||
} else if (cssSize >= NS_FONT_SUB_SUPER_SMALL_SIZE) {
|
||||
cssSize *= NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE;
|
||||
size *= NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL;
|
||||
} else if (cssSize >= NS_FONT_SUB_SUPER_LARGE_SIZE) {
|
||||
size *= NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE;
|
||||
} else {
|
||||
gfxFloat t = (cssSize - NS_FONT_SUB_SUPER_SMALL_SIZE) /
|
||||
(NS_FONT_SUB_SUPER_LARGE_SIZE -
|
||||
|
|
|
@ -141,18 +141,10 @@ js::ExecuteRegExpLegacy(JSContext *cx, RegExpStatics *res, RegExpObject &reobj,
|
|||
|
||||
/*
|
||||
* Compile a new |RegExpShared| for the |RegExpObject|.
|
||||
*
|
||||
* Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
|
||||
* arguments:
|
||||
*
|
||||
* RegExp, undefined => flags := pattern.flags
|
||||
* RegExp, _ => throw TypeError
|
||||
* _ => pattern := ToString(pattern) if defined(pattern) else ''
|
||||
* flags := ToString(flags) if defined(flags) else ''
|
||||
*/
|
||||
static bool
|
||||
CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args,
|
||||
RegExpStaticsUse staticsUse)
|
||||
RegExpStaticsUse staticsUse, RegExpCreationMode creationMode)
|
||||
{
|
||||
if (args.length() == 0) {
|
||||
MOZ_ASSERT(staticsUse == UseRegExpStatics);
|
||||
|
@ -169,11 +161,16 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args,
|
|||
|
||||
RootedValue sourceValue(cx, args[0]);
|
||||
|
||||
/*
|
||||
* If we get passed in an object whose internal [[Class]] property is
|
||||
* "RegExp", return a new object with the same source/flags.
|
||||
*/
|
||||
if (IsObjectWithClass(sourceValue, ESClass_RegExp, cx)) {
|
||||
/*
|
||||
* For RegExp.prototype.compile, if the first argument is a RegExp object,
|
||||
* the second argument must be undefined. Otherwise, throw a TypeError.
|
||||
*/
|
||||
if (args.hasDefined(1) && creationMode == CreateForCompile) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NEWREGEXP_FLAGGED);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Beware, sourceObj may be a (transparent) proxy to a RegExp, so only
|
||||
* use generic (proxyable) operations on sourceObj that do not assume
|
||||
|
@ -181,24 +178,32 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args,
|
|||
*/
|
||||
RootedObject sourceObj(cx, &sourceValue.toObject());
|
||||
|
||||
if (args.hasDefined(1)) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NEWREGEXP_FLAGGED);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the 'source' and the 'flags' out of sourceObj; do not reuse
|
||||
* the RegExpShared since it may be from a different compartment.
|
||||
*/
|
||||
RootedAtom sourceAtom(cx);
|
||||
RegExpFlag flags;
|
||||
{
|
||||
/*
|
||||
* Extract the 'source' from sourceObj; do not reuse the RegExpShared
|
||||
* since it may be from a different compartment.
|
||||
*/
|
||||
RegExpGuard g(cx);
|
||||
if (!RegExpToShared(cx, sourceObj, &g))
|
||||
return false;
|
||||
|
||||
sourceAtom = g->getSource();
|
||||
flags = g->getFlags();
|
||||
|
||||
/*
|
||||
* If args[1] is not undefined, then parse the 'flags' from args[1].
|
||||
* Otherwise, extract the 'flags' from sourceObj.
|
||||
*/
|
||||
if (args.hasDefined(1)) {
|
||||
flags = RegExpFlag(0);
|
||||
RootedString flagStr(cx, ToString<CanGC>(cx, args[1]));
|
||||
if (!flagStr)
|
||||
return false;
|
||||
if (!ParseRegExpFlags(cx, flagStr, &flags))
|
||||
return false;
|
||||
} else {
|
||||
flags = g->getFlags();
|
||||
}
|
||||
}
|
||||
|
||||
RegExpObject *reobj = builder.build(sourceAtom, flags);
|
||||
|
@ -224,7 +229,6 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args,
|
|||
RootedString flagStr(cx, ToString<CanGC>(cx, args[1]));
|
||||
if (!flagStr)
|
||||
return false;
|
||||
args[1].setString(flagStr);
|
||||
if (!ParseRegExpFlags(cx, flagStr, &flags))
|
||||
return false;
|
||||
}
|
||||
|
@ -260,7 +264,7 @@ regexp_compile_impl(JSContext *cx, CallArgs args)
|
|||
{
|
||||
MOZ_ASSERT(IsRegExp(args.thisv()));
|
||||
RegExpObjectBuilder builder(cx, &args.thisv().toObject().as<RegExpObject>());
|
||||
return CompileRegExpObject(cx, builder, args, UseRegExpStatics);
|
||||
return CompileRegExpObject(cx, builder, args, UseRegExpStatics, CreateForCompile);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -291,7 +295,7 @@ regexp_construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
}
|
||||
|
||||
RegExpObjectBuilder builder(cx);
|
||||
return CompileRegExpObject(cx, builder, args, UseRegExpStatics);
|
||||
return CompileRegExpObject(cx, builder, args, UseRegExpStatics, CreateForConstruct);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -305,7 +309,7 @@ js::regexp_construct_no_statics(JSContext *cx, unsigned argc, Value *vp)
|
|||
MOZ_ASSERT(!args.isConstructing());
|
||||
|
||||
RegExpObjectBuilder builder(cx);
|
||||
return CompileRegExpObject(cx, builder, args, DontUseRegExpStatics);
|
||||
return CompileRegExpObject(cx, builder, args, DontUseRegExpStatics, CreateForConstruct);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
|
|
|
@ -29,6 +29,9 @@ enum RegExpStaticsUpdate { UpdateRegExpStatics, DontUpdateRegExpStatics };
|
|||
// Whether RegExp statics should be used to create a RegExp instance.
|
||||
enum RegExpStaticsUse { UseRegExpStatics, DontUseRegExpStatics };
|
||||
|
||||
// This enum is used to indicate whether 'CompileRegExpObject' is called from 'regexp_compile'.
|
||||
enum RegExpCreationMode { CreateForCompile, CreateForConstruct };
|
||||
|
||||
RegExpRunStatus
|
||||
ExecuteRegExp(JSContext *cx, HandleObject regexp, HandleString string,
|
||||
MatchPairs *matches, RegExpStaticsUpdate staticsUpdate);
|
||||
|
|
|
@ -5360,7 +5360,7 @@ EmitFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top
|
|||
}
|
||||
|
||||
static MOZ_NEVER_INLINE bool
|
||||
EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool needsProto = false)
|
||||
{
|
||||
FunctionBox *funbox = pn->pn_funbox;
|
||||
RootedFunction fun(cx, funbox->function());
|
||||
|
@ -5459,7 +5459,13 @@ EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
MOZ_ASSERT(fun->isArrow() == (pn->getOp() == JSOP_LAMBDA_ARROW));
|
||||
if (fun->isArrow() && Emit1(cx, bce, JSOP_THIS) < 0)
|
||||
return false;
|
||||
if (needsProto) {
|
||||
MOZ_ASSERT(pn->getOp() == JSOP_LAMBDA);
|
||||
pn->setOp(JSOP_FUNWITHPROTO);
|
||||
}
|
||||
return EmitIndex32(cx, pn->getOp(), index, bce);
|
||||
} else {
|
||||
MOZ_ASSERT(!needsProto);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -6942,12 +6948,9 @@ EmitClass(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
|
||||
ClassNames *names = classNode.names();
|
||||
|
||||
MOZ_ASSERT(!classNode.heritage(), "For now, no heritage expressions");
|
||||
LexicalScopeNode *innerBlock = classNode.scope();
|
||||
|
||||
ParseNode *classMethods = innerBlock->pn_expr;
|
||||
MOZ_ASSERT(classMethods->isKind(PNK_CLASSMETHODLIST));
|
||||
ParseNode *heritageExpression = classNode.heritage();
|
||||
|
||||
ParseNode *classMethods = classNode.methodList();
|
||||
ParseNode *constructor = nullptr;
|
||||
for (ParseNode *mn = classMethods->pn_head; mn; mn = mn->pn_next) {
|
||||
ClassMethod &method = mn->as<ClassMethod>();
|
||||
|
@ -6964,14 +6967,32 @@ EmitClass(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
bool savedStrictness = bce->sc->setLocalStrictMode(true);
|
||||
|
||||
StmtInfoBCE stmtInfo(cx);
|
||||
if (!EnterBlockScope(cx, bce, &stmtInfo, innerBlock->pn_objbox, JSOP_UNINITIALIZED))
|
||||
if (names) {
|
||||
if (!EnterBlockScope(cx, bce, &stmtInfo, classNode.scopeObject(), JSOP_UNINITIALIZED))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (heritageExpression) {
|
||||
if (!EmitTree(cx, bce, heritageExpression))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_CLASSHERITAGE) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EmitFunc(cx, bce, constructor, !!heritageExpression))
|
||||
return false;
|
||||
|
||||
if (!EmitFunc(cx, bce, constructor))
|
||||
return false;
|
||||
|
||||
if (!EmitNewInit(cx, bce, JSProto_Object))
|
||||
return false;
|
||||
if (heritageExpression) {
|
||||
// JSOP_CLASSHERITAGE leaves both prototypes on the stack. After
|
||||
// creating the constructor, trickly it to the bottom to make the object.
|
||||
if (Emit1(cx, bce, JSOP_SWAP) < 0)
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_OBJWITHPROTO) < 0)
|
||||
return false;
|
||||
} else {
|
||||
if (!EmitNewInit(cx, bce, JSProto_Object))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Emit1(cx, bce, JSOP_DUP2) < 0)
|
||||
return false;
|
||||
|
@ -6987,20 +7008,25 @@ EmitClass(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
return false;
|
||||
|
||||
// That DEFCONST is never gonna be used, but use it here for logical consistency.
|
||||
ParseNode *innerName = names->innerBinding();
|
||||
if (!EmitLexicalInitialization(cx, bce, innerName, JSOP_DEFCONST))
|
||||
return false;
|
||||
if (names) {
|
||||
// That DEFCONST is never gonna be used, but use it here for logical consistency.
|
||||
ParseNode *innerName = names->innerBinding();
|
||||
if (!EmitLexicalInitialization(cx, bce, innerName, JSOP_DEFCONST))
|
||||
return false;
|
||||
|
||||
if (!LeaveNestedScope(cx, bce, &stmtInfo))
|
||||
return false;
|
||||
if (!LeaveNestedScope(cx, bce, &stmtInfo))
|
||||
return false;
|
||||
|
||||
ParseNode *outerName = names->outerBinding();
|
||||
if (!EmitLexicalInitialization(cx, bce, outerName, JSOP_DEFVAR))
|
||||
return false;
|
||||
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
return false;
|
||||
ParseNode *outerName = names->outerBinding();
|
||||
if (outerName) {
|
||||
if (!EmitLexicalInitialization(cx, bce, outerName, JSOP_DEFVAR))
|
||||
return false;
|
||||
// Only class statements make outer bindings, and they do not leave
|
||||
// themselves on the stack.
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(bce->sc->setLocalStrictMode(savedStrictness));
|
||||
|
||||
|
|
|
@ -268,7 +268,6 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
|
|||
case PNK_WHILE:
|
||||
case PNK_SWITCH:
|
||||
case PNK_LETBLOCK:
|
||||
case PNK_CLASSNAMES:
|
||||
case PNK_CLASSMETHOD:
|
||||
case PNK_FOR: {
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
|
@ -277,6 +276,15 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
|
|||
return PushResult::Recyclable;
|
||||
}
|
||||
|
||||
// Named class expressions do not have outer binding nodes
|
||||
case PNK_CLASSNAMES: {
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
if (pn->pn_left)
|
||||
stack->push(pn->pn_left);
|
||||
stack->push(pn->pn_right);
|
||||
return PushResult::Recyclable;
|
||||
}
|
||||
|
||||
// PNK_WITH is PN_BINARY_OBJ -- that is, PN_BINARY with (irrelevant for
|
||||
// this method's purposes) the addition of the StaticWithObject as
|
||||
// pn_binary_obj. Both left (expression) and right (statement) are
|
||||
|
@ -383,10 +391,11 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
|
|||
return PushResult::Recyclable;
|
||||
}
|
||||
|
||||
// classes might have an optinal node for the heritage
|
||||
// classes might have an optional node for the heritage, as well as the names
|
||||
case PNK_CLASS: {
|
||||
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
||||
stack->push(pn->pn_kid1);
|
||||
if (pn->pn_kid1)
|
||||
stack->push(pn->pn_kid1);
|
||||
if (pn->pn_kid2)
|
||||
stack->push(pn->pn_kid2);
|
||||
stack->push(pn->pn_kid3);
|
||||
|
|
|
@ -1360,9 +1360,9 @@ struct ClassNames : public BinaryNode {
|
|||
ClassNames(ParseNode *outerBinding, ParseNode *innerBinding, const TokenPos &pos)
|
||||
: BinaryNode(PNK_CLASSNAMES, JSOP_NOP, pos, outerBinding, innerBinding)
|
||||
{
|
||||
MOZ_ASSERT(outerBinding->isKind(PNK_NAME));
|
||||
MOZ_ASSERT_IF(outerBinding, outerBinding->isKind(PNK_NAME));
|
||||
MOZ_ASSERT(innerBinding->isKind(PNK_NAME));
|
||||
MOZ_ASSERT(innerBinding->pn_atom == outerBinding->pn_atom);
|
||||
MOZ_ASSERT_IF(outerBinding, innerBinding->pn_atom == outerBinding->pn_atom);
|
||||
}
|
||||
|
||||
static bool test(const ParseNode &node) {
|
||||
|
@ -1388,11 +1388,12 @@ struct ClassNames : public BinaryNode {
|
|||
};
|
||||
|
||||
struct ClassNode : public TernaryNode {
|
||||
ClassNode(ParseNode *names, ParseNode *heritage, ParseNode *methodBlock)
|
||||
: TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodBlock)
|
||||
ClassNode(ParseNode *names, ParseNode *heritage, ParseNode *methodsOrBlock)
|
||||
: TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock)
|
||||
{
|
||||
MOZ_ASSERT(names->is<ClassNames>());
|
||||
MOZ_ASSERT(methodBlock->is<LexicalScopeNode>());
|
||||
MOZ_ASSERT_IF(names, names->is<ClassNames>());
|
||||
MOZ_ASSERT(methodsOrBlock->is<LexicalScopeNode>() ||
|
||||
methodsOrBlock->isKind(PNK_CLASSMETHODLIST));
|
||||
}
|
||||
|
||||
static bool test(const ParseNode &node) {
|
||||
|
@ -1402,13 +1403,23 @@ struct ClassNode : public TernaryNode {
|
|||
}
|
||||
|
||||
ClassNames *names() const {
|
||||
return &pn_kid1->as<ClassNames>();
|
||||
return pn_kid1 ? &pn_kid1->as<ClassNames>() : nullptr;
|
||||
}
|
||||
ParseNode *heritage() const {
|
||||
return pn_kid2;
|
||||
}
|
||||
LexicalScopeNode *scope() const {
|
||||
return &pn_kid3->as<LexicalScopeNode>();
|
||||
ParseNode *methodList() const {
|
||||
if (pn_kid3->isKind(PNK_CLASSMETHODLIST))
|
||||
return pn_kid3;
|
||||
|
||||
MOZ_ASSERT(pn_kid3->is<LexicalScopeNode>());
|
||||
ParseNode *list = pn_kid3->pn_expr;
|
||||
MOZ_ASSERT(list->isKind(PNK_CLASSMETHODLIST));
|
||||
return list;
|
||||
}
|
||||
ObjectBox *scopeObject() const {
|
||||
MOZ_ASSERT(pn_kid3->is<LexicalScopeNode>());
|
||||
return pn_kid3->pn_objbox;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -5825,10 +5825,12 @@ Parser<ParseHandler>::debuggerStatement()
|
|||
|
||||
template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::classStatement()
|
||||
Parser<FullParseHandler>::classDefinition(ClassContext classContext)
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
|
||||
|
||||
bool savedStrictness = setLocalStrictMode(true);
|
||||
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt))
|
||||
return null();
|
||||
|
@ -5840,10 +5842,13 @@ Parser<FullParseHandler>::classStatement()
|
|||
if (!checkYieldNameValidity())
|
||||
return null();
|
||||
name = tokenStream.currentName();
|
||||
} else {
|
||||
} else if (classContext == ClassStatement) {
|
||||
// Class statements must have a bound name
|
||||
report(ParseError, false, null(), JSMSG_UNNAMED_CLASS_STMT);
|
||||
return null();
|
||||
} else {
|
||||
// Make sure to put it back, whatever it was
|
||||
tokenStream.ungetToken();
|
||||
}
|
||||
|
||||
if (name == context->names().let) {
|
||||
|
@ -5851,49 +5856,72 @@ Parser<FullParseHandler>::classStatement()
|
|||
return null();
|
||||
}
|
||||
|
||||
ParseNode *classBlock = null();
|
||||
StmtInfoPC classStmt(context);
|
||||
if (name) {
|
||||
classBlock = pushLexicalScope(&classStmt);
|
||||
if (!classBlock)
|
||||
return null();
|
||||
}
|
||||
|
||||
// Because the binding definitions keep track of their blockId, we need to
|
||||
// create at least the inner binding later. Keep track of the name's position
|
||||
// in order to provide it for the nodes created later.
|
||||
TokenPos namePos = pos();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CLASS);
|
||||
|
||||
bool savedStrictness = setLocalStrictMode(true);
|
||||
|
||||
StmtInfoPC classStmt(context);
|
||||
ParseNode *classBlock = pushLexicalScope(&classStmt);
|
||||
if (!classBlock)
|
||||
ParseNode *classHeritage = null();
|
||||
bool hasHeritage;
|
||||
if (!tokenStream.matchToken(&hasHeritage, TOK_EXTENDS))
|
||||
return null();
|
||||
if (hasHeritage) {
|
||||
if (!tokenStream.getToken(&tt))
|
||||
return null();
|
||||
classHeritage = memberExpr(tt, true);
|
||||
if (!classHeritage)
|
||||
return null();
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CLASS);
|
||||
|
||||
ParseNode *classMethods = propertyList(ClassBody);
|
||||
if (!classMethods)
|
||||
return null();
|
||||
handler.setLexicalScopeBody(classBlock, classMethods);
|
||||
|
||||
ParseNode *innerBinding = makeInitializedLexicalBinding(name, true, namePos);
|
||||
if (!innerBinding)
|
||||
return null();
|
||||
ParseNode *nameNode = null();
|
||||
ParseNode *methodsOrBlock = classMethods;
|
||||
if (name) {
|
||||
ParseNode *innerBinding = makeInitializedLexicalBinding(name, true, namePos);
|
||||
if (!innerBinding)
|
||||
return null();
|
||||
|
||||
PopStatementPC(tokenStream, pc);
|
||||
MOZ_ASSERT(classBlock);
|
||||
handler.setLexicalScopeBody(classBlock, classMethods);
|
||||
methodsOrBlock = classBlock;
|
||||
|
||||
ParseNode *outerBinding = makeInitializedLexicalBinding(name, false, namePos);
|
||||
if (!outerBinding)
|
||||
return null();
|
||||
PopStatementPC(tokenStream, pc);
|
||||
|
||||
ParseNode *nameNode = handler.newClassNames(outerBinding, innerBinding, namePos);
|
||||
if (!nameNode)
|
||||
return null();
|
||||
ParseNode *outerBinding = null();
|
||||
if (classContext == ClassStatement) {
|
||||
outerBinding = makeInitializedLexicalBinding(name, false, namePos);
|
||||
if (!outerBinding)
|
||||
return null();
|
||||
}
|
||||
|
||||
nameNode = handler.newClassNames(outerBinding, innerBinding, namePos);
|
||||
if (!nameNode)
|
||||
return null();
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness));
|
||||
|
||||
return handler.newClass(nameNode, null(), classBlock);
|
||||
return handler.newClass(nameNode, classHeritage, methodsOrBlock);
|
||||
}
|
||||
|
||||
template <>
|
||||
SyntaxParseHandler::Node
|
||||
Parser<SyntaxParseHandler>::classStatement()
|
||||
Parser<SyntaxParseHandler>::classDefinition(ClassContext classContext)
|
||||
{
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
|
@ -5967,7 +5995,7 @@ Parser<ParseHandler>::statement(bool canHaveDirectives)
|
|||
case TOK_CLASS:
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
return classStatement();
|
||||
return classDefinition(ClassStatement);
|
||||
|
||||
|
||||
/* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
|
||||
|
@ -8329,6 +8357,9 @@ Parser<ParseHandler>::primaryExpr(TokenKind tt, InvokedPrediction invoked)
|
|||
case TOK_FUNCTION:
|
||||
return functionExpr(invoked);
|
||||
|
||||
case TOK_CLASS:
|
||||
return classDefinition(ClassExpression);
|
||||
|
||||
case TOK_LB:
|
||||
return arrayInitializer();
|
||||
|
||||
|
|
|
@ -553,7 +553,6 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
|||
Node throwStatement();
|
||||
Node tryStatement();
|
||||
Node debuggerStatement();
|
||||
Node classStatement();
|
||||
|
||||
Node lexicalDeclaration(bool isConst);
|
||||
Node letDeclarationOrBlock();
|
||||
|
@ -618,6 +617,9 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
|||
Node destructuringExpr(BindData<ParseHandler> *data, TokenKind tt);
|
||||
Node destructuringExprWithoutYield(BindData<ParseHandler> *data, TokenKind tt, unsigned msg);
|
||||
|
||||
enum ClassContext { ClassStatement, ClassExpression };
|
||||
Node classDefinition(ClassContext classContext);
|
||||
|
||||
Node identifierName();
|
||||
|
||||
bool matchLabel(MutableHandle<PropertyName*> label);
|
||||
|
|
|
@ -111,6 +111,7 @@
|
|||
macro(EXPORT, "keyword 'export'") \
|
||||
macro(IMPORT, "keyword 'import'") \
|
||||
macro(CLASS, "keyword 'class'") \
|
||||
macro(EXTENDS, "keyword 'extends'") \
|
||||
macro(RESERVED, "reserved keyword") \
|
||||
/* reserved keywords in strict mode */ \
|
||||
macro(STRICT_RESERVED, "reserved keyword") \
|
||||
|
|
|
@ -979,6 +979,7 @@ TokenStream::checkForKeyword(const KeywordInfo *kw, TokenKind *ttp)
|
|||
if (kw->tokentype == TOK_RESERVED
|
||||
#ifndef JS_HAS_CLASSES
|
||||
|| kw->tokentype == TOK_CLASS
|
||||
|| kw->tokentype == TOK_EXTENDS
|
||||
#endif
|
||||
)
|
||||
{
|
||||
|
|
|
@ -15,7 +15,6 @@ function f() {
|
|||
var i8 = new Int8Array(f32.buffer);
|
||||
var u8 = new Uint8Array(f32.buffer);
|
||||
|
||||
var r;
|
||||
for (var i = 0; i < 150; i++) {
|
||||
assertEqX4(SIMD.float32x4.load(f64, 0), [1,2,3,4]);
|
||||
assertEqX4(SIMD.float32x4.load(f32, 1), [2,3,4,5]);
|
||||
|
@ -32,17 +31,32 @@ function f() {
|
|||
assertEqX4(SIMD.float32x4.load(u16, (16 << 1) - (4 << 1)), [13,14,15,16]);
|
||||
assertEqX4(SIMD.float32x4.load(i8, (16 << 2) - (4 << 2)), [13,14,15,16]);
|
||||
assertEqX4(SIMD.float32x4.load(u8, (16 << 2) - (4 << 2)), [13,14,15,16]);
|
||||
|
||||
var caught = false;
|
||||
try {
|
||||
SIMD.float32x4.load(i8, (i < 149) ? 0 : (16 << 2) - (4 << 2) + 1);
|
||||
} catch (e) {
|
||||
caught = true;
|
||||
}
|
||||
assertEq(i < 149 || caught, true);
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
f();
|
||||
|
||||
function testBailout(uglyDuckling) {
|
||||
var f32 = new Float32Array(16);
|
||||
for (var i = 0; i < 16; i++)
|
||||
f32[i] = i + 1;
|
||||
|
||||
var i8 = new Int8Array(f32.buffer);
|
||||
|
||||
for (var i = 0; i < 150; i++) {
|
||||
var caught = false;
|
||||
try {
|
||||
SIMD.float32x4.load(i8, (i < 149) ? 0 : uglyDuckling);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
assertEq(e instanceof RangeError, true);
|
||||
caught = true;
|
||||
}
|
||||
assertEq(i < 149 || caught, true);
|
||||
}
|
||||
}
|
||||
|
||||
print('Testing range checks...');
|
||||
testBailout(-1);
|
||||
testBailout(-15);
|
||||
testBailout(12 * 4 + 1);
|
||||
|
|
|
@ -46,17 +46,35 @@ function f() {
|
|||
check();
|
||||
SIMD.float32x4.store(u8, 0, f4);
|
||||
check();
|
||||
}
|
||||
}
|
||||
|
||||
f();
|
||||
|
||||
function testBailout(uglyDuckling) {
|
||||
var f32 = new Float32Array(16);
|
||||
for (var i = 0; i < 16; i++)
|
||||
f32[i] = i + 1;
|
||||
|
||||
var i8 = new Int8Array(f32.buffer);
|
||||
|
||||
var f4 = SIMD.float32x4(42, 43, 44, 45);
|
||||
|
||||
for (var i = 0; i < 150; i++) {
|
||||
var caught = false;
|
||||
try {
|
||||
SIMD.float32x4.store(i8, (i < 149) ? 0 : (16 << 2) - (4 << 2) + 1, f4);
|
||||
check();
|
||||
} catch (e) {
|
||||
print(e);
|
||||
assertEq(e instanceof RangeError, true);
|
||||
caught = true;
|
||||
}
|
||||
assertEq(i < 149 || caught, true);
|
||||
}
|
||||
}
|
||||
|
||||
f();
|
||||
print('Testing range checks...');
|
||||
testBailout(-1);
|
||||
testBailout(-15);
|
||||
testBailout(12 * 4 + 1);
|
||||
|
||||
|
|
|
@ -3181,8 +3181,13 @@ IonBuilder::prepareForSimdLoadStore(CallInfo &callInfo, Scalar::Type simdType, M
|
|||
MInstruction *length;
|
||||
addTypedArrayLengthAndData(array, SkipBoundsCheck, index, &length, elements);
|
||||
|
||||
MInstruction *check = MBoundsCheck::New(alloc(), indexForBoundsCheck, length);
|
||||
current->add(check);
|
||||
// It can be that the index is out of bounds, while the added index for the
|
||||
// bounds check is in bounds, so we actually need two bounds checks here.
|
||||
MInstruction *positiveCheck = MBoundsCheck::New(alloc(), *index, length);
|
||||
current->add(positiveCheck);
|
||||
|
||||
MInstruction *fullCheck = MBoundsCheck::New(alloc(), indexForBoundsCheck, length);
|
||||
current->add(fullCheck);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,7 @@ MSG_DEF(JSMSG_CANT_SET_PROTO_OF, 1, JSEXN_TYPEERR, "can't set prototype of
|
|||
MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE, 0, JSEXN_TYPEERR, "can't set prototype: it would cause a prototype chain cycle")
|
||||
MSG_DEF(JSMSG_INVALID_ARG_TYPE, 3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
|
||||
MSG_DEF(JSMSG_TERMINATED, 1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
|
||||
MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0}.prototype is not an object or null")
|
||||
|
||||
// JSON
|
||||
MSG_DEF(JSMSG_JSON_BAD_PARSE, 3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
|
||||
|
|
|
@ -38,6 +38,7 @@ ASTDEF(AST_COMP_EXPR, "ComprehensionExpression", "comprehensi
|
|||
ASTDEF(AST_GENERATOR_EXPR, "GeneratorExpression", "generatorExpression")
|
||||
ASTDEF(AST_YIELD_EXPR, "YieldExpression", "yieldExpression")
|
||||
ASTDEF(AST_LET_EXPR, "LetExpression", "letExpression")
|
||||
ASTDEF(AST_CLASS_EXPR, "ClassExpression", "classExpression")
|
||||
|
||||
ASTDEF(AST_EMPTY_STMT, "EmptyStatement", "emptyStatement")
|
||||
ASTDEF(AST_BLOCK_STMT, "BlockStatement", "blockStatement")
|
||||
|
|
|
@ -2076,7 +2076,9 @@ js::CloneFunctionObjectUseSameScript(JSCompartment *compartment, HandleFunction
|
|||
|
||||
JSFunction *
|
||||
js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
|
||||
gc::AllocKind allocKind, NewObjectKind newKindArg /* = GenericObject */)
|
||||
gc::AllocKind allocKind,
|
||||
NewObjectKind newKindArg /* = GenericObject */,
|
||||
HandleObject proto)
|
||||
{
|
||||
MOZ_ASSERT(parent);
|
||||
MOZ_ASSERT(!fun->isBoundFunction());
|
||||
|
@ -2087,8 +2089,8 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
|
|||
return nullptr;
|
||||
|
||||
NewObjectKind newKind = useSameScript ? newKindArg : SingletonObject;
|
||||
RootedObject cloneProto(cx);
|
||||
if (fun->isStarGenerator()) {
|
||||
RootedObject cloneProto(cx, proto);
|
||||
if (!cloneProto && fun->isStarGenerator()) {
|
||||
cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
|
||||
if (!cloneProto)
|
||||
return nullptr;
|
||||
|
|
|
@ -578,7 +578,8 @@ CloneFunctionObjectUseSameScript(JSCompartment *compartment, HandleFunction fun)
|
|||
extern JSFunction *
|
||||
CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
|
||||
gc::AllocKind kind = JSFunction::FinalizeKind,
|
||||
NewObjectKind newKindArg = GenericObject);
|
||||
NewObjectKind newKindArg = GenericObject,
|
||||
HandleObject proto = NullPtr());
|
||||
|
||||
extern bool
|
||||
FindBody(JSContext *cx, HandleFunction fun, HandleLinearString src, size_t *bodyStart,
|
||||
|
|
|
@ -53,6 +53,7 @@ CanReuseFunctionForClone(JSContext *cx, HandleFunction fun)
|
|||
|
||||
inline JSFunction *
|
||||
CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObject parent,
|
||||
HandleObject proto = NullPtr(),
|
||||
NewObjectKind newKind = GenericObject)
|
||||
{
|
||||
/*
|
||||
|
@ -71,6 +72,10 @@ CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObjec
|
|||
RootedObject obj(cx, SkipScopeParent(parent));
|
||||
if (!JSObject::setParent(cx, fun, obj))
|
||||
return nullptr;
|
||||
ObjectOpResult succeeded;
|
||||
if (proto && !SetPrototype(cx, fun, proto, succeeded))
|
||||
return nullptr;
|
||||
MOZ_ASSERT(!proto || succeeded);
|
||||
fun->setEnvironment(parent);
|
||||
return fun;
|
||||
}
|
||||
|
@ -82,7 +87,7 @@ CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObjec
|
|||
gc::AllocKind kind = fun->isExtended()
|
||||
? extendedFinalizeKind
|
||||
: finalizeKind;
|
||||
return CloneFunctionObject(cx, fun, parent, kind, newKind);
|
||||
return CloneFunctionObject(cx, fun, parent, kind, newKind, proto);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
|
|
@ -616,7 +616,8 @@ class NodeBuilder
|
|||
|
||||
bool exportBatchSpecifier(TokenPos *pos, MutableHandleValue dst);
|
||||
|
||||
bool classStatement(HandleValue name, HandleValue heritage, HandleValue block, TokenPos *pos, MutableHandleValue dst);
|
||||
bool classDefinition(bool expr, HandleValue name, HandleValue heritage, HandleValue block, TokenPos *pos,
|
||||
MutableHandleValue dst);
|
||||
bool classMethods(NodeVector &methods, MutableHandleValue dst);
|
||||
bool classMethod(HandleValue name, HandleValue body, PropKind kind, bool isStatic, TokenPos *pos, MutableHandleValue dst);
|
||||
|
||||
|
@ -1751,14 +1752,15 @@ NodeBuilder::classMethods(NodeVector &methods, MutableHandleValue dst)
|
|||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::classStatement(HandleValue name, HandleValue heritage, HandleValue block,
|
||||
TokenPos *pos, MutableHandleValue dst)
|
||||
NodeBuilder::classDefinition(bool expr, HandleValue name, HandleValue heritage, HandleValue block,
|
||||
TokenPos *pos, MutableHandleValue dst)
|
||||
{
|
||||
RootedValue cb(cx, callbacks[AST_CLASS_STMT]);
|
||||
ASTType type = expr ? AST_CLASS_EXPR : AST_CLASS_STMT;
|
||||
RootedValue cb(cx, callbacks[type]);
|
||||
if (!cb.isNull())
|
||||
return callback(cb, name, heritage, block, pos, dst);
|
||||
|
||||
return newNode(AST_CLASS_STMT, pos,
|
||||
return newNode(type, pos,
|
||||
"name", name,
|
||||
"heritage", heritage,
|
||||
"body", block,
|
||||
|
@ -1803,6 +1805,7 @@ class ASTSerializer
|
|||
bool importSpecifier(ParseNode *pn, MutableHandleValue dst);
|
||||
bool exportDeclaration(ParseNode *pn, MutableHandleValue dst);
|
||||
bool exportSpecifier(ParseNode *pn, MutableHandleValue dst);
|
||||
bool classDefinition(ParseNode *pn, bool expr, MutableHandleValue dst);
|
||||
|
||||
bool optStatement(ParseNode *pn, MutableHandleValue dst) {
|
||||
if (!pn) {
|
||||
|
@ -2411,6 +2414,23 @@ ASTSerializer::forIn(ParseNode *loop, ParseNode *head, HandleValue var, HandleVa
|
|||
builder.forInStatement(var, expr, stmt, isForEach, &loop->pn_pos, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::classDefinition(ParseNode *pn, bool expr, MutableHandleValue dst)
|
||||
{
|
||||
RootedValue className(cx, MagicValue(JS_SERIALIZE_NO_NODE));
|
||||
RootedValue heritage(cx);
|
||||
RootedValue classBody(cx);
|
||||
|
||||
if (pn->pn_kid1) {
|
||||
if (!identifier(pn->pn_kid1->as<ClassNames>().innerBinding(), &className))
|
||||
return false;
|
||||
}
|
||||
|
||||
return optExpression(pn->pn_kid2, &heritage) &&
|
||||
statement(pn->pn_kid3, &classBody) &&
|
||||
builder.classDefinition(expr, className, heritage, classBody, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::statement(ParseNode *pn, MutableHandleValue dst)
|
||||
{
|
||||
|
@ -2614,15 +2634,7 @@ ASTSerializer::statement(ParseNode *pn, MutableHandleValue dst)
|
|||
return builder.debuggerStatement(&pn->pn_pos, dst);
|
||||
|
||||
case PNK_CLASS:
|
||||
{
|
||||
RootedValue className(cx);
|
||||
RootedValue heritage(cx);
|
||||
RootedValue classBody(cx);
|
||||
return identifier(pn->pn_kid1->as<ClassNames>().innerBinding(), &className) &&
|
||||
optExpression(pn->pn_kid2, &heritage) &&
|
||||
statement(pn->pn_kid3, &classBody) &&
|
||||
builder.classStatement(className, heritage, classBody, &pn->pn_pos, dst);
|
||||
}
|
||||
return classDefinition(pn, false, dst);
|
||||
|
||||
case PNK_CLASSMETHODLIST:
|
||||
{
|
||||
|
@ -3159,6 +3171,9 @@ ASTSerializer::expression(ParseNode *pn, MutableHandleValue dst)
|
|||
case PNK_LETEXPR:
|
||||
return let(pn, true, dst);
|
||||
|
||||
case PNK_CLASS:
|
||||
return classDefinition(pn, true, dst);
|
||||
|
||||
default:
|
||||
LOCAL_NOT_REACHED("unexpected expression type");
|
||||
}
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
*
|
||||
* Date: 26 November 2000
|
||||
*
|
||||
*
|
||||
*SUMMARY: Passing a RegExp object to a RegExp() constructor.
|
||||
*This test arose from Bugzilla bug 61266. The ECMA3 section is:
|
||||
*
|
||||
* 15.10.4.1 new RegExp(pattern, flags)
|
||||
*
|
||||
* If pattern is an object R whose [[Class]] property is "RegExp" and
|
||||
* flags is undefined, then let P be the pattern used to construct R
|
||||
* and let F be the flags used to construct R. If pattern is an object R
|
||||
* whose [[Class]] property is "RegExp" and flags is not undefined,
|
||||
* then throw a TypeError exception. Otherwise, let P be the empty string
|
||||
* if pattern is undefined and ToString(pattern) otherwise, and let F be
|
||||
* the empty string if flags is undefined and ToString(flags) otherwise.
|
||||
*
|
||||
*
|
||||
*The current test will check the second scenario outlined above:
|
||||
*
|
||||
* "pattern" is itself a RegExp object R
|
||||
* "flags" is NOT undefined
|
||||
*
|
||||
* This should throw an exception ... we test for this.
|
||||
*
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
var BUGNUMBER = '61266';
|
||||
var summary = 'Negative test: Passing (RegExp object, flag) to RegExp() constructor';
|
||||
var statprefix = 'Passing RegExp object on pattern ';
|
||||
var statsuffix = '; passing flag ';
|
||||
var cnFAILURE = 'Expected an exception to be thrown, but none was -';
|
||||
var singlequote = "'";
|
||||
var i = -1; var j = -1; var s = ''; var f = '';
|
||||
var obj1 = {}; var obj2 = {};
|
||||
var patterns = new Array();
|
||||
var flags = new Array();
|
||||
|
||||
|
||||
// various regular expressions to try -
|
||||
patterns[0] = '';
|
||||
patterns[1] = 'abc';
|
||||
patterns[2] = '(.*)(3-1)\s\w';
|
||||
patterns[3] = '(.*)(...)\\s\\w';
|
||||
patterns[4] = '[^A-Za-z0-9_]';
|
||||
patterns[5] = '[^\f\n\r\t\v](123.5)([4 - 8]$)';
|
||||
|
||||
// various flags to try -
|
||||
flags[0] = 'i';
|
||||
flags[1] = 'g';
|
||||
flags[2] = 'm';
|
||||
|
||||
|
||||
DESCRIPTION = "Negative test: Passing (RegExp object, flag) to RegExp() constructor"
|
||||
EXPECTED = "error";
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
test();
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
enterFunc ('test');
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
for (i in patterns)
|
||||
{
|
||||
s = patterns[i];
|
||||
|
||||
for (j in flags)
|
||||
{
|
||||
f = flags[j];
|
||||
printStatus(getStatus(s, f));
|
||||
obj1 = new RegExp(s, f);
|
||||
obj2 = new RegExp(obj1, f); // this should cause an exception
|
||||
|
||||
// WE SHOULD NEVER REACH THIS POINT -
|
||||
reportCompare('PASS', 'FAIL', cnFAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
exitFunc ('test');
|
||||
}
|
||||
|
||||
|
||||
function getStatus(regexp, flag)
|
||||
{
|
||||
return (statprefix + quote(regexp) + statsuffix + flag);
|
||||
}
|
||||
|
||||
|
||||
function quote(text)
|
||||
{
|
||||
return (singlequote + text + singlequote);
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
var test = `
|
||||
|
||||
// It's an error to have a non-constructor as your heritage
|
||||
assertThrowsInstanceOf(() => eval(\`class a extends Math.sin {
|
||||
constructor() { }
|
||||
}\`), TypeError);
|
||||
assertThrowsInstanceOf(() => eval(\`(class a extends Math.sin {
|
||||
constructor() { }
|
||||
})\`), TypeError);
|
||||
|
||||
// Unless it's null, in which case it works like a normal class, except that
|
||||
// the prototype object does not inherit from Object.prototype.
|
||||
class basic {
|
||||
constructor() { }
|
||||
}
|
||||
class nullExtends extends null {
|
||||
constructor() { }
|
||||
}
|
||||
var nullExtendsExpr = class extends null { constructor() { } };
|
||||
|
||||
assertEq(Object.getPrototypeOf(basic), Function.prototype);
|
||||
assertEq(Object.getPrototypeOf(basic.prototype), Object.prototype);
|
||||
|
||||
for (let extension of [nullExtends, nullExtendsExpr]) {
|
||||
assertEq(Object.getPrototypeOf(extension), Function.prototype);
|
||||
assertEq(Object.getPrototypeOf(extension.prototype), null);
|
||||
}
|
||||
|
||||
var baseMethodCalled;
|
||||
var staticMethodCalled;
|
||||
var overrideCalled;
|
||||
class base {
|
||||
constructor() { };
|
||||
method() { baseMethodCalled = true; }
|
||||
static staticMethod() { staticMethodCalled = true; }
|
||||
override() { overrideCalled = "base" }
|
||||
}
|
||||
class derived extends base {
|
||||
constructor() { };
|
||||
override() { overrideCalled = "derived"; }
|
||||
}
|
||||
var derivedExpr = class extends base {
|
||||
constructor() { };
|
||||
override() { overrideCalled = "derived"; }
|
||||
};
|
||||
|
||||
// Make sure we get the right object layouts.
|
||||
for (let extension of [derived, derivedExpr]) {
|
||||
baseMethodCalled = false;
|
||||
staticMethodCalled = false;
|
||||
overrideCalled = "";
|
||||
// Make sure we get the right object layouts.
|
||||
assertEq(Object.getPrototypeOf(extension), base);
|
||||
assertEq(Object.getPrototypeOf(extension.prototype), base.prototype);
|
||||
|
||||
// We do inherit the methods, right?
|
||||
(new extension()).method();
|
||||
assertEq(baseMethodCalled, true);
|
||||
|
||||
// But we can still override them?
|
||||
(new extension()).override();
|
||||
assertEq(overrideCalled, "derived");
|
||||
|
||||
// What about the statics?
|
||||
extension.staticMethod();
|
||||
assertEq(staticMethodCalled, true);
|
||||
}
|
||||
|
||||
// Gotta extend an object, or null.
|
||||
function nope() {
|
||||
class Foo extends "Bar" {
|
||||
constructor() { }
|
||||
}
|
||||
}
|
||||
function nopeExpr() {
|
||||
(class extends "Bar" {
|
||||
constructor() { }
|
||||
});
|
||||
}
|
||||
assertThrowsInstanceOf(nope, TypeError);
|
||||
assertThrowsInstanceOf(nopeExpr, TypeError);
|
||||
|
||||
// The .prototype of the extension must be an object, or null.
|
||||
nope.prototype = "not really, no";
|
||||
function stillNo() {
|
||||
class Foo extends nope {
|
||||
constructor() { }
|
||||
}
|
||||
}
|
||||
function stillNoExpr() {
|
||||
(class extends nope {
|
||||
constructor() { }
|
||||
});
|
||||
}
|
||||
assertThrowsInstanceOf(stillNo, TypeError);
|
||||
assertThrowsInstanceOf(stillNoExpr, TypeError);
|
||||
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0, "OK");
|
|
@ -2,22 +2,25 @@ var test = `
|
|||
|
||||
// The prototype of a class is a non-writable, non-configurable, non-enumerable data property.
|
||||
class a { constructor() { } }
|
||||
var protoDesc = Object.getOwnPropertyDescriptor(a, "prototype");
|
||||
assertEq(protoDesc.writable, false);
|
||||
assertEq(protoDesc.configurable, false);
|
||||
assertEq(protoDesc.enumerable, false);
|
||||
let b = class { constructor() { } };
|
||||
for (let test of [a,b]) {
|
||||
var protoDesc = Object.getOwnPropertyDescriptor(test, "prototype");
|
||||
assertEq(protoDesc.writable, false);
|
||||
assertEq(protoDesc.configurable, false);
|
||||
assertEq(protoDesc.enumerable, false);
|
||||
|
||||
var prototype = protoDesc.value;
|
||||
assertEq(typeof prototype, "object");
|
||||
assertEq(Object.getPrototypeOf(prototype), Object.prototype);
|
||||
assertEq(Object.isExtensible(prototype), true);
|
||||
var prototype = protoDesc.value;
|
||||
assertEq(typeof prototype, "object");
|
||||
assertEq(Object.getPrototypeOf(prototype), Object.prototype);
|
||||
assertEq(Object.isExtensible(prototype), true);
|
||||
|
||||
var desiredPrototype = {};
|
||||
Object.defineProperty(desiredPrototype, "constructor", { writable: true,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: a });
|
||||
assertDeepEq(prototype, desiredPrototype);
|
||||
var desiredPrototype = {};
|
||||
Object.defineProperty(desiredPrototype, "constructor", { writable: true,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: test });
|
||||
assertDeepEq(prototype, desiredPrototype);
|
||||
}
|
||||
|
||||
try {
|
||||
eval(\`class a {
|
||||
|
@ -50,6 +53,25 @@ assertThrowsInstanceOf(() => eval(\`
|
|||
static set ["prototype"](x) { }
|
||||
}
|
||||
\`), TypeError);
|
||||
|
||||
assertThrowsInstanceOf(() => eval(\`(
|
||||
class a {
|
||||
constructor() { };
|
||||
static ["prototype"]() { }
|
||||
}
|
||||
)\`), TypeError);
|
||||
assertThrowsInstanceOf(() => eval(\`(
|
||||
class a {
|
||||
constructor() { };
|
||||
static get ["prototype"]() { }
|
||||
}
|
||||
)\`), TypeError);
|
||||
assertThrowsInstanceOf(() => eval(\`(
|
||||
class a {
|
||||
constructor() { };
|
||||
static set ["prototype"](x) { }
|
||||
}
|
||||
)\`), TypeError);
|
||||
*/
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -8,20 +8,40 @@ class a { constructor(x) { assertEq(x, 4); called = true } }
|
|||
new a(4);
|
||||
assertEq(called, true);
|
||||
|
||||
called = false;
|
||||
var aExpr = class { constructor(x) { assertEq(x, 4); called = true } };
|
||||
new aExpr(4);
|
||||
assertEq(called, true);
|
||||
|
||||
called = false;
|
||||
class b { constructor() { called = true } method() { } }
|
||||
new b();
|
||||
assertEq(called, true);
|
||||
|
||||
called = false;
|
||||
var bExpr = class { constructor() { called = true } method() { } };
|
||||
new bExpr();
|
||||
assertEq(called, true);
|
||||
|
||||
called = false;
|
||||
class c { method() { } constructor() { called = true; } }
|
||||
new c();
|
||||
assertEq(called, true);
|
||||
|
||||
called = false;
|
||||
var cExpr = class { method() { } constructor() { called = true; } }
|
||||
new cExpr();
|
||||
assertEq(called, true);
|
||||
|
||||
called = false;
|
||||
class d { [\"constructor\"]() { throw new Error(\"NO\"); } constructor() { called = true; } }
|
||||
new d();
|
||||
assertEq(called, true);
|
||||
|
||||
called = false;
|
||||
var dExpr = class { [\"constructor\"]() { throw new Error(\"NO\"); } constructor() { called = true; } }
|
||||
new dExpr();
|
||||
assertEq(called, true);
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
|
|
|
@ -1,23 +1,36 @@
|
|||
// Class statements should create an immutable inner binding. Since all code in
|
||||
// classes is in strict mode, attempts to mutate it should throw.
|
||||
// Named class definitions should create an immutable inner binding.
|
||||
// Since all code in classes is in strict mode, attempts to mutate it
|
||||
// should throw.
|
||||
|
||||
if (classesEnabled()) {
|
||||
|
||||
// XXXefaust Because we currently try to do assignment to const as an early error,
|
||||
// this is a syntax error. It is specced to be a TypeError
|
||||
// XXXefaust Because we currently try to do assignment to const as an early
|
||||
// error, sometimes, maybe, this is almost sometimes a syntax error.
|
||||
// It is specced to be a TypeError
|
||||
|
||||
function syntaxWrapper() {
|
||||
function statementWrapper() {
|
||||
eval("class Foo { constructor() { } tryBreak() { Foo = 4; } }");
|
||||
}
|
||||
assertThrowsInstanceOf(syntaxWrapper, SyntaxError);
|
||||
|
||||
function expressionWrapper() {
|
||||
// Mmmmm. Lazy parseing means we don't see this as an error until later.
|
||||
eval(`var x = class Foo { constructor() { }; tryBreak() { Foo = 4; } };
|
||||
new x().tryBreak();`);
|
||||
}
|
||||
|
||||
assertThrowsInstanceOf(statementWrapper, SyntaxError);
|
||||
assertThrowsInstanceOf(expressionWrapper, TypeError);
|
||||
|
||||
/*
|
||||
var test = `
|
||||
class Foo { constructor() { }; tryBreak() { Foo = 4; } }
|
||||
assertThrowsInstanceOf(() => new Foo().tryBreak(), TypeError);
|
||||
for (let result of [Foo, class Bar { constructor() { }; tryBreak() { Bar = 4; } }])
|
||||
assertThrowsInstanceOf(() => new result().tryBreak(), TypeError);
|
||||
|
||||
{
|
||||
class foo { constructor() { }; tryBreak() { foo = 4; } }
|
||||
assertThrowsInstanceOf(() => new foo().tryBreak(), TypeError);
|
||||
for (let result of [foo, class Bar { constructor() { }; tryBreak() { Bar = 4 }])
|
||||
assertThrowsInstanceOf(() => new result().tryBreak(), TypeError);
|
||||
}
|
||||
`;
|
||||
*/
|
||||
|
@ -30,6 +43,11 @@ assertThrowsInstanceOf(()=>eval(\`class Bar {
|
|||
[Bar] () { };
|
||||
}\`), ReferenceError);
|
||||
|
||||
assertThrowsInstanceOf(()=>eval(\`(class Bar {
|
||||
constructor() { };
|
||||
[Bar] () { };
|
||||
})\`), ReferenceError);
|
||||
|
||||
// There's no magic "inner binding" global
|
||||
{
|
||||
class Foo {
|
||||
|
@ -43,6 +61,15 @@ assertThrowsInstanceOf(()=>eval(\`class Bar {
|
|||
}
|
||||
}
|
||||
assertEq(new Foo().test(), false);
|
||||
assertEq(new class foo {
|
||||
constructor() { };
|
||||
test() {
|
||||
return new class bar {
|
||||
constructor() { }
|
||||
test() { return foo === bar }
|
||||
}().test();
|
||||
}
|
||||
}().test(), false);
|
||||
}
|
||||
|
||||
// Inner bindings are shadowable
|
||||
|
@ -52,8 +79,20 @@ assertThrowsInstanceOf(()=>eval(\`class Bar {
|
|||
test(Foo) { return Foo; }
|
||||
}
|
||||
assertEq(new Foo().test(4), 4);
|
||||
assertEq(new class foo {
|
||||
constructor() { };
|
||||
test(foo) { return foo }
|
||||
}().test(4), 4);
|
||||
}
|
||||
|
||||
// Inner bindings in expressions should shadow even existing names.
|
||||
class Foo { constructor() { } static method() { throw new Error("NO!"); } }
|
||||
assertEq(new class Foo {
|
||||
constructor() { };
|
||||
static method() { return 4; };
|
||||
test() { return Foo.method(); }
|
||||
}().test(), 4);
|
||||
|
||||
// The outer binding is distinct from the inner one
|
||||
{
|
||||
let orig_X;
|
||||
|
@ -68,6 +107,8 @@ assertThrowsInstanceOf(()=>eval(\`class Bar {
|
|||
assertEq(X, 13);
|
||||
new orig_X().f();
|
||||
}
|
||||
|
||||
|
||||
`;
|
||||
|
||||
eval(test);
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
var test= `
|
||||
|
||||
var methodCalled = false;
|
||||
var getterCalled = false;
|
||||
var setterCalled = false;
|
||||
var constructorCalled = false;
|
||||
var staticMethodCalled = false;
|
||||
var staticGetterCalled = false;
|
||||
var staticSetterCalled = false;
|
||||
class a {
|
||||
var methodCalled;
|
||||
var getterCalled;
|
||||
var setterCalled;
|
||||
var constructorCalled;
|
||||
var staticMethodCalled;
|
||||
var staticGetterCalled;
|
||||
var staticSetterCalled;
|
||||
class testClass {
|
||||
constructor() { constructorCalled = true; }
|
||||
__proto__() { methodCalled = true }
|
||||
get getter() { getterCalled = true; }
|
||||
|
@ -19,58 +19,80 @@ class a {
|
|||
static set staticSetter(x) { staticSetterCalled = true; }
|
||||
*[Symbol.iterator]() { yield "cow"; yield "pig"; }
|
||||
}
|
||||
var aConstDesc = Object.getOwnPropertyDescriptor(a.prototype, \"constructor\");
|
||||
assertEq(aConstDesc.writable, true);
|
||||
assertEq(aConstDesc.configurable, true);
|
||||
assertEq(aConstDesc.enumerable, false);
|
||||
aConstDesc.value();
|
||||
assertEq(constructorCalled, true);
|
||||
|
||||
// __proto__ is just an identifier for classes. No prototype changes are made.
|
||||
assertEq(Object.getPrototypeOf(a.prototype), Object.prototype);
|
||||
var aMethDesc = Object.getOwnPropertyDescriptor(a.prototype, \"__proto__\");
|
||||
assertEq(aMethDesc.writable, true);
|
||||
assertEq(aMethDesc.configurable, true);
|
||||
assertEq(aMethDesc.enumerable, true);
|
||||
aMethDesc.value();
|
||||
assertEq(methodCalled, true);
|
||||
for (let a of [testClass,
|
||||
class {
|
||||
constructor() { constructorCalled = true; }
|
||||
__proto__() { methodCalled = true }
|
||||
get getter() { getterCalled = true; }
|
||||
set setter(x) { setterCalled = true; }
|
||||
static staticMethod() { staticMethodCalled = true; }
|
||||
static get staticGetter() { staticGetterCalled = true; }
|
||||
static set staticSetter(x) { staticSetterCalled = true; }
|
||||
*[Symbol.iterator]() { yield "cow"; yield "pig"; }
|
||||
}]) {
|
||||
|
||||
var aGetDesc = Object.getOwnPropertyDescriptor(a.prototype, \"getter\");
|
||||
assertEq(aGetDesc.configurable, true);
|
||||
assertEq(aGetDesc.enumerable, true);
|
||||
aGetDesc.get();
|
||||
assertEq(getterCalled, true);
|
||||
methodCalled = false;
|
||||
getterCalled = false;
|
||||
setterCalled = false;
|
||||
constructorCalled = false;
|
||||
staticMethodCalled = false;
|
||||
staticGetterCalled = false;
|
||||
staticSetterCalled = false;
|
||||
|
||||
var aSetDesc = Object.getOwnPropertyDescriptor(a.prototype, \"setter\");
|
||||
assertEq(aSetDesc.configurable, true);
|
||||
assertEq(aSetDesc.enumerable, true);
|
||||
aSetDesc.set();
|
||||
assertEq(setterCalled, true);
|
||||
assertDeepEq(aSetDesc, Object.getOwnPropertyDescriptor(a.prototype, \"setter\"));
|
||||
var aConstDesc = Object.getOwnPropertyDescriptor(a.prototype, \"constructor\");
|
||||
assertEq(aConstDesc.writable, true);
|
||||
assertEq(aConstDesc.configurable, true);
|
||||
assertEq(aConstDesc.enumerable, false);
|
||||
aConstDesc.value();
|
||||
assertEq(constructorCalled, true);
|
||||
|
||||
assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticMethod\"), undefined);
|
||||
var aStaticMethDesc = Object.getOwnPropertyDescriptor(a, \"staticMethod\");
|
||||
assertEq(aStaticMethDesc.configurable, true);
|
||||
assertEq(aStaticMethDesc.enumerable, true);
|
||||
assertEq(aStaticMethDesc.writable, true);
|
||||
aStaticMethDesc.value();
|
||||
assertEq(staticMethodCalled, true);
|
||||
// __proto__ is just an identifier for classes. No prototype changes are made.
|
||||
assertEq(Object.getPrototypeOf(a.prototype), Object.prototype);
|
||||
var aMethDesc = Object.getOwnPropertyDescriptor(a.prototype, \"__proto__\");
|
||||
assertEq(aMethDesc.writable, true);
|
||||
assertEq(aMethDesc.configurable, true);
|
||||
assertEq(aMethDesc.enumerable, true);
|
||||
aMethDesc.value();
|
||||
assertEq(methodCalled, true);
|
||||
|
||||
assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticGetter\"), undefined);
|
||||
var aStaticGetDesc = Object.getOwnPropertyDescriptor(a, \"staticGetter\");
|
||||
assertEq(aStaticGetDesc.configurable, true);
|
||||
assertEq(aStaticGetDesc.enumerable, true);
|
||||
aStaticGetDesc.get();
|
||||
assertEq(staticGetterCalled, true);
|
||||
var aGetDesc = Object.getOwnPropertyDescriptor(a.prototype, \"getter\");
|
||||
assertEq(aGetDesc.configurable, true);
|
||||
assertEq(aGetDesc.enumerable, true);
|
||||
aGetDesc.get();
|
||||
assertEq(getterCalled, true);
|
||||
|
||||
assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticSetter\"), undefined);
|
||||
var aStaticSetDesc = Object.getOwnPropertyDescriptor(a, \"staticSetter\");
|
||||
assertEq(aStaticSetDesc.configurable, true);
|
||||
assertEq(aStaticSetDesc.enumerable, true);
|
||||
aStaticSetDesc.set();
|
||||
assertEq(staticSetterCalled, true);
|
||||
var aSetDesc = Object.getOwnPropertyDescriptor(a.prototype, \"setter\");
|
||||
assertEq(aSetDesc.configurable, true);
|
||||
assertEq(aSetDesc.enumerable, true);
|
||||
aSetDesc.set();
|
||||
assertEq(setterCalled, true);
|
||||
assertDeepEq(aSetDesc, Object.getOwnPropertyDescriptor(a.prototype, \"setter\"));
|
||||
|
||||
assertEq([...new a()].join(), "cow,pig");
|
||||
assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticMethod\"), undefined);
|
||||
var aStaticMethDesc = Object.getOwnPropertyDescriptor(a, \"staticMethod\");
|
||||
assertEq(aStaticMethDesc.configurable, true);
|
||||
assertEq(aStaticMethDesc.enumerable, true);
|
||||
assertEq(aStaticMethDesc.writable, true);
|
||||
aStaticMethDesc.value();
|
||||
assertEq(staticMethodCalled, true);
|
||||
|
||||
assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticGetter\"), undefined);
|
||||
var aStaticGetDesc = Object.getOwnPropertyDescriptor(a, \"staticGetter\");
|
||||
assertEq(aStaticGetDesc.configurable, true);
|
||||
assertEq(aStaticGetDesc.enumerable, true);
|
||||
aStaticGetDesc.get();
|
||||
assertEq(staticGetterCalled, true);
|
||||
|
||||
assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticSetter\"), undefined);
|
||||
var aStaticSetDesc = Object.getOwnPropertyDescriptor(a, \"staticSetter\");
|
||||
assertEq(aStaticSetDesc.configurable, true);
|
||||
assertEq(aStaticSetDesc.enumerable, true);
|
||||
aStaticSetDesc.set();
|
||||
assertEq(staticSetterCalled, true);
|
||||
|
||||
assertEq([...new a()].join(), "cow,pig");
|
||||
}
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
|
|
|
@ -1,41 +1,83 @@
|
|||
// Ensure that we can overwrite methods when more tha one is present.
|
||||
|
||||
var test = `
|
||||
var result = 0;
|
||||
// Regardless of order, the constructor is overridden by any CPN, because it's
|
||||
// processed seperately.
|
||||
class a { [\"constructor\"]() { result += 1; }; constructor() { result += 2; } }
|
||||
var aInst = new a();
|
||||
assertEq(result, 2);
|
||||
aInst.constructor();
|
||||
assertEq(result, 3);
|
||||
|
||||
class b { constructor() { result += 2; } [\"constructor\"]() { result += 1; }; }
|
||||
var bInst = new b();
|
||||
assertEq(result, 5);
|
||||
bInst.constructor();
|
||||
assertEq(result, 6);
|
||||
{
|
||||
var result = 0;
|
||||
// Regardless of order, the constructor is overridden by any CPN, because it's
|
||||
// processed seperately.
|
||||
class a { [\"constructor\"]() { result += 1; }; constructor() { result += 2; } }
|
||||
var aInst = new a();
|
||||
assertEq(result, 2);
|
||||
aInst.constructor();
|
||||
assertEq(result, 3);
|
||||
|
||||
class c { constructor() { } method() { result += 1 } get method() { result += 2 } }
|
||||
new c().method;
|
||||
assertEq(result, 8);
|
||||
class b { constructor() { result += 2; } [\"constructor\"]() { result += 1; }; }
|
||||
var bInst = new b();
|
||||
assertEq(result, 5);
|
||||
bInst.constructor();
|
||||
assertEq(result, 6);
|
||||
|
||||
class d { constructor() { } get method() { result += 1 } method() { result += 2 } }
|
||||
new d().method();
|
||||
assertEq(result, 10);
|
||||
class c { constructor() { } method() { result += 1 } get method() { result += 2 } }
|
||||
new c().method;
|
||||
assertEq(result, 8);
|
||||
|
||||
// getters and setter should not overwrite each other, but merge cleanly.
|
||||
class e { constructor() { } get method() { result += 1 } set method(x) { } }
|
||||
new e().method;
|
||||
assertEq(result, 11);
|
||||
class d { constructor() { } get method() { result += 1 } method() { result += 2 } }
|
||||
new d().method();
|
||||
assertEq(result, 10);
|
||||
|
||||
class f { constructor() { }
|
||||
set method(x) { throw "NO"; }
|
||||
method() { throw "NO" }
|
||||
get method() { return new Function("result += 1"); }
|
||||
}
|
||||
new f().method();
|
||||
assertEq(result, 12);
|
||||
// getters and setter should not overwrite each other, but merge cleanly.
|
||||
class e { constructor() { } get method() { result += 1 } set method(x) { } }
|
||||
new e().method;
|
||||
assertEq(result, 11);
|
||||
|
||||
class f { constructor() { }
|
||||
set method(x) { throw "NO"; }
|
||||
method() { throw "NO" }
|
||||
get method() { return new Function("result += 1"); }
|
||||
}
|
||||
new f().method();
|
||||
assertEq(result, 12);
|
||||
}
|
||||
|
||||
// Try again with expressions.
|
||||
{
|
||||
var result = 0;
|
||||
// Regardless of order, the constructor is overridden by any CPN, because it's
|
||||
// processed seperately.
|
||||
let a = class { [\"constructor\"]() { result += 1; }; constructor() { result += 2; } };
|
||||
var aInst = new a();
|
||||
assertEq(result, 2);
|
||||
aInst.constructor();
|
||||
assertEq(result, 3);
|
||||
|
||||
let b = class { constructor() { result += 2; } [\"constructor\"]() { result += 1; }; };
|
||||
var bInst = new b();
|
||||
assertEq(result, 5);
|
||||
bInst.constructor();
|
||||
assertEq(result, 6);
|
||||
|
||||
let c = class { constructor() { } method() { result += 1 } get method() { result += 2 } };
|
||||
new c().method;
|
||||
assertEq(result, 8);
|
||||
|
||||
let d = class { constructor() { } get method() { result += 1 } method() { result += 2 } };
|
||||
new d().method();
|
||||
assertEq(result, 10);
|
||||
|
||||
// getters and setter should not overwrite each other, but merge cleanly.
|
||||
let e = class { constructor() { } get method() { result += 1 } set method(x) { } };
|
||||
new e().method;
|
||||
assertEq(result, 11);
|
||||
|
||||
let f = class { constructor() { }
|
||||
set method(x) { throw "NO"; }
|
||||
method() { throw "NO" }
|
||||
get method() { return new Function("result += 1"); }
|
||||
};
|
||||
new f().method();
|
||||
assertEq(result, 12);
|
||||
}
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// A class creates a mutable lexical outer binding.
|
||||
// A class statement creates a mutable lexical outer binding.
|
||||
|
||||
var test = `
|
||||
class Foo { constructor() { } }
|
||||
|
|
|
@ -1,16 +1,39 @@
|
|||
// Classes are always strict mode. Check computed property names as well.
|
||||
// Classes are always strict mode. Check computed property names and heritage
|
||||
// expressions as well.
|
||||
|
||||
var test = `
|
||||
class a { constructor() { Object.preventExtensions({}).prop = 0; } }
|
||||
assertThrowsInstanceOf(() => new a(), TypeError);
|
||||
var aExpr = class { constructor() { Object.preventExtensions().prop = 0; } };
|
||||
assertThrowsInstanceOf(() => new aExpr(), TypeError);
|
||||
|
||||
function shouldThrow() {
|
||||
function shouldThrowCPN() {
|
||||
class b {
|
||||
[Object.preventExtensions({}).prop = 4]() { }
|
||||
constructor() { }
|
||||
}
|
||||
}
|
||||
assertThrowsInstanceOf(shouldThrow, TypeError);
|
||||
function shouldThrowCPNExpr() {
|
||||
var b = class {
|
||||
[Object.preventExtensions({}).prop = 4]() { }
|
||||
constructor() { }
|
||||
};
|
||||
}
|
||||
assertThrowsInstanceOf(shouldThrowCPN, TypeError);
|
||||
assertThrowsInstanceOf(shouldThrowCPNExpr, TypeError);
|
||||
|
||||
function shouldThrowHeritage() {
|
||||
class b extends (Object.preventExtensions({}).prop = 4) {
|
||||
constructor() { }
|
||||
}
|
||||
}
|
||||
function shouldThrowHeritageExpr() {
|
||||
var b = class extends (Object.preventExtensions({}).prop = 4) {
|
||||
constructor() { }
|
||||
};
|
||||
}
|
||||
assertThrowsInstanceOf(shouldThrowHeritage, TypeError);
|
||||
assertThrowsInstanceOf(shouldThrowHeritageExpr, TypeError);
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
assertEq(RegExp(/foo/my).flags, "my");
|
||||
assertEq(RegExp(/foo/, "gi").flags, "gi");
|
||||
assertEq(RegExp(/foo/my, "gi").flags, "gi");
|
||||
assertEq(RegExp(/foo/my, "").flags, "");
|
||||
assertEq(RegExp(/foo/my, undefined).flags, "my");
|
||||
assertThrowsInstanceOf(() => RegExp(/foo/my, null), SyntaxError);
|
||||
assertThrowsInstanceOf(() => RegExp(/foo/my, "foo"), SyntaxError);
|
||||
|
||||
assertEq(/a/.compile("b", "gi").flags, "gi");
|
||||
assertEq(/a/.compile(/b/my).flags, "my");
|
||||
assertEq(/a/.compile(/b/my, undefined).flags, "my");
|
||||
assertThrowsInstanceOf(() => /a/.compile(/b/my, "gi"), TypeError);
|
||||
assertThrowsInstanceOf(() => /a/.compile(/b/my, ""), TypeError);
|
||||
assertThrowsInstanceOf(() => /a/.compile(/b/my, null), TypeError);
|
||||
assertThrowsInstanceOf(() => /a/.compile(/b/my, "foo"), TypeError);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
|
@ -72,6 +72,10 @@ function classStmt(id, heritage, body) Pattern({ type: "ClassStatement",
|
|||
name: id,
|
||||
heritage: heritage,
|
||||
body: body})
|
||||
function classExpr(id, heritage, body) Pattern({ type: "ClassExpression",
|
||||
name: id,
|
||||
heritage: heritage,
|
||||
body: body})
|
||||
function classMethod(id, body, kind, static) Pattern({ type: "ClassMethod",
|
||||
name: id,
|
||||
body: body,
|
||||
|
@ -1158,9 +1162,6 @@ function classesEnabled() {
|
|||
}
|
||||
|
||||
function testClasses() {
|
||||
// No unnamed class statements.
|
||||
assertError("class { constructor() { } }", SyntaxError);
|
||||
|
||||
function simpleMethod(id, kind, generator, args=[], isStatic=false) {
|
||||
assertEq(generator && kind === "method", generator);
|
||||
let idN = ident(id);
|
||||
|
@ -1175,73 +1176,98 @@ function testClasses() {
|
|||
funExpr(null, [], blockStmt([])),
|
||||
"method", isStatic);
|
||||
}
|
||||
function setClassMethods(class_, methods) {
|
||||
class_.template.body = methods;
|
||||
|
||||
function assertClassExpr(str, methods, heritage=null, name=null) {
|
||||
let template = classExpr(name, heritage, methods);
|
||||
assertExpr("(" + str + ")", template);
|
||||
}
|
||||
function assertClass(str, methods, heritage=null) {
|
||||
let namelessStr = str.replace("NAME", "");
|
||||
let namedStr = str.replace("NAME", "Foo");
|
||||
assertClassExpr(namelessStr, methods, heritage);
|
||||
assertClassExpr(namedStr, methods, heritage, ident("Foo"));
|
||||
|
||||
let template = classStmt(ident("Foo"), heritage, methods);
|
||||
assertStmt(namedStr, template);
|
||||
}
|
||||
function assertNamedClassError(str, error) {
|
||||
assertError(str, error);
|
||||
assertError("(" + str + ")", error);
|
||||
}
|
||||
function assertClassError(str, error) {
|
||||
assertNamedClassError(str, error);
|
||||
assertError("(" + str.replace("NAME", "") + ")", error);
|
||||
}
|
||||
|
||||
let simpleConstructor = simpleMethod("constructor", "method", false);
|
||||
let emptyFooClass = classStmt(ident("Foo"), null, [simpleConstructor]);
|
||||
|
||||
/* Trivial classes */
|
||||
assertStmt("class Foo { constructor() { } }", emptyFooClass);
|
||||
// Unnamed class statements are forbidden, but unnamed class expressions are
|
||||
// just fine.
|
||||
assertError("class { constructor() { } }", SyntaxError);
|
||||
assertClass("class NAME { constructor() { } }", [simpleConstructor]);
|
||||
|
||||
// A class name must actually be a name
|
||||
assertNamedClassError("class x.y { constructor() {} }", SyntaxError);
|
||||
assertNamedClassError("class [] { constructor() {} }", SyntaxError);
|
||||
assertNamedClassError("class {x} { constructor() {} }", SyntaxError);
|
||||
assertNamedClassError("class for { constructor() {} }", SyntaxError);
|
||||
|
||||
// Allow methods and accessors
|
||||
let stmt = classStmt(ident("Foo"), null,
|
||||
[simpleConstructor, simpleMethod("method", "method", false)]);
|
||||
assertStmt("class Foo { constructor() { } method() { } }", stmt);
|
||||
assertClass("class NAME { constructor() { } method() { } }",
|
||||
[simpleConstructor, simpleMethod("method", "method", false)]);
|
||||
|
||||
setClassMethods(stmt, [simpleConstructor, simpleMethod("method", "get", false)]);
|
||||
assertStmt("class Foo { constructor() { } get method() { } }", stmt);
|
||||
assertClass("class NAME { constructor() { } get method() { } }",
|
||||
[simpleConstructor, simpleMethod("method", "get", false)]);
|
||||
|
||||
setClassMethods(stmt, [simpleConstructor, simpleMethod("method", "set", false, ["x"])]);
|
||||
assertStmt("class Foo { constructor() { } set method(x) { } }", stmt);
|
||||
assertClass("class NAME { constructor() { } set method(x) { } }",
|
||||
[simpleConstructor, simpleMethod("method", "set", false, ["x"])]);
|
||||
|
||||
/* Static */
|
||||
setClassMethods(stmt, [simpleConstructor,
|
||||
simpleMethod("method", "method", false, [], true),
|
||||
simpleMethod("methodGen", "method", true, [], true),
|
||||
simpleMethod("getter", "get", false, [], true),
|
||||
simpleMethod("setter", "set", false, ["x"], true)]);
|
||||
assertStmt(`class Foo {
|
||||
constructor() { };
|
||||
static method() { };
|
||||
static *methodGen() { };
|
||||
static get getter() { };
|
||||
static set setter(x) { }
|
||||
}`, stmt);
|
||||
|
||||
assertClass(`class NAME {
|
||||
constructor() { };
|
||||
static method() { };
|
||||
static *methodGen() { };
|
||||
static get getter() { };
|
||||
static set setter(x) { }
|
||||
}`,
|
||||
[simpleConstructor,
|
||||
simpleMethod("method", "method", false, [], true),
|
||||
simpleMethod("methodGen", "method", true, [], true),
|
||||
simpleMethod("getter", "get", false, [], true),
|
||||
simpleMethod("setter", "set", false, ["x"], true)]);
|
||||
|
||||
// It's not an error to have a method named static, static, or not.
|
||||
setClassMethods(stmt, [simpleConstructor, simpleMethod("static", "method", false, [], false)]);
|
||||
assertStmt("class Foo{ constructor() { } static() { } }", stmt);
|
||||
setClassMethods(stmt, [simpleMethod("static", "method", false, [], true), simpleConstructor]);
|
||||
assertStmt("class Foo{ static static() { }; constructor() { } }", stmt);
|
||||
setClassMethods(stmt, [simpleMethod("static", "get", false, [], true), simpleConstructor]);
|
||||
assertStmt("class Foo { static get static() { }; constructor() { } }", stmt);
|
||||
setClassMethods(stmt, [simpleConstructor, simpleMethod("static", "set", false, ["x"], true)]);
|
||||
assertStmt("class Foo { constructor() { }; static set static(x) { } }", stmt);
|
||||
assertClass("class NAME { constructor() { } static() { } }",
|
||||
[simpleConstructor, simpleMethod("static", "method", false)]);
|
||||
assertClass("class NAME { static static() { }; constructor() { } }",
|
||||
[simpleMethod("static", "method", false, [], true), simpleConstructor]);
|
||||
assertClass("class NAME { static get static() { }; constructor() { } }",
|
||||
[simpleMethod("static", "get", false, [], true), simpleConstructor]);
|
||||
assertClass("class NAME { constructor() { }; static set static(x) { } }",
|
||||
[simpleConstructor, simpleMethod("static", "set", false, ["x"], true)]);
|
||||
|
||||
// You do, however, have to put static in the right spot
|
||||
assertError("class Foo { constructor() { }; get static foo() { } }", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { }; get static foo() { } }", SyntaxError);
|
||||
|
||||
// Spec disallows "prototype" as a static member in a class, since that
|
||||
// one's important to make the desugaring work
|
||||
assertError("class Foo { constructor() { } static prototype() { } }", SyntaxError);
|
||||
assertError("class Foo { constructor() { } static *prototype() { } }", SyntaxError);
|
||||
assertError("class Foo { static get prototype() { }; constructor() { } }", SyntaxError);
|
||||
assertError("class Foo { static set prototype(x) { }; constructor() { } }", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { } static prototype() { } }", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { } static *prototype() { } }", SyntaxError);
|
||||
assertClassError("class NAME { static get prototype() { }; constructor() { } }", SyntaxError);
|
||||
assertClassError("class NAME { static set prototype(x) { }; constructor() { } }", SyntaxError);
|
||||
|
||||
// You are, however, allowed to have a CPN called prototype as a static
|
||||
setClassMethods(stmt, [simpleConstructor, emptyCPNMethod("prototype", true)]);
|
||||
assertStmt("class Foo { constructor() { }; static [\"prototype\"]() { } }", stmt);
|
||||
assertClass("class NAME { constructor() { }; static [\"prototype\"]() { } }",
|
||||
[simpleConstructor, emptyCPNMethod("prototype", true)]);
|
||||
|
||||
/* Constructor */
|
||||
// Currently, we do not allow default constructors
|
||||
assertError("class Foo { }", TypeError);
|
||||
assertClassError("class NAME { }", TypeError);
|
||||
|
||||
// It is an error to have two methods named constructor, but not other
|
||||
// names, regardless if one is an accessor or a generator or static.
|
||||
assertError("class Foo { constructor() { } constructor(a) { } }", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { } constructor(a) { } }", SyntaxError);
|
||||
let methods = [["method() { }", simpleMethod("method", "method", false)],
|
||||
["*method() { }", simpleMethod("method", "method", true)],
|
||||
["get method() { }", simpleMethod("method", "get", false)],
|
||||
|
@ -1253,86 +1279,158 @@ function testClasses() {
|
|||
let i,j;
|
||||
for (i=0; i < methods.length; i++) {
|
||||
for (j=0; j < methods.length; j++) {
|
||||
setClassMethods(stmt,
|
||||
[simpleConstructor,
|
||||
methods[i][1],
|
||||
methods[j][1]]);
|
||||
let str = "class Foo { constructor() { } " +
|
||||
let str = "class NAME { constructor() { } " +
|
||||
methods[i][0] + " " + methods[j][0] +
|
||||
" }";
|
||||
assertStmt(str, stmt);
|
||||
assertClass(str, [simpleConstructor, methods[i][1], methods[j][1]]);
|
||||
}
|
||||
}
|
||||
|
||||
// It is, however, not an error to have a constructor, and a method with a
|
||||
// computed property name 'constructor'
|
||||
setClassMethods(stmt, [simpleConstructor, emptyCPNMethod("constructor", false)]);
|
||||
assertStmt("class Foo { constructor () { } [\"constructor\"] () { } }", stmt);
|
||||
assertClass("class NAME { constructor () { } [\"constructor\"] () { } }",
|
||||
[simpleConstructor, emptyCPNMethod("constructor", false)]);
|
||||
|
||||
// It is an error to have a generator or accessor named constructor
|
||||
assertError("class Foo { *constructor() { } }", SyntaxError);
|
||||
assertError("class Foo { get constructor() { } }", SyntaxError);
|
||||
assertError("class Foo { set constructor() { } }", SyntaxError);
|
||||
assertClassError("class NAME { *constructor() { } }", SyntaxError);
|
||||
assertClassError("class NAME { get constructor() { } }", SyntaxError);
|
||||
assertClassError("class NAME { set constructor() { } }", SyntaxError);
|
||||
|
||||
/* Semicolons */
|
||||
// Allow Semicolons in Class Definitions
|
||||
assertStmt("class Foo { constructor() { }; }", emptyFooClass);
|
||||
assertClass("class NAME { constructor() { }; }", [simpleConstructor]);
|
||||
|
||||
// Allow more than one semicolon, even in otherwise trivial classses
|
||||
assertStmt("class Foo { ;;; constructor() { } }", emptyFooClass);
|
||||
assertClass("class NAME { ;;; constructor() { } }", [simpleConstructor]);
|
||||
|
||||
// Semicolons are optional, even if the methods share a line
|
||||
setClassMethods(stmt, [simpleMethod("method", "method", false), simpleConstructor]);
|
||||
assertStmt("class Foo { method() { } constructor() { } }", stmt);
|
||||
assertClass("class NAME { method() { } constructor() { } }",
|
||||
[simpleMethod("method", "method", false), simpleConstructor]);
|
||||
|
||||
/* Generators */
|
||||
// No yield as a class name inside a generator
|
||||
assertError("function *foo() {\
|
||||
class yield {\
|
||||
constructor() { } \
|
||||
}\
|
||||
}", SyntaxError);
|
||||
assertError(`function *foo() {
|
||||
class yield {
|
||||
constructor() { }
|
||||
}
|
||||
}`, SyntaxError);
|
||||
assertError(`function *foo() {
|
||||
(class yield {
|
||||
constructor() { }
|
||||
})
|
||||
}`, SyntaxError);
|
||||
|
||||
// Methods may be generators, but not accessors
|
||||
assertError("class Foo { constructor() { } *get foo() { } }", SyntaxError);
|
||||
assertError("class Foo { constructor() { } *set foo() { } }", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { } *get foo() { } }", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { } *set foo() { } }", SyntaxError);
|
||||
|
||||
setClassMethods(stmt, [simpleMethod("method", "method", true), simpleConstructor]);
|
||||
assertStmt("class Foo { *method() { } constructor() { } }", stmt);
|
||||
assertClass("class NAME { *method() { } constructor() { } }",
|
||||
[simpleMethod("method", "method", true), simpleConstructor]);
|
||||
|
||||
/* Strictness */
|
||||
// yield is a strict-mode keyword, and class definitions are always strict.
|
||||
assertError("class Foo { constructor() { var yield; } }", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { var yield; } }", SyntaxError);
|
||||
|
||||
// Beware of the strictness of computed property names. Here use bareword
|
||||
// deletion (a deprecated action) to check.
|
||||
assertError("class Foo { constructor() { } [delete bar]() { }}", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { } [delete bar]() { }}", SyntaxError);
|
||||
|
||||
/* Bindings */
|
||||
// Classes should bind lexically, so they should collide with other in-block
|
||||
// lexical bindings
|
||||
// Class statements bind lexically, so they should collide with other
|
||||
// in-block lexical bindings, but class expressions don't.
|
||||
assertError("{ let Foo; class Foo { constructor() { } } }", TypeError);
|
||||
assertStmt("{ let Foo; (class Foo { constructor() { } }) }",
|
||||
blockStmt([letDecl([{id: ident("Foo"), init: null}]),
|
||||
exprStmt(classExpr(ident("Foo"), null, [simpleConstructor]))]));
|
||||
assertError("{ const Foo = 0; class Foo { constructor() { } } }", TypeError);
|
||||
assertStmt("{ const Foo = 0; (class Foo { constructor() { } }) }",
|
||||
blockStmt([constDecl([{id: ident("Foo"), init: lit(0)}]),
|
||||
exprStmt(classExpr(ident("Foo"), null, [simpleConstructor]))]));
|
||||
assertError("{ class Foo { constructor() { } } class Foo { constructor() { } } }", TypeError);
|
||||
assertStmt(`{
|
||||
(class Foo {
|
||||
constructor() { }
|
||||
},
|
||||
class Foo {
|
||||
constructor() { }
|
||||
});
|
||||
}`,
|
||||
blockStmt([exprStmt(seqExpr([classExpr(ident("Foo"), null, [simpleConstructor]),
|
||||
classExpr(ident("Foo"), null, [simpleConstructor])]))]));
|
||||
assertStmt(`{
|
||||
var x = class Foo { constructor() { } };
|
||||
class Foo { constructor() { } }
|
||||
}`,
|
||||
blockStmt([varDecl([{ id: ident("x"),
|
||||
init: classExpr(ident("Foo"), null, [simpleConstructor])
|
||||
}]),
|
||||
classStmt(ident("Foo"), null, [simpleConstructor])]));
|
||||
|
||||
|
||||
// Can't make a lexical binding inside a block.
|
||||
assertError("if (1) class Foo { constructor() { } }", SyntaxError);
|
||||
|
||||
/* Heritage Expressions */
|
||||
// It's illegal to have things that look like "multiple inheritance":
|
||||
// non-parenthesized comma expressions.
|
||||
assertClassError("class NAME extends null, undefined { constructor() { } }", SyntaxError);
|
||||
|
||||
// Again check for strict-mode in heritage expressions
|
||||
assertClassError("class NAME extends (delete x) { constructor() { } }", SyntaxError);
|
||||
|
||||
// You must specify an inheritance if you say "extends"
|
||||
assertClassError("class NAME extends { constructor() { } }", SyntaxError);
|
||||
|
||||
// "extends" is still a valid name for a method
|
||||
assertClass("class NAME { constructor() { }; extends() { } }",
|
||||
[simpleConstructor, simpleMethod("extends", "method", false)]);
|
||||
|
||||
// Immediate expression
|
||||
assertClass("class NAME extends null { constructor() { } }",
|
||||
[simpleConstructor], lit(null));
|
||||
|
||||
// Sequence expresson
|
||||
assertClass("class NAME extends (undefined, undefined) { constructor() { } }",
|
||||
[simpleConstructor], seqExpr([ident("undefined"), ident("undefined")]));
|
||||
|
||||
// Function expression
|
||||
let emptyFunction = funExpr(null, [], blockStmt([]));
|
||||
assertClass("class NAME extends function(){ } { constructor() { } }",
|
||||
[simpleConstructor], emptyFunction);
|
||||
|
||||
// New expression
|
||||
assertClass("class NAME extends new function(){ }() { constructor() { } }",
|
||||
[simpleConstructor], newExpr(emptyFunction, []));
|
||||
|
||||
// Call expression
|
||||
assertClass("class NAME extends function(){ }() { constructor() { } }",
|
||||
[simpleConstructor], callExpr(emptyFunction, []));
|
||||
|
||||
// Dot expression
|
||||
assertClass("class NAME extends {}.foo { constructor() { } }",
|
||||
[simpleConstructor], dotExpr(objExpr([]), ident("foo")));
|
||||
|
||||
// Member expression
|
||||
assertClass("class NAME extends {}[foo] { constructor() { } }",
|
||||
[simpleConstructor], memExpr(objExpr([]), ident("foo")));
|
||||
|
||||
/* EOF */
|
||||
// Clipped classes should throw a syntax error
|
||||
assertError("class Foo {", SyntaxError);
|
||||
assertError("class Foo {;", SyntaxError);
|
||||
assertError("class Foo { constructor", SyntaxError);
|
||||
assertError("class Foo { constructor(", SyntaxError);
|
||||
assertError("class Foo { constructor()", SyntaxError);
|
||||
assertError("class Foo { constructor()", SyntaxError);
|
||||
assertError("class Foo { constructor() {", SyntaxError);
|
||||
assertError("class Foo { constructor() { }", SyntaxError);
|
||||
assertError("class Foo { static", SyntaxError);
|
||||
assertError("class Foo { static y", SyntaxError);
|
||||
assertError("class Foo { static *", SyntaxError);
|
||||
assertError("class Foo { static *y", SyntaxError);
|
||||
assertError("class Foo { static get", SyntaxError);
|
||||
assertError("class Foo { static get y", SyntaxError);
|
||||
assertClassError("class NAME {", SyntaxError);
|
||||
assertClassError("class NAME {;", SyntaxError);
|
||||
assertClassError("class NAME { constructor", SyntaxError);
|
||||
assertClassError("class NAME { constructor(", SyntaxError);
|
||||
assertClassError("class NAME { constructor()", SyntaxError);
|
||||
assertClassError("class NAME { constructor()", SyntaxError);
|
||||
assertClassError("class NAME { constructor() {", SyntaxError);
|
||||
assertClassError("class NAME { constructor() { }", SyntaxError);
|
||||
assertClassError("class NAME { static", SyntaxError);
|
||||
assertClassError("class NAME { static y", SyntaxError);
|
||||
assertClassError("class NAME { static *", SyntaxError);
|
||||
assertClassError("class NAME { static *y", SyntaxError);
|
||||
assertClassError("class NAME { static get", SyntaxError);
|
||||
assertClassError("class NAME { static get y", SyntaxError);
|
||||
assertClassError("class NAME extends", SyntaxError);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1650,9 +1650,6 @@ CASE(EnableInterruptsPseudoOpcode)
|
|||
/* Various 1-byte no-ops. */
|
||||
CASE(JSOP_NOP)
|
||||
CASE(JSOP_UNUSED2)
|
||||
CASE(JSOP_UNUSED51)
|
||||
CASE(JSOP_UNUSED52)
|
||||
CASE(JSOP_UNUSED83)
|
||||
CASE(JSOP_UNUSED92)
|
||||
CASE(JSOP_UNUSED103)
|
||||
CASE(JSOP_UNUSED104)
|
||||
|
@ -3527,6 +3524,70 @@ CASE(JSOP_ARRAYPUSH)
|
|||
}
|
||||
END_CASE(JSOP_ARRAYPUSH)
|
||||
|
||||
CASE(JSOP_CLASSHERITAGE)
|
||||
{
|
||||
RootedValue &val = rootValue0;
|
||||
val = REGS.sp[-1];
|
||||
|
||||
RootedValue &objProto = rootValue1;
|
||||
RootedObject &funcProto = rootObject0;
|
||||
if (val.isNull()) {
|
||||
objProto.setNull();
|
||||
if (!GetBuiltinPrototype(cx, JSProto_Function, &funcProto))
|
||||
goto error;
|
||||
} else {
|
||||
if (!val.isObject() || !val.toObject().isConstructor()) {
|
||||
ReportIsNotFunction(cx, val, 0, CONSTRUCT);
|
||||
goto error;
|
||||
}
|
||||
|
||||
funcProto = &val.toObject();
|
||||
|
||||
if (!GetProperty(cx, funcProto, funcProto, cx->names().prototype, &objProto))
|
||||
goto error;
|
||||
|
||||
if (!objProto.isObjectOrNull()) {
|
||||
ReportValueError(cx, JSMSG_PROTO_NOT_OBJORNULL, -1, objProto, NullPtr());
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
REGS.sp[-1] = objProto;
|
||||
PUSH_OBJECT(*funcProto);
|
||||
}
|
||||
END_CASE(JSOP_CLASSHERITAGE)
|
||||
|
||||
CASE(JSOP_FUNWITHPROTO)
|
||||
{
|
||||
RootedObject &proto = rootObject1;
|
||||
proto = ®S.sp[-1].toObject();
|
||||
|
||||
/* Load the specified function object literal. */
|
||||
RootedFunction &fun = rootFunction0;
|
||||
fun = script->getFunction(GET_UINT32_INDEX(REGS.pc));
|
||||
|
||||
JSObject *obj = CloneFunctionObjectIfNotSingleton(cx, fun, REGS.fp()->scopeChain(),
|
||||
proto, GenericObject);
|
||||
if (!obj)
|
||||
goto error;
|
||||
|
||||
REGS.sp[-1].setObject(*obj);
|
||||
}
|
||||
END_CASE(JSOP_FUNWITHPROTO)
|
||||
|
||||
CASE(JSOP_OBJWITHPROTO)
|
||||
{
|
||||
RootedObject &proto = rootObject0;
|
||||
proto = REGS.sp[-1].toObjectOrNull();
|
||||
|
||||
JSObject *obj = NewObjectWithGivenProto<PlainObject>(cx, proto, cx->global());
|
||||
if (!obj)
|
||||
goto error;
|
||||
|
||||
REGS.sp[-1].setObject(*obj);
|
||||
}
|
||||
END_CASE(JSOP_OBJWITHPROTO)
|
||||
|
||||
DEFAULT()
|
||||
{
|
||||
char numBuf[12];
|
||||
|
@ -3709,7 +3770,8 @@ js::LambdaArrow(JSContext *cx, HandleFunction fun, HandleObject parent, HandleVa
|
|||
{
|
||||
MOZ_ASSERT(fun->isArrow());
|
||||
|
||||
RootedObject clone(cx, CloneFunctionObjectIfNotSingleton(cx, fun, parent, TenuredObject));
|
||||
RootedObject clone(cx, CloneFunctionObjectIfNotSingleton(cx, fun, parent, NullPtr(),
|
||||
TenuredObject));
|
||||
if (!clone)
|
||||
return nullptr;
|
||||
|
||||
|
@ -3735,7 +3797,7 @@ js::DefFunOperation(JSContext *cx, HandleScript script, HandleObject scopeChain,
|
|||
*/
|
||||
RootedFunction fun(cx, funArg);
|
||||
if (fun->isNative() || fun->environment() != scopeChain) {
|
||||
fun = CloneFunctionObjectIfNotSingleton(cx, fun, scopeChain, TenuredObject);
|
||||
fun = CloneFunctionObjectIfNotSingleton(cx, fun, scopeChain, NullPtr(), TenuredObject);
|
||||
if (!fun)
|
||||
return false;
|
||||
} else {
|
||||
|
|
|
@ -44,9 +44,9 @@
|
|||
macro(import, import, TOK_IMPORT, JSVERSION_DEFAULT) \
|
||||
macro(export, export, TOK_EXPORT, JSVERSION_DEFAULT) \
|
||||
macro(class, class_, TOK_CLASS, JSVERSION_DEFAULT) \
|
||||
macro(extends, extends, TOK_EXTENDS, JSVERSION_DEFAULT) \
|
||||
/* Reserved keywords. */ \
|
||||
macro(enum, enum_, TOK_RESERVED, JSVERSION_DEFAULT) \
|
||||
macro(extends, extends, TOK_RESERVED, JSVERSION_DEFAULT) \
|
||||
macro(super, super, TOK_RESERVED, JSVERSION_DEFAULT) \
|
||||
/* Future reserved keywords, but only in strict mode. */ \
|
||||
macro(implements, implements, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
|
||||
|
|
|
@ -491,6 +491,11 @@ class ObjectGroup : public gc::TenuredCell
|
|||
/* Get a property only if it already exists. */
|
||||
inline HeapTypeSet *maybeGetProperty(jsid id);
|
||||
|
||||
/*
|
||||
* Iterate through the group's properties. getPropertyCount overapproximates
|
||||
* in the hash case (see SET_ARRAY_SIZE in TypeInference-inl.h), and
|
||||
* getProperty may return nullptr.
|
||||
*/
|
||||
inline unsigned getPropertyCount();
|
||||
inline Property *getProperty(unsigned i);
|
||||
|
||||
|
@ -559,8 +564,9 @@ class ObjectGroup : public gc::TenuredCell
|
|||
return Addendum_OriginalUnboxedGroup << OBJECT_FLAG_ADDENDUM_SHIFT;
|
||||
}
|
||||
|
||||
private:
|
||||
inline uint32_t basePropertyCount();
|
||||
|
||||
private:
|
||||
inline void setBasePropertyCount(uint32_t count);
|
||||
|
||||
static void staticAsserts() {
|
||||
|
|
|
@ -480,8 +480,23 @@
|
|||
* Stack: callee, this, args => rval
|
||||
*/ \
|
||||
macro(JSOP_STRICTSPREADEVAL, 50, "strict-spreadeval", NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET|JOF_CHECKSTRICT) \
|
||||
macro(JSOP_UNUSED51, 51, "unused51", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED52, 52, "unused52", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
/*
|
||||
* Writes the [[Prototype]] objects for both a class and its .prototype to
|
||||
* the stack, given the result of a heritage expression.
|
||||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands:
|
||||
* Stack: heritage => objProto, funcProto
|
||||
*/ \
|
||||
macro(JSOP_CLASSHERITAGE, 51, "classheritage", NULL, 1, 1, 2, JOF_BYTE) \
|
||||
/*
|
||||
* Pushes a clone of a function with a given [[Prototype]] onto the stack.
|
||||
* Category: Statements
|
||||
* Type: Function
|
||||
* Operands: uint32_t funcIndex
|
||||
* Stack: proto => obj
|
||||
*/ \
|
||||
macro(JSOP_FUNWITHPROTO, 52, "funwithproto", NULL, 5, 1, 1, JOF_OBJECT) \
|
||||
\
|
||||
/*
|
||||
* Pops the top of stack value, pushes property of it onto the stack.
|
||||
|
@ -753,8 +768,15 @@
|
|||
* nuses: (argc+2)
|
||||
*/ \
|
||||
macro(JSOP_NEW, 82, js_new_str, NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \
|
||||
\
|
||||
macro(JSOP_UNUSED83, 83, "unused83", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
/*
|
||||
* Pushes newly created object onto the stack with provided [[Prototype]].
|
||||
*
|
||||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands:
|
||||
* Stack: proto => obj
|
||||
*/ \
|
||||
macro(JSOP_OBJWITHPROTO, 83, "objwithproto", NULL, 1, 1, 1, JOF_BYTE) \
|
||||
\
|
||||
/*
|
||||
* Fast get op for function arguments and local variables.
|
||||
|
|
|
@ -611,8 +611,8 @@ TypeScript::MonitorAssign(JSContext *cx, HandleObject obj, jsid id)
|
|||
// But if we don't have too many properties yet, don't do anything. The
|
||||
// idea here is that normal object initialization should not trigger
|
||||
// deoptimization in most cases, while actual usage as a hashmap should.
|
||||
ObjectGroup* group = obj->group();
|
||||
if (group->getPropertyCount() < 128)
|
||||
ObjectGroup *group = obj->group();
|
||||
if (group->basePropertyCount() < 128)
|
||||
return;
|
||||
MarkObjectGroupUnknownProperties(cx, group);
|
||||
}
|
||||
|
|
|
@ -29,11 +29,11 @@ namespace js {
|
|||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 252;
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 255;
|
||||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
static_assert(JSErr_Limit == 384,
|
||||
static_assert(JSErr_Limit == 385,
|
||||
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
||||
"removed MSG_DEFs from js.msg, you should increment "
|
||||
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
||||
|
|
|
@ -117,3 +117,4 @@ HTTP(..) != subsuper-fallback-omega.html subsuper-fallback-omega-notref.html
|
|||
HTTP(..) == subsuper-nofallback.html subsuper-nofallback-ref1.html
|
||||
random-if(cocoaWidget) HTTP(..) == subsuper-nofallback.html subsuper-nofallback-ref2.html # bug 1139269
|
||||
HTTP(..) != subsuper-nofallback.html subsuper-nofallback-notref.html
|
||||
HTTP(..) == subsuper-fallback-size.html subsuper-fallback-size-ref.html
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>font-variant-position fallback</title>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
|
||||
/* sups: 0-9 + - ( ) = subs: 0-9 + - ( ) */
|
||||
@font-face {
|
||||
font-family: subsuper;
|
||||
src: url(../fonts/subsuper.woff); /* FiraSans with blank omega */
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 20px;
|
||||
font-family: subsuper, sans-serif;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 10px;
|
||||
font-size: 50px;
|
||||
line-height: 2;
|
||||
width: -moz-fit-content;
|
||||
background: black;
|
||||
}
|
||||
h4 { font-weight: normal; }
|
||||
span { font-size: 0.667em; } /* see NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h4>The black bars should NOT be the same length</h4>
|
||||
<p> XXXXXXXXXX </p>
|
||||
<p> X<span>XXXXXXXX</span>X </p>
|
||||
<p> X<span>XXXXXXXX</span>X </p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>font-variant-position fallback</title>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
|
||||
/* sups: 0-9 + - ( ) = subs: 0-9 + - ( ) */
|
||||
@font-face {
|
||||
font-family: subsuper;
|
||||
src: url(../fonts/subsuper.woff); /* FiraSans with blank omega */
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 20px;
|
||||
font-family: subsuper, sans-serif;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 10px;
|
||||
font-size: 50px;
|
||||
line-height: 2;
|
||||
width: -moz-fit-content;
|
||||
background: black;
|
||||
}
|
||||
h4 { font-weight: normal; }
|
||||
span.sub { font-variant-position: sub; }
|
||||
span.super { font-variant-position: super; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h4>The black bars should NOT be the same length</h4>
|
||||
<p> XXXXXXXXXX </p>
|
||||
<p> X<span class=sub>XXXXXXXX</span>X </p>
|
||||
<p> X<span class=super>XXXXXXXX</span>X </p>
|
||||
</body>
|
||||
</html>
|
|
@ -142,7 +142,7 @@ CSSUnprefixingService.prototype = {
|
|||
let newRightHalf = aRightHalfOfDecl;
|
||||
for (let strToReplace in propInfo.stringMap) {
|
||||
let replacement = propInfo.stringMap[strToReplace];
|
||||
newRightHalf = newRightHalf.replace(strToReplace, replacement, "g");
|
||||
newRightHalf = newRightHalf.split(strToReplace).join(replacement);
|
||||
}
|
||||
aUnprefixedDecl.value = propInfo.unprefixedPropName + ":" + newRightHalf;
|
||||
|
||||
|
|
|
@ -17,7 +17,11 @@
|
|||
// mozalloc.cpp is part of the same library as mozmemory, thus MOZ_MEMORY_IMPL
|
||||
// is needed.
|
||||
#define MOZ_MEMORY_IMPL
|
||||
#include "mozmemory.h"
|
||||
#include "mozmemory_wrap.h"
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
#include <malloc/malloc.h> // for malloc_size
|
||||
#endif
|
||||
|
||||
// See mozmemory_wrap.h for more details. This file is part of libmozglue, so
|
||||
// it needs to use _impl suffixes. However, with libmozglue growing, this is
|
||||
|
|
|
@ -595,12 +595,6 @@ bin/libfreebl_32int64_3.so
|
|||
@BINPATH@/crashreporter-override.ini
|
||||
#endif
|
||||
|
||||
; [Extensions]
|
||||
;
|
||||
#ifdef MOZ_ENABLE_GNOMEVFS
|
||||
bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
|
||||
#endif
|
||||
|
||||
[mobile]
|
||||
@BINPATH@/chrome/icons/
|
||||
@BINPATH@/chrome/chrome@JAREXT@
|
||||
|
|
|
@ -161,11 +161,14 @@ class TreeMetadataEmitter(LoggingMixin):
|
|||
else:
|
||||
raise Exception('Unhandled output type: %s' % type(out))
|
||||
|
||||
start = time.time()
|
||||
objs = list(self._emit_libs_derived(contexts))
|
||||
emitter_time += time.time() - start
|
||||
# Don't emit Linkable objects when COMPILE_ENVIRONMENT is explicitely
|
||||
# set to a value meaning false (usually '').
|
||||
if self.config.substs.get('COMPILE_ENVIRONMENT', True):
|
||||
start = time.time()
|
||||
objs = list(self._emit_libs_derived(contexts))
|
||||
emitter_time += time.time() - start
|
||||
|
||||
for o in emit_objs(objs): yield o
|
||||
for o in emit_objs(objs): yield o
|
||||
|
||||
yield ReaderSummary(file_count, sandbox_execution_time, emitter_time)
|
||||
|
||||
|
|
|
@ -539,7 +539,7 @@ class BaseMarionetteTestRunner(object):
|
|||
self.result_callbacks = result_callbacks if result_callbacks is not None else []
|
||||
self._adb_host = adb_host
|
||||
self._adb_port = adb_port
|
||||
self.prefs = prefs
|
||||
self.prefs = prefs or {}
|
||||
|
||||
def gather_debug(test, status):
|
||||
rv = {}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"repo": "https://hg.mozilla.org/build/mozharness",
|
||||
"revision": "0230a275037f"
|
||||
"revision": "8dc154c5a136"
|
||||
}
|
||||
|
|
|
@ -181,41 +181,24 @@ nsBaseWidget::Shutdown()
|
|||
mShutdownObserver = nullptr;
|
||||
}
|
||||
|
||||
static void DeferredDestroyCompositor(nsRefPtr<CompositorParent> aCompositorParent,
|
||||
nsRefPtr<CompositorChild> aCompositorChild)
|
||||
{
|
||||
// Bug 848949 needs to be fixed before
|
||||
// we can close the channel properly
|
||||
//aCompositorChild->Close();
|
||||
}
|
||||
|
||||
void nsBaseWidget::DestroyCompositor()
|
||||
{
|
||||
if (mCompositorChild) {
|
||||
nsRefPtr<CompositorChild> compositorChild = mCompositorChild.forget();
|
||||
nsRefPtr<CompositorParent> compositorParent = mCompositorParent.forget();
|
||||
|
||||
compositorChild->SendWillStop();
|
||||
// New LayerManager, CompositorParent and CompositorChild might be created
|
||||
// as a result of internal GetLayerManager() call.
|
||||
compositorChild->Destroy();
|
||||
|
||||
// The call just made to SendWillStop can result in IPC from the
|
||||
// CompositorParent to the CompositorChild (e.g. caused by the destruction
|
||||
// of shared memory). We need to ensure this gets processed by the
|
||||
// CompositorChild before it gets destroyed. It suffices to ensure that
|
||||
// events already in the MessageLoop get processed before the
|
||||
// CompositorChild is destroyed, so we add a task to the MessageLoop to
|
||||
// handle compositor desctruction.
|
||||
|
||||
// The DefferedDestroyCompositor task takes ownership of compositorParent and
|
||||
// will release them when it runs.
|
||||
MessageLoop::current()->PostTask(FROM_HERE,
|
||||
NewRunnableFunction(DeferredDestroyCompositor, compositorParent,
|
||||
compositorChild));
|
||||
mCompositorChild->Destroy();
|
||||
mCompositorChild = nullptr;
|
||||
mCompositorParent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void nsBaseWidget::DestroyLayerManager()
|
||||
{
|
||||
if (mLayerManager) {
|
||||
mLayerManager->Destroy();
|
||||
mLayerManager = nullptr;
|
||||
}
|
||||
DestroyCompositor();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
// nsBaseWidget destructor
|
||||
|
@ -228,11 +211,6 @@ nsBaseWidget::~nsBaseWidget()
|
|||
static_cast<BasicLayerManager*>(mLayerManager.get())->ClearRetainerWidget();
|
||||
}
|
||||
|
||||
if (mLayerManager) {
|
||||
mLayerManager->Destroy();
|
||||
mLayerManager = nullptr;
|
||||
}
|
||||
|
||||
if (mShutdownObserver) {
|
||||
// If the shutdown observer is currently processing observers,
|
||||
// then UnregisterShutdownObserver won't stop our Observer
|
||||
|
@ -242,7 +220,7 @@ nsBaseWidget::~nsBaseWidget()
|
|||
nsContentUtils::UnregisterShutdownObserver(mShutdownObserver);
|
||||
}
|
||||
|
||||
DestroyCompositor();
|
||||
DestroyLayerManager();
|
||||
|
||||
#ifdef NOISY_WIDGET_LEAKS
|
||||
gNumWidgets--;
|
||||
|
@ -1081,8 +1059,12 @@ void nsBaseWidget::CreateCompositor(int aWidth, int aHeight)
|
|||
MOZ_ASSERT(gfxPlatform::UsesOffMainThreadCompositing(),
|
||||
"This function assumes OMTC");
|
||||
|
||||
MOZ_ASSERT(!mCompositorParent,
|
||||
"Should have properly cleaned up the previous CompositorParent beforehand");
|
||||
MOZ_ASSERT(!mCompositorParent && !mCompositorChild,
|
||||
"Should have properly cleaned up the previous PCompositor pair beforehand");
|
||||
|
||||
if (mCompositorChild) {
|
||||
mCompositorChild->Destroy();
|
||||
}
|
||||
|
||||
// Recreating this is tricky, as we may still have an old and we need
|
||||
// to make sure it's properly destroyed by calling DestroyCompositor!
|
||||
|
@ -1095,11 +1077,9 @@ void nsBaseWidget::CreateCompositor(int aWidth, int aHeight)
|
|||
|
||||
CreateCompositorVsyncDispatcher();
|
||||
mCompositorParent = NewCompositorParent(aWidth, aHeight);
|
||||
MessageChannel *parentChannel = mCompositorParent->GetIPCChannel();
|
||||
nsRefPtr<ClientLayerManager> lm = new ClientLayerManager(this);
|
||||
MessageLoop *childMessageLoop = CompositorParent::CompositorLoop();
|
||||
mCompositorChild = new CompositorChild(lm);
|
||||
mCompositorChild->Open(parentChannel, childMessageLoop, ipc::ChildSide);
|
||||
mCompositorChild->OpenSameProcess(mCompositorParent);
|
||||
|
||||
if (gfxPrefs::AsyncPanZoomEnabled() &&
|
||||
(WindowType() == eWindowType_toplevel || WindowType() == eWindowType_child)) {
|
||||
|
@ -1128,26 +1108,20 @@ void nsBaseWidget::CreateCompositor(int aWidth, int aHeight)
|
|||
backendHints, 0, &textureFactoryIdentifier, &success);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
ShadowLayerForwarder* lf = lm->AsShadowForwarder();
|
||||
if (!lf) {
|
||||
lm = nullptr;
|
||||
mCompositorChild = nullptr;
|
||||
return;
|
||||
}
|
||||
lf->SetShadowManager(shadowManager);
|
||||
lf->IdentifyTextureHost(textureFactoryIdentifier);
|
||||
ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier);
|
||||
WindowUsesOMTC();
|
||||
ShadowLayerForwarder* lf = lm->AsShadowForwarder();
|
||||
|
||||
mLayerManager = lm.forget();
|
||||
if (!success || !lf) {
|
||||
NS_WARNING("Failed to create an OMT compositor.");
|
||||
DestroyCompositor();
|
||||
return;
|
||||
}
|
||||
|
||||
NS_WARNING("Failed to create an OMT compositor.");
|
||||
DestroyCompositor();
|
||||
// Compositor child had the only reference to LayerManager and will have
|
||||
// deallocated it when being freed.
|
||||
lf->SetShadowManager(shadowManager);
|
||||
lf->IdentifyTextureHost(textureFactoryIdentifier);
|
||||
ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier);
|
||||
WindowUsesOMTC();
|
||||
|
||||
mLayerManager = lm.forget();
|
||||
}
|
||||
|
||||
bool nsBaseWidget::ShouldUseOffMainThreadCompositing()
|
||||
|
|
|
@ -441,6 +441,7 @@ protected:
|
|||
* reached (This is the case with gtk2 for instance).
|
||||
*/
|
||||
void DestroyCompositor();
|
||||
void DestroyLayerManager();
|
||||
|
||||
nsIWidgetListener* mWidgetListener;
|
||||
nsIWidgetListener* mAttachedWidgetListener;
|
||||
|
|
|
@ -671,11 +671,7 @@ NS_METHOD nsWindow::Destroy()
|
|||
* On windows the LayerManagerOGL destructor wants the widget to be around for
|
||||
* cleanup. It also would like to have the HWND intact, so we nullptr it here.
|
||||
*/
|
||||
if (mLayerManager) {
|
||||
mLayerManager->Destroy();
|
||||
}
|
||||
mLayerManager = nullptr;
|
||||
DestroyCompositor();
|
||||
DestroyLayerManager();
|
||||
|
||||
/* We should clear our cached resources now and not wait for the GC to
|
||||
* delete the nsWindow. */
|
||||
|
@ -6536,10 +6532,7 @@ bool nsWindow::AutoErase(HDC dc)
|
|||
void
|
||||
nsWindow::ClearCompositor(nsWindow* aWindow)
|
||||
{
|
||||
if (aWindow->mLayerManager) {
|
||||
aWindow->mLayerManager = nullptr;
|
||||
aWindow->DestroyCompositor();
|
||||
}
|
||||
aWindow->DestroyLayerManager();
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -90,12 +90,15 @@ NS_OpenAnonymousTemporaryFile(PRFileDesc** aOutFileDesc)
|
|||
}
|
||||
|
||||
if (dom::ContentChild* child = dom::ContentChild::GetSingleton()) {
|
||||
ipc::FileDescriptor fd;
|
||||
DebugOnly<bool> succeeded = child->SendOpenAnonymousTemporaryFile(&fd);
|
||||
// The child process should already have been terminated if the
|
||||
// IPC had failed.
|
||||
MOZ_ASSERT(succeeded);
|
||||
*aOutFileDesc = PR_ImportFile(PROsfd(fd.PlatformHandle()));
|
||||
dom::FileDescOrError fd;
|
||||
child->SendOpenAnonymousTemporaryFile(&fd);
|
||||
if (fd.type() == dom::FileDescOrError::Tnsresult) {
|
||||
nsresult rv = fd.get_nsresult();
|
||||
MOZ_ASSERT(NS_FAILED(rv));
|
||||
return rv;
|
||||
}
|
||||
*aOutFileDesc =
|
||||
PR_ImportFile(PROsfd(fd.get_FileDescriptor().PlatformHandle()));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче