Merge m-c to b2g-inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-03-11 16:03:39 -04:00
Родитель 05d8e81730 658021c2d9
Коммит 97acbd9776
78 изменённых файлов: 1306 добавлений и 1678 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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 = &REGS.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>&nbsp;XXXXXXXXXX&nbsp;</p>
<p>&nbsp;X<span>XXXXXXXX</span>X&nbsp;</p>
<p>&nbsp;X<span>XXXXXXXX</span>X&nbsp;</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>&nbsp;XXXXXXXXXX&nbsp;</p>
<p>&nbsp;X<span class=sub>XXXXXXXX</span>X&nbsp;</p>
<p>&nbsp;X<span class=super>XXXXXXXX</span>X&nbsp;</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;
}