diff --git a/accessible/tests/mochitest/relations/a11y.ini b/accessible/tests/mochitest/relations/a11y.ini index 7c5cc25b900..45a746bcace 100644 --- a/accessible/tests/mochitest/relations/a11y.ini +++ b/accessible/tests/mochitest/relations/a11y.ini @@ -2,6 +2,7 @@ [test_bindings.xhtml] [test_embeds.xul] +skip-if = (os == "linux" && (debug || asan)) # Bug 845176 [test_general.html] [test_general.xul] [test_tabbrowser.xul] diff --git a/accessible/xpcom/AccEventGen.py b/accessible/xpcom/AccEventGen.py index eb454f574e0..050ab88e8ae 100755 --- a/accessible/xpcom/AccEventGen.py +++ b/accessible/xpcom/AccEventGen.py @@ -209,14 +209,18 @@ def write_cpp(eventname, iface, fd): for a in attributes: writeAttributeGetter(fd, classname, a) -def gen_header_file(fd, conf_file): +def get_conf(conf_file): conf = Configuration(conf_file) - inc_dir = mozpath.join(buildconfig.topobjdir, 'dist', 'idl') + inc_dir = [ + mozpath.join(buildconfig.topsrcdir, 'accessible', 'interfaces'), + mozpath.join(buildconfig.topsrcdir, 'xpcom', 'base'), + ] + return conf, inc_dir - return print_header_file(fd, conf, [inc_dir]) +def gen_header_file(fd, conf_file): + conf, inc_dir = get_conf(conf_file) + return print_header_file(fd, conf, inc_dir) def gen_cpp_file(fd, conf_file): - conf = Configuration(conf_file) - inc_dir = mozpath.join(buildconfig.topobjdir, 'dist', 'idl') - - return print_cpp_file(fd, conf, [inc_dir]) + conf, inc_dir = get_conf(conf_file) + return print_cpp_file(fd, conf, inc_dir) diff --git a/b2g/components/LogCapture.jsm b/b2g/components/LogCapture.jsm index 2170d1f597f..803028d5749 100644 --- a/b2g/components/LogCapture.jsm +++ b/b2g/components/LogCapture.jsm @@ -10,6 +10,7 @@ const Cu = Components.utils; const Ci = Components.interfaces; const Cc = Components.classes; +Cu.importGlobalProperties(['FileReader']); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm"); @@ -198,9 +199,7 @@ var LogCapture = { try { this.ensureLoaded(); - let fr = Cc["@mozilla.org/files/filereader;1"] - .createInstance(Ci.nsIDOMFileReader); - + let fr = new FileReader(); fr.onload = function(evt) { deferred.resolve(new Uint8Array(evt.target.result)); }; diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 8ebf6177dcb..979b55595e5 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1279,6 +1279,8 @@ pref("services.sync.prefs.sync.lightweightThemes.selectedThemeID", true); pref("services.sync.prefs.sync.lightweightThemes.usedThemes", true); pref("services.sync.prefs.sync.network.cookie.cookieBehavior", true); pref("services.sync.prefs.sync.network.cookie.lifetimePolicy", true); +pref("services.sync.prefs.sync.network.cookie.lifetime.days", true); +pref("services.sync.prefs.sync.network.cookie.thirdparty.sessionOnly", true); pref("services.sync.prefs.sync.permissions.default.image", true); pref("services.sync.prefs.sync.pref.advanced.images.disable_button.view_image", true); pref("services.sync.prefs.sync.pref.advanced.javascript.disable_button.advanced", true); diff --git a/browser/components/migration/MSMigrationUtils.jsm b/browser/components/migration/MSMigrationUtils.jsm index 3ac5f7935c4..7c30348166b 100644 --- a/browser/components/migration/MSMigrationUtils.jsm +++ b/browser/components/migration/MSMigrationUtils.jsm @@ -16,6 +16,8 @@ Cu.import("resource:///modules/MigrationUtils.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/LoginHelper.jsm"); +Cu.importGlobalProperties(['FileReader']); + XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry", @@ -545,8 +547,7 @@ Cookies.prototype = { }, _readCookieFile(aFile, aCallback) { - let fileReader = Cc["@mozilla.org/files/filereader;1"]. - createInstance(Ci.nsIDOMFileReader); + let fileReader = new FileReader(); let onLoadEnd = () => { fileReader.removeEventListener("loadend", onLoadEnd, false); diff --git a/browser/config/mozconfigs/macosx-universal/nightly b/browser/config/mozconfigs/macosx-universal/nightly index 423a90a0f75..5832a3035f9 100644 --- a/browser/config/mozconfigs/macosx-universal/nightly +++ b/browser/config/mozconfigs/macosx-universal/nightly @@ -12,5 +12,6 @@ fi ac_add_options --with-branding=browser/branding/nightly +. "$topsrcdir/build/macosx/mozconfig.rust" . "$topsrcdir/build/mozconfig.common.override" . "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/mozconfigs/whitelist b/browser/config/mozconfigs/whitelist index d3389b9f243..919bb1610a9 100644 --- a/browser/config/mozconfigs/whitelist +++ b/browser/config/mozconfigs/whitelist @@ -53,6 +53,7 @@ whitelist['nightly']['macosx-universal'] += [ 'ac_add_options --disable-install-strip', 'ac_add_options --enable-instruments', 'ac_add_options --enable-dtrace', + '. "$topsrcdir/build/macosx/mozconfig.rust"', ] whitelist['nightly']['win32'] += [ diff --git a/browser/config/tooltool-manifests/macosx64/releng.manifest b/browser/config/tooltool-manifests/macosx64/releng.manifest index 197d348de47..c34baf77810 100644 --- a/browser/config/tooltool-manifests/macosx64/releng.manifest +++ b/browser/config/tooltool-manifests/macosx64/releng.manifest @@ -10,17 +10,17 @@ "unpack": true }, { +"size": 128301120, +"digest": "d2d71103a6cec84b150b8f08bfef2682aa713c2d6eadce584f79836ef27ba4380ffb444165d999b79605f18ad165641a7a8cc0e04a201675ad5f655a59adbda9", +"algorithm": "sha512", +"filename": "rustc.tar.bz2", +"unpack": true +}, +{ "size": 167175, "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", "algorithm": "sha512", "filename": "sccache.tar.bz2", "unpack": true -}, -{ -"algorithm": "sha512", -"filename": "rustc.tar.bz2", -"unpack": true, -"digest": "28e0d27846cfa6fac5be2df4debb469b6f488d45357d21f2bec14c7c2b6abe5713965497961e7ac10120d3687751ccced95b9498cc54c529cbdfcd59d51a67ec", -"size": 84116661 } ] diff --git a/build/macosx/mozconfig.rust b/build/macosx/mozconfig.rust index bbd21cce976..92d9a608690 100644 --- a/build/macosx/mozconfig.rust +++ b/build/macosx/mozconfig.rust @@ -2,6 +2,5 @@ if test `uname -s` != Linux; then RUSTC="$topsrcdir/rustc/bin/rustc" -mk_add_options "export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$topsrcdir/rustc/lib" ac_add_options --enable-rust fi diff --git a/config/baseconfig.mk b/config/baseconfig.mk index ee10b710d4b..86920aadedf 100644 --- a/config/baseconfig.mk +++ b/config/baseconfig.mk @@ -41,8 +41,6 @@ endif endif endif # WINNT -include_deps = $(eval $(if $(2),,-)include $(1)) - ifndef INCLUDED_AUTOCONF_MK default:: else diff --git a/config/rules.mk b/config/rules.mk index 49e636c24aa..938c956720d 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -1296,7 +1296,7 @@ ifneq (,$(filter-out all chrome default export realchrome clean clobber clobber_ MDDEPEND_FILES := $(strip $(wildcard $(addprefix $(MDDEPDIR)/,$(addsuffix .pp,$(notdir $(sort $(OBJS) $(PROGOBJS) $(HOST_OBJS) $(HOST_PROGOBJS))))))) ifneq (,$(MDDEPEND_FILES)) -$(call include_deps,$(MDDEPEND_FILES)) +-include $(MDDEPEND_FILES) endif endif @@ -1304,7 +1304,7 @@ endif MDDEPEND_FILES := $(strip $(wildcard $(addprefix $(MDDEPDIR)/,$(EXTRA_MDDEPEND_FILES)))) ifneq (,$(MDDEPEND_FILES)) -$(call include_deps,$(MDDEPEND_FILES)) +-include $(MDDEPEND_FILES) endif ############################################################################# @@ -1483,7 +1483,7 @@ $(foreach file,$(PP_TARGETS_ALL_RESULTS), \ MDDEPEND_FILES := $(strip $(wildcard $(addprefix $(MDDEPDIR)/,$(addsuffix .pp,$(notdir $(PP_TARGETS_ALL_RESULTS)))))) ifneq (,$(MDDEPEND_FILES)) -$(call include_deps,$(MDDEPEND_FILES)) +-include $(MDDEPEND_FILES) endif endif diff --git a/configure.in b/configure.in index 9875916e577..1bc095c8c80 100644 --- a/configure.in +++ b/configure.in @@ -446,44 +446,6 @@ esac AC_SUBST(MACOSX_DEPLOYMENT_TARGET) -dnl ======================================================== -dnl Special MacOS X checks -dnl ======================================================== - -if test -n "$MACOSX_DEPLOYMENT_TARGET" -a -n "$MOZ_RUST"; then - AC_MSG_CHECKING([rustc compatibility with MacOS X]) - # Stock rustc doesn't support MacOS X 10.6 or earlier. - # https://github.com/rust-lang/rust/issues/25342 - _MACOSX_TARGET_MINOR=`echo "$MACOSX_DEPLOYMENT_TARGET" | cut -d. -f2` - if test "$_MACOSX_TARGET_MINOR" -lt 7; then - dnl Test C linkage against rust code to see if the rust - dnl toolchain output is compatible. - cat > conftest.rs < u8 { 42 } -EOF - ac_try="$RUSTC --crate-type staticlib -o conftest.a conftest.rs >/dev/null" - AC_TRY_EVAL(ac_try) - save_LDFLAGS=$LDFLAGS - LDFLAGS="$LDFLAGS conftest.a -lpthread -lm" - AC_TRY_LINK_FUNC([rusty_answer], [ - AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok with this rustc]) - ], [ - AC_MSG_RESULT([cannot link on $MACOSX_DEPLOYMENT_TARGET]) - MOZ_RUST= - ]) - LDFLAGS=$save_LDFLAGS - rm -rf conftest* - else - AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok]) - fi - if test -z "$MOZ_RUST"; then - AC_MSG_ERROR([rustc does not support MacOS X $MACOSX_DEPLOYMENT_TARGET - Add 'ac_add_options --enable-macos-target=10.7' (or later) - to mozconfig, disable Rust support, or use an alternate toolchain.]) - fi -fi - AC_PROG_CPP AC_PROG_CXXCPP @@ -1681,6 +1643,51 @@ AC_SUBST(HAVE_64BIT_BUILD) AC_LANG_RESTORE fi # COMPILE_ENVIRONMENT +dnl ======================================================== +dnl Special rust checks +dnl ======================================================== + +if test -n "$MACOSX_DEPLOYMENT_TARGET" -a -n "$MOZ_RUST"; then + AC_MSG_CHECKING([if we're targeting 32-bit]) + if test -z "$HAVE_64BIT_BUILD"; then + RUSTC="$RUSTC --target=i686-apple-darwin" + AC_MSG_RESULT([using $RUSTC]) + else + AC_MSG_RESULT([no]) + fi + AC_MSG_CHECKING([rustc compatibility with MacOS X]) + # Stock rustc doesn't support MacOS X 10.6 or earlier. + # https://github.com/rust-lang/rust/issues/25342 + _MACOSX_TARGET_MINOR=`echo "$MACOSX_DEPLOYMENT_TARGET" | cut -d. -f2` + if test "$_MACOSX_TARGET_MINOR" -lt 7; then + dnl Test C linkage against rust code to see if the rust + dnl toolchain output is compatible. + cat > conftest.rs < u8 { 42 } +EOF + ac_try="$RUSTC --crate-type staticlib -o conftest.a conftest.rs >/dev/null" + AC_TRY_EVAL(ac_try) + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS conftest.a -lpthread -lm" + AC_TRY_LINK_FUNC([rusty_answer], [ + AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok with this rustc]) + ], [ + AC_MSG_RESULT([cannot link for $MACOSX_DEPLOYMENT_TARGET]) + MOZ_RUST= + ]) + LDFLAGS=$save_LDFLAGS + rm -rf conftest* + else + AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok]) + fi + if test -z "$MOZ_RUST"; then + AC_MSG_ERROR([rustc does not support MacOS X $MACOSX_DEPLOYMENT_TARGET + Add 'ac_add_options --enable-macos-target=10.7' (or later) + to mozconfig, disable Rust support, or use an alternate toolchain.]) + fi +fi + dnl ======================================================== dnl = Use profiling compile flags dnl ======================================================== diff --git a/devtools/server/actors/device.js b/devtools/server/actors/device.js index 6dbc4aa1719..92e25f78955 100644 --- a/devtools/server/actors/device.js +++ b/devtools/server/actors/device.js @@ -11,6 +11,7 @@ const {LongStringActor} = require("devtools/server/actors/string"); const {DebuggerServer} = require("devtools/server/main"); const {getSystemInfo, getSetting} = require("devtools/shared/system"); +Cu.importGlobalProperties(["FileReader"]); Cu.import("resource://gre/modules/PermissionsTable.jsm") var DeviceActor = exports.DeviceActor = protocol.ActorClass({ @@ -25,7 +26,6 @@ var DeviceActor = exports.DeviceActor = protocol.ActorClass({ getWallpaper: method(function() { let deferred = promise.defer(); getSetting("wallpaper.image").then((blob) => { - let FileReader = CC("@mozilla.org/files/filereader;1"); let reader = new FileReader(); let conn = this.conn; reader.addEventListener("load", function() { diff --git a/devtools/server/actors/webapps.js b/devtools/server/actors/webapps.js index a17e31ce254..8d6b53e0ffc 100644 --- a/devtools/server/actors/webapps.js +++ b/devtools/server/actors/webapps.js @@ -11,6 +11,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/UserCustomizations.jsm"); +Cu.importGlobalProperties(["FileReader"]); var promise = require("promise"); var DevToolsUtils = require("devtools/shared/DevToolsUtils"); @@ -851,8 +852,7 @@ WebappsActor.prototype = { } // Convert the blog to a base64 encoded data URI - let reader = Cc["@mozilla.org/files/filereader;1"] - .createInstance(Ci.nsIDOMFileReader); + let reader = new FileReader(); reader.onload = function () { deferred.resolve({ url: reader.result diff --git a/dom/animation/EffectCompositor.cpp b/dom/animation/EffectCompositor.cpp index ab15ac0e679..34e4a6d22c9 100644 --- a/dom/animation/EffectCompositor.cpp +++ b/dom/animation/EffectCompositor.cpp @@ -17,11 +17,28 @@ using mozilla::dom::KeyframeEffectReadOnly; namespace mozilla { -/* static */ nsTArray> -EffectCompositor::GetAnimationsForCompositor(const nsIFrame* aFrame, - nsCSSProperty aProperty) +// Helper function to factor out the common logic from +// GetAnimationsForCompositor and HasAnimationsForCompositor. +// +// Takes an optional array to fill with eligible animations. +// +// Returns true if there are eligible animations, false otherwise. +bool +FindAnimationsForCompositor(const nsIFrame* aFrame, + nsCSSProperty aProperty, + nsTArray>* aMatches /*out*/) { - nsTArray> result; + MOZ_ASSERT(!aMatches || aMatches->IsEmpty(), + "Matches array, if provided, should be empty"); + + EffectSet* effects = EffectSet::GetEffectSet(aFrame); + if (!effects || effects->IsEmpty()) { + return false; + } + + if (aFrame->RefusedAsyncAnimation()) { + return false; + } if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) { if (nsLayoutUtils::IsAnimationLoggingEnabled()) { @@ -30,18 +47,10 @@ EffectCompositor::GetAnimationsForCompositor(const nsIFrame* aFrame, "disabled"); AnimationUtils::LogAsyncAnimationFailure(message); } - return result; - } - - if (aFrame->RefusedAsyncAnimation()) { - return result; - } - - EffectSet* effects = EffectSet::GetEffectSet(aFrame); - if (!effects) { - return result; + return false; } + bool foundSome = false; for (KeyframeEffectReadOnly* effect : *effects) { MOZ_ASSERT(effect && effect->GetAnimation()); Animation* animation = effect->GetAnimation(); @@ -51,17 +60,47 @@ EffectCompositor::GetAnimationsForCompositor(const nsIFrame* aFrame, } if (effect->ShouldBlockCompositorAnimations(aFrame)) { - result.Clear(); - return result; + if (aMatches) { + aMatches->Clear(); + } + return false; } if (!effect->HasAnimationOfProperty(aProperty)) { continue; } - result.AppendElement(animation); + if (aMatches) { + aMatches->AppendElement(animation); + } + foundSome = true; } + MOZ_ASSERT(!foundSome || !aMatches || !aMatches->IsEmpty(), + "If return value is true, matches array should be non-empty"); + return foundSome; +} + +/* static */ bool +EffectCompositor::HasAnimationsForCompositor(const nsIFrame* aFrame, + nsCSSProperty aProperty) +{ + return FindAnimationsForCompositor(aFrame, aProperty, nullptr); +} + +/* static */ nsTArray> +EffectCompositor::GetAnimationsForCompositor(const nsIFrame* aFrame, + nsCSSProperty aProperty) +{ + nsTArray> result; + +#ifdef DEBUG + bool foundSome = +#endif + FindAnimationsForCompositor(aFrame, aProperty, &result); + MOZ_ASSERT(!foundSome || !result.IsEmpty(), + "If return value is true, matches array should be non-empty"); + return result; } diff --git a/dom/animation/EffectCompositor.h b/dom/animation/EffectCompositor.h index 2c6f3fb9c99..c9a3f3fd87c 100644 --- a/dom/animation/EffectCompositor.h +++ b/dom/animation/EffectCompositor.h @@ -19,6 +19,9 @@ class Animation; class EffectCompositor { public: + static bool HasAnimationsForCompositor(const nsIFrame* aFrame, + nsCSSProperty aProperty); + static nsTArray> GetAnimationsForCompositor(const nsIFrame* aFrame, nsCSSProperty aProperty); diff --git a/dom/animation/EffectSet.h b/dom/animation/EffectSet.h index c8cbe8d9167..5c827d47ba0 100644 --- a/dom/animation/EffectSet.h +++ b/dom/animation/EffectSet.h @@ -118,6 +118,7 @@ public: { return Iterator::EndIterator(mEffects.Iter()); } + bool IsEmpty() const { return mEffects.IsEmpty(); } static nsIAtom** GetEffectSetPropertyAtoms(); diff --git a/dom/animation/KeyframeEffect.cpp b/dom/animation/KeyframeEffect.cpp index 863193280b0..a9fdacda9a1 100644 --- a/dom/animation/KeyframeEffect.cpp +++ b/dom/animation/KeyframeEffect.cpp @@ -1353,6 +1353,8 @@ BuildSegmentsFromValueEntries(nsTArray& aEntries, lastProperty = aEntries[i].mProperty; } + MOZ_ASSERT(animationProperty, "animationProperty should be valid pointer."); + // Now generate the segment. AnimationPropertySegment* segment = animationProperty->mSegments.AppendElement(); diff --git a/dom/apps/ImportExport.jsm b/dom/apps/ImportExport.jsm index cf759de7309..63d78fcdcb4 100644 --- a/dom/apps/ImportExport.jsm +++ b/dom/apps/ImportExport.jsm @@ -15,7 +15,7 @@ Cu.import("resource://gre/modules/Promise.jsm"); Cu.import("resource://gre/modules/Webapps.jsm"); Cu.import("resource://gre/modules/MessageBroadcaster.jsm"); -Cu.importGlobalProperties(['File']); +Cu.importGlobalProperties(['File', 'FileReader']); XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm"); @@ -262,8 +262,7 @@ this.ImportExport = { debug("_writeBlobToTempFile"); let path; return new Promise((aResolve, aReject) => { - let reader = Cc['@mozilla.org/files/filereader;1'] - .createInstance(Ci.nsIDOMFileReader); + let reader = new FileReader(); reader.onloadend = () => { path = OS.Path.join(OS.Constants.Path.tmpDir, "app-blob.zip"); debug("onloadend path=" + path); diff --git a/dom/base/FileIOObject.cpp b/dom/base/FileIOObject.cpp deleted file mode 100644 index 8c0c097b55d..00000000000 --- a/dom/base/FileIOObject.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "FileIOObject.h" -#include "mozilla/dom/File.h" -#include "mozilla/dom/ProgressEvent.h" -#include "mozilla/EventDispatcher.h" -#include "nsComponentManagerUtils.h" -#include "nsError.h" -#include "nsIDOMEvent.h" - -#define ERROR_STR "error" -#define ABORT_STR "abort" -#define PROGRESS_STR "progress" - -namespace mozilla { -namespace dom { - -const uint64_t kUnknownSize = uint64_t(-1); - -NS_IMPL_ADDREF_INHERITED(FileIOObject, DOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(FileIOObject, DOMEventTargetHelper) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileIOObject) - NS_INTERFACE_MAP_ENTRY(nsITimerCallback) - NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback) -NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) - -NS_IMPL_CYCLE_COLLECTION_CLASS(FileIOObject) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileIOObject, - DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressNotifier) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileIOObject, - DOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressNotifier) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mError) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_EVENT_HANDLER(FileIOObject, abort) -NS_IMPL_EVENT_HANDLER(FileIOObject, error) -NS_IMPL_EVENT_HANDLER(FileIOObject, progress) - -FileIOObject::FileIOObject() - : mProgressEventWasDelayed(false), - mTimerIsActive(false), - mReadyState(0), - mTotal(0), mTransferred(0) -{} - -FileIOObject::~FileIOObject() -{} - -void -FileIOObject::StartProgressEventTimer() -{ - if (!mProgressNotifier) { - mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID); - } - if (mProgressNotifier) { - mProgressEventWasDelayed = false; - mTimerIsActive = true; - mProgressNotifier->Cancel(); - mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL, - nsITimer::TYPE_ONE_SHOT); - } -} - -void -FileIOObject::ClearProgressEventTimer() -{ - mProgressEventWasDelayed = false; - mTimerIsActive = false; - if (mProgressNotifier) { - mProgressNotifier->Cancel(); - } -} - -void -FileIOObject::DispatchError(nsresult rv, nsAString& finalEvent) -{ - // Set the status attribute, and dispatch the error event - switch (rv) { - case NS_ERROR_FILE_NOT_FOUND: - mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotFoundError")); - break; - case NS_ERROR_FILE_ACCESS_DENIED: - mError = new DOMError(GetOwner(), NS_LITERAL_STRING("SecurityError")); - break; - default: - mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotReadableError")); - break; - } - - // Dispatch error event to signify load failure - DispatchProgressEvent(NS_LITERAL_STRING(ERROR_STR)); - DispatchProgressEvent(finalEvent); -} - -nsresult -FileIOObject::DispatchProgressEvent(const nsAString& aType) -{ - ProgressEventInit init; - init.mBubbles = false; - init.mCancelable = false; - init.mLoaded = mTransferred; - - if (mTotal != kUnknownSize) { - init.mLengthComputable = true; - init.mTotal = mTotal; - } else { - init.mLengthComputable = false; - init.mTotal = 0; - } - RefPtr event = - ProgressEvent::Constructor(this, aType, init); - event->SetTrusted(true); - - return DispatchDOMEvent(nullptr, event, nullptr, nullptr); -} - -// nsITimerCallback -NS_IMETHODIMP -FileIOObject::Notify(nsITimer* aTimer) -{ - nsresult rv; - mTimerIsActive = false; - - if (mProgressEventWasDelayed) { - rv = DispatchProgressEvent(NS_LITERAL_STRING("progress")); - NS_ENSURE_SUCCESS(rv, rv); - - StartProgressEventTimer(); - } - - return NS_OK; -} - -// InputStreamCallback -NS_IMETHODIMP -FileIOObject::OnInputStreamReady(nsIAsyncInputStream* aStream) -{ - if (mReadyState != 1 || aStream != mAsyncStream) { - return NS_OK; - } - - uint64_t aCount; - nsresult rv = aStream->Available(&aCount); - - if (NS_SUCCEEDED(rv) && aCount) { - rv = DoReadData(aStream, aCount); - } - - if (NS_SUCCEEDED(rv)) { - rv = DoAsyncWait(aStream); - } - - if (NS_FAILED(rv) || !aCount) { - if (rv == NS_BASE_STREAM_CLOSED) { - rv = NS_OK; - } - return OnLoadEnd(rv); - } - - mTransferred += aCount; - - //Notify the timer is the appropriate timeframe has passed - if (mTimerIsActive) { - mProgressEventWasDelayed = true; - } else { - rv = DispatchProgressEvent(NS_LITERAL_STRING(PROGRESS_STR)); - NS_ENSURE_SUCCESS(rv, rv); - - StartProgressEventTimer(); - } - - return NS_OK; -} - -nsresult -FileIOObject::OnLoadEnd(nsresult aStatus) -{ - // Cancel the progress event timer - ClearProgressEventTimer(); - - // FileIOObject must be in DONE stage after an operation - mReadyState = 2; - - nsString successEvent, termEvent; - nsresult rv = DoOnLoadEnd(aStatus, successEvent, termEvent); - NS_ENSURE_SUCCESS(rv, rv); - - // Set the status field as appropriate - if (NS_FAILED(aStatus)) { - DispatchError(aStatus, termEvent); - return NS_OK; - } - - // Dispatch event to signify end of a successful operation - DispatchProgressEvent(successEvent); - DispatchProgressEvent(termEvent); - - return NS_OK; -} - -nsresult -FileIOObject::DoAsyncWait(nsIAsyncInputStream* aStream) -{ - return aStream->AsyncWait(this, - /* aFlags*/ 0, - /* aRequestedCount */ 0, - NS_GetCurrentThread()); -} - -void -FileIOObject::Abort(ErrorResult& aRv) -{ - if (mReadyState != 1) { - // XXX The spec doesn't say this - aRv.Throw(NS_ERROR_DOM_FILE_ABORT_ERR); - return; - } - - ClearProgressEventTimer(); - - mReadyState = 2; // There are DONE constants on multiple interfaces, - // but they all have value 2. - // XXX The spec doesn't say this - mError = new DOMError(GetOwner(), NS_LITERAL_STRING("AbortError")); - - nsString finalEvent; - DoAbort(finalEvent); - - // Dispatch the events - DispatchProgressEvent(NS_LITERAL_STRING(ABORT_STR)); - DispatchProgressEvent(finalEvent); -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/base/FileIOObject.h b/dom/base/FileIOObject.h deleted file mode 100644 index 769f09b0415..00000000000 --- a/dom/base/FileIOObject.h +++ /dev/null @@ -1,103 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef FileIOObject_h__ -#define FileIOObject_h__ - -#include "mozilla/DOMEventTargetHelper.h" -#include "nsIFile.h" -#include "nsITimer.h" -#include "nsCOMPtr.h" -#include "nsIAsyncInputStream.h" - -#include "mozilla/dom/DOMError.h" - -#define NS_PROGRESS_EVENT_INTERVAL 50 - -namespace mozilla { - -class ErrorResult; - -namespace dom { - -extern const uint64_t kUnknownSize; - -// A common base class for FileReader and FileSaver - -class FileIOObject : public DOMEventTargetHelper, - public nsIInputStreamCallback, - public nsITimerCallback -{ -public: - FileIOObject(); - - NS_DECL_ISUPPORTS_INHERITED - - // Common methods - void Abort(ErrorResult& aRv); - uint16_t ReadyState() const - { - return mReadyState; - } - DOMError* GetError() const - { - return mError; - } - - NS_METHOD GetOnabort(JSContext* aCx, JS::MutableHandle aValue); - NS_METHOD SetOnabort(JSContext* aCx, JS::Handle aValue); - NS_METHOD GetOnerror(JSContext* aCx, JS::MutableHandle aValue); - NS_METHOD SetOnerror(JSContext* aCx, JS::Handle aValue); - NS_METHOD GetOnprogress(JSContext* aCx, JS::MutableHandle aValue); - NS_METHOD SetOnprogress(JSContext* aCx, JS::Handle aValue); - - IMPL_EVENT_HANDLER(abort) - IMPL_EVENT_HANDLER(error) - IMPL_EVENT_HANDLER(progress) - - NS_DECL_NSITIMERCALLBACK - - NS_DECL_NSIINPUTSTREAMCALLBACK - - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileIOObject, DOMEventTargetHelper) - -protected: - virtual ~FileIOObject(); - - // Implemented by the derived class to do whatever it needs to do for abort - virtual void DoAbort(nsAString& aEvent) = 0; - - virtual nsresult DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount) = 0; - - virtual nsresult DoOnLoadEnd(nsresult aStatus, nsAString& aSuccessEvent, - nsAString& aTerminationEvent) = 0; - - nsresult OnLoadEnd(nsresult aStatus); - nsresult DoAsyncWait(nsIAsyncInputStream* aStream); - - void StartProgressEventTimer(); - void ClearProgressEventTimer(); - void DispatchError(nsresult rv, nsAString& finalEvent); - nsresult DispatchProgressEvent(const nsAString& aType); - - nsCOMPtr mProgressNotifier; - bool mProgressEventWasDelayed; - bool mTimerIsActive; - - nsCOMPtr mAsyncStream; - - RefPtr mError; - - uint16_t mReadyState; - - uint64_t mTotal; - uint64_t mTransferred; -}; - -} // namespace dom -} // namespace mozilla - -#endif diff --git a/dom/base/nsDOMFileReader.cpp b/dom/base/FileReader.cpp similarity index 52% rename from dom/base/nsDOMFileReader.cpp rename to dom/base/FileReader.cpp index ee9a82e6fbc..10bfea467d3 100644 --- a/dom/base/nsDOMFileReader.cpp +++ b/dom/base/FileReader.cpp @@ -4,7 +4,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "nsDOMFileReader.h" +#include "FileReader.h" #include "nsContentCID.h" #include "nsContentUtils.h" @@ -22,6 +22,7 @@ #include "mozilla/dom/EncodingUtils.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FileReaderBinding.h" +#include "mozilla/dom/ProgressEvent.h" #include "xpcpublic.h" #include "nsDOMJSUtils.h" @@ -30,226 +31,137 @@ #include "nsITransport.h" #include "nsIStreamTransportService.h" -using namespace mozilla; -using namespace mozilla::dom; +namespace mozilla { +namespace dom { +#define ABORT_STR "abort" #define LOAD_STR "load" #define LOADSTART_STR "loadstart" #define LOADEND_STR "loadend" +#define ERROR_STR "error" +#define PROGRESS_STR "progress" + +const uint64_t kUnknownSize = uint64_t(-1); static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID); -NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMFileReader) +NS_IMPL_CYCLE_COLLECTION_CLASS(FileReader) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMFileReader, - FileIOObject) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileReader, + DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBlob) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressNotifier) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMFileReader, - FileIOObject) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileReader, + DOMEventTargetHelper) tmp->mResultArrayBuffer = nullptr; NS_IMPL_CYCLE_COLLECTION_UNLINK(mBlob) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressNotifier) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mError) NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsDOMFileReader, +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(FileReader, DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer) NS_IMPL_CYCLE_COLLECTION_TRACE_END -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMFileReader) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsIDOMFileReader) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileReader) + NS_INTERFACE_MAP_ENTRY(nsITimerCallback) + NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) -NS_INTERFACE_MAP_END_INHERITING(FileIOObject) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) -NS_IMPL_ADDREF_INHERITED(nsDOMFileReader, FileIOObject) -NS_IMPL_RELEASE_INHERITED(nsDOMFileReader, FileIOObject) - -NS_IMPL_EVENT_HANDLER(nsDOMFileReader, load) -NS_IMPL_EVENT_HANDLER(nsDOMFileReader, loadend) -NS_IMPL_EVENT_HANDLER(nsDOMFileReader, loadstart) -NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, abort, FileIOObject) -NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, progress, FileIOObject) -NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, error, FileIOObject) +NS_IMPL_ADDREF_INHERITED(FileReader, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(FileReader, DOMEventTargetHelper) void -nsDOMFileReader::RootResultArrayBuffer() +FileReader::RootResultArrayBuffer() { mozilla::HoldJSObjects(this); } -//nsDOMFileReader constructors/initializers +//FileReader constructors/initializers -nsDOMFileReader::nsDOMFileReader() - : mFileData(nullptr), - mDataLen(0), mDataFormat(FILE_AS_BINARY), - mResultArrayBuffer(nullptr) +FileReader::FileReader(nsPIDOMWindow* aWindow) + : DOMEventTargetHelper(aWindow) + , mFileData(nullptr) + , mDataLen(0) + , mDataFormat(FILE_AS_BINARY) + , mResultArrayBuffer(nullptr) + , mProgressEventWasDelayed(false) + , mTimerIsActive(false) + , mReadyState(EMPTY) + , mTotal(0) + , mTransferred(0) { SetDOMStringToNull(mResult); } -nsDOMFileReader::~nsDOMFileReader() +FileReader::~FileReader() { FreeFileData(); mResultArrayBuffer = nullptr; - mozilla::DropJSObjects(this); + DropJSObjects(this); } - -/** - * This Init method is called from the factory constructor. - */ -nsresult -nsDOMFileReader::Init() +/* static */ already_AddRefed +FileReader::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) { - // Instead of grabbing some random global from the context stack, - // let's use the default one (junk scope) for now. - // We should move away from this Init... - BindToOwner(xpc::NativeGlobal(xpc::PrivilegedJunkScope())); - return NS_OK; -} - -/* static */ already_AddRefed -nsDOMFileReader::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) -{ - RefPtr fileReader = new nsDOMFileReader(); - + // The owner can be null when this object is used by chrome code. nsCOMPtr owner = do_QueryInterface(aGlobal.GetAsSupports()); - if (!owner) { - NS_WARNING("Unexpected owner"); - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; + RefPtr fileReader = new FileReader(owner); + + if (!owner && nsContentUtils::IsCallerChrome()) { + // Instead of grabbing some random global from the context stack, + // let's use the default one (junk scope) for now. + // We should move away from this Init... + fileReader->BindToOwner(xpc::NativeGlobal(xpc::PrivilegedJunkScope())); } - fileReader->BindToOwner(owner); return fileReader.forget(); } // nsIInterfaceRequestor NS_IMETHODIMP -nsDOMFileReader::GetInterface(const nsIID & aIID, void **aResult) +FileReader::GetInterface(const nsIID & aIID, void **aResult) { return QueryInterface(aIID, aResult); } -// nsIDOMFileReader - -NS_IMETHODIMP -nsDOMFileReader::GetReadyState(uint16_t *aReadyState) -{ - *aReadyState = ReadyState(); - return NS_OK; -} - void -nsDOMFileReader::GetResult(JSContext* aCx, JS::MutableHandle aResult, - ErrorResult& aRv) -{ - aRv = GetResult(aCx, aResult); -} - -NS_IMETHODIMP -nsDOMFileReader::GetResult(JSContext* aCx, JS::MutableHandle aResult) +FileReader::GetResult(JSContext* aCx, + JS::MutableHandle aResult, + ErrorResult& aRv) { JS::Rooted result(aCx); + if (mDataFormat == FILE_AS_ARRAYBUFFER) { - if (mReadyState == nsIDOMFileReader::DONE && mResultArrayBuffer) { + if (mReadyState == DONE && mResultArrayBuffer) { result.setObject(*mResultArrayBuffer); } else { result.setNull(); } + if (!JS_WrapValue(aCx, &result)) { - return NS_ERROR_FAILURE; + aRv.Throw(NS_ERROR_FAILURE); + return; } + aResult.set(result); - return NS_OK; + return; } nsString tmpResult = mResult; if (!xpc::StringToJsval(aCx, tmpResult, aResult)) { - return NS_ERROR_FAILURE; + aRv.Throw(NS_ERROR_FAILURE); + return; } - return NS_OK; } -NS_IMETHODIMP -nsDOMFileReader::GetError(nsISupports** aError) -{ - NS_IF_ADDREF(*aError = GetError()); - return NS_OK; -} - -NS_IMETHODIMP -nsDOMFileReader::ReadAsArrayBuffer(nsIDOMBlob* aBlob, JSContext* aCx) -{ - NS_ENSURE_TRUE(aBlob, NS_ERROR_NULL_POINTER); - ErrorResult rv; - RefPtr blob = static_cast(aBlob); - ReadAsArrayBuffer(aCx, *blob, rv); - return rv.StealNSResult(); -} - -NS_IMETHODIMP -nsDOMFileReader::ReadAsBinaryString(nsIDOMBlob* aBlob) -{ - NS_ENSURE_TRUE(aBlob, NS_ERROR_NULL_POINTER); - ErrorResult rv; - RefPtr blob = static_cast(aBlob); - ReadAsBinaryString(*blob, rv); - return rv.StealNSResult(); -} - -NS_IMETHODIMP -nsDOMFileReader::ReadAsText(nsIDOMBlob* aBlob, - const nsAString &aCharset) -{ - NS_ENSURE_TRUE(aBlob, NS_ERROR_NULL_POINTER); - ErrorResult rv; - RefPtr blob = static_cast(aBlob); - ReadAsText(*blob, aCharset, rv); - return rv.StealNSResult(); -} - -NS_IMETHODIMP -nsDOMFileReader::ReadAsDataURL(nsIDOMBlob* aBlob) -{ - NS_ENSURE_TRUE(aBlob, NS_ERROR_NULL_POINTER); - ErrorResult rv; - RefPtr blob = static_cast(aBlob); - ReadAsDataURL(*blob, rv); - return rv.StealNSResult(); -} - -NS_IMETHODIMP -nsDOMFileReader::Abort() -{ - ErrorResult rv; - FileIOObject::Abort(rv); - return rv.StealNSResult(); -} - -/* virtual */ void -nsDOMFileReader::DoAbort(nsAString& aEvent) -{ - // Revert status and result attributes - SetDOMStringToNull(mResult); - mResultArrayBuffer = nullptr; - - mAsyncStream = nullptr; - mBlob = nullptr; - - //Clean up memory buffer - FreeFileData(); - - // Tell the base class which event to dispatch - aEvent = NS_LITERAL_STRING(LOADEND_STR); -} - -static -NS_METHOD +static NS_IMETHODIMP ReadFuncBinaryString(nsIInputStream* in, void* closure, const char* fromRawSegment, @@ -271,9 +183,9 @@ ReadFuncBinaryString(nsIInputStream* in, } nsresult -nsDOMFileReader::DoOnLoadEnd(nsresult aStatus, - nsAString& aSuccessEvent, - nsAString& aTerminationEvent) +FileReader::DoOnLoadEnd(nsresult aStatus, + nsAString& aSuccessEvent, + nsAString& aTerminationEvent) { // Make sure we drop all the objects that could hold files open now. nsCOMPtr stream; @@ -303,7 +215,7 @@ nsDOMFileReader::DoOnLoadEnd(nsresult aStatus, switch (mDataFormat) { case FILE_AS_ARRAYBUFFER: { AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(mozilla::DOMEventTargetHelper::GetParentObject()))) { + if (NS_WARN_IF(!jsapi.Init(DOMEventTargetHelper::GetParentObject()))) { FreeFileData(); return NS_ERROR_FAILURE; } @@ -344,7 +256,7 @@ nsDOMFileReader::DoOnLoadEnd(nsresult aStatus, } nsresult -nsDOMFileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount) +FileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount) { MOZ_ASSERT(aStream); @@ -386,18 +298,21 @@ nsDOMFileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount) // Helper methods void -nsDOMFileReader::ReadFileContent(Blob& aBlob, - const nsAString &aCharset, - eDataFormat aDataFormat, - ErrorResult& aRv) +FileReader::ReadFileContent(Blob& aBlob, + const nsAString &aCharset, + eDataFormat aDataFormat, + ErrorResult& aRv) { //Implicit abort to clear any other activity going on - Abort(); + ErrorResult error; + Abort(error); + error.SuppressException(); + mError = nullptr; SetDOMStringToNull(mResult); mTransferred = 0; mTotal = 0; - mReadyState = nsIDOMFileReader::EMPTY; + mReadyState = EMPTY; FreeFileData(); mBlob = &aBlob; @@ -405,7 +320,6 @@ nsDOMFileReader::ReadFileContent(Blob& aBlob, CopyUTF16toUTF8(aCharset, mCharset); nsresult rv; - nsCOMPtr sts = do_GetService(kStreamTransportServiceCID, &rv); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -420,23 +334,21 @@ nsDOMFileReader::ReadFileContent(Blob& aBlob, } nsCOMPtr transport; - rv = sts->CreateInputTransport(stream, - /* aStartOffset */ 0, - /* aReadLimit */ -1, - /* aCloseWhenDone */ true, - getter_AddRefs(transport)); - if (NS_WARN_IF(NS_FAILED(rv))) { - aRv.Throw(rv); + aRv = sts->CreateInputTransport(stream, + /* aStartOffset */ 0, + /* aReadLimit */ -1, + /* aCloseWhenDone */ true, + getter_AddRefs(transport)); + if (NS_WARN_IF(aRv.Failed())) { return; } nsCOMPtr wrapper; - rv = transport->OpenInputStream(/* aFlags */ 0, - /* aSegmentSize */ 0, - /* aSegmentCount */ 0, - getter_AddRefs(wrapper)); - if (NS_WARN_IF(NS_FAILED(rv))) { - aRv.Throw(rv); + aRv = transport->OpenInputStream(/* aFlags */ 0, + /* aSegmentSize */ 0, + /* aSegmentCount */ 0, + getter_AddRefs(wrapper)); + if (NS_WARN_IF(aRv.Failed())) { return; } @@ -449,14 +361,16 @@ nsDOMFileReader::ReadFileContent(Blob& aBlob, return; } - rv = DoAsyncWait(mAsyncStream); - if (NS_WARN_IF(NS_FAILED(rv))) { - aRv.Throw(rv); + aRv = mAsyncStream->AsyncWait(this, + /* aFlags*/ 0, + /* aRequestedCount */ 0, + NS_GetCurrentThread()); + if (NS_WARN_IF(aRv.Failed())) { return; } //FileReader should be in loading state here - mReadyState = nsIDOMFileReader::LOADING; + mReadyState = LOADING; DispatchProgressEvent(NS_LITERAL_STRING(LOADSTART_STR)); if (mDataFormat == FILE_AS_ARRAYBUFFER) { @@ -469,11 +383,11 @@ nsDOMFileReader::ReadFileContent(Blob& aBlob, } nsresult -nsDOMFileReader::GetAsText(Blob *aBlob, - const nsACString &aCharset, - const char *aFileData, - uint32_t aDataLen, - nsAString& aResult) +FileReader::GetAsText(Blob *aBlob, + const nsACString &aCharset, + const char *aFileData, + uint32_t aDataLen, + nsAString& aResult) { // The BOM sniffing is baked into the "decode" part of the Encoding // Standard, which the File API references. @@ -509,14 +423,14 @@ nsDOMFileReader::GetAsText(Blob *aBlob, } nsresult -nsDOMFileReader::GetAsDataURL(Blob *aBlob, - const char *aFileData, - uint32_t aDataLen, - nsAString& aResult) +FileReader::GetAsDataURL(Blob *aBlob, + const char *aFileData, + uint32_t aDataLen, + nsAString& aResult) { aResult.AssignLiteral("data:"); - nsString contentType; + nsAutoString contentType; aBlob->GetType(contentType); if (!contentType.IsEmpty()) { aResult.Append(contentType); @@ -537,7 +451,197 @@ nsDOMFileReader::GetAsDataURL(Blob *aBlob, } /* virtual */ JSObject* -nsDOMFileReader::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +FileReader::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { return FileReaderBinding::Wrap(aCx, this, aGivenProto); } + +void +FileReader::StartProgressEventTimer() +{ + if (!mProgressNotifier) { + mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID); + } + + if (mProgressNotifier) { + mProgressEventWasDelayed = false; + mTimerIsActive = true; + mProgressNotifier->Cancel(); + mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL, + nsITimer::TYPE_ONE_SHOT); + } +} + +void +FileReader::ClearProgressEventTimer() +{ + mProgressEventWasDelayed = false; + mTimerIsActive = false; + if (mProgressNotifier) { + mProgressNotifier->Cancel(); + } +} + +void +FileReader::DispatchError(nsresult rv, nsAString& finalEvent) +{ + // Set the status attribute, and dispatch the error event + switch (rv) { + case NS_ERROR_FILE_NOT_FOUND: + mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotFoundError")); + break; + case NS_ERROR_FILE_ACCESS_DENIED: + mError = new DOMError(GetOwner(), NS_LITERAL_STRING("SecurityError")); + break; + default: + mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotReadableError")); + break; + } + + // Dispatch error event to signify load failure + DispatchProgressEvent(NS_LITERAL_STRING(ERROR_STR)); + DispatchProgressEvent(finalEvent); +} + +nsresult +FileReader::DispatchProgressEvent(const nsAString& aType) +{ + ProgressEventInit init; + init.mBubbles = false; + init.mCancelable = false; + init.mLoaded = mTransferred; + + if (mTotal != kUnknownSize) { + init.mLengthComputable = true; + init.mTotal = mTotal; + } else { + init.mLengthComputable = false; + init.mTotal = 0; + } + RefPtr event = + ProgressEvent::Constructor(this, aType, init); + event->SetTrusted(true); + + return DispatchDOMEvent(nullptr, event, nullptr, nullptr); +} + +// nsITimerCallback +NS_IMETHODIMP +FileReader::Notify(nsITimer* aTimer) +{ + nsresult rv; + mTimerIsActive = false; + + if (mProgressEventWasDelayed) { + rv = DispatchProgressEvent(NS_LITERAL_STRING("progress")); + NS_ENSURE_SUCCESS(rv, rv); + + StartProgressEventTimer(); + } + + return NS_OK; +} + +// InputStreamCallback +NS_IMETHODIMP +FileReader::OnInputStreamReady(nsIAsyncInputStream* aStream) +{ + if (mReadyState != LOADING || aStream != mAsyncStream) { + return NS_OK; + } + + uint64_t aCount; + nsresult rv = aStream->Available(&aCount); + + if (NS_SUCCEEDED(rv) && aCount) { + rv = DoReadData(aStream, aCount); + } + + if (NS_SUCCEEDED(rv)) { + rv = aStream->AsyncWait(this, + /* aFlags*/ 0, + /* aRequestedCount */ 0, + NS_GetCurrentThread()); + } + + if (NS_FAILED(rv) || !aCount) { + if (rv == NS_BASE_STREAM_CLOSED) { + rv = NS_OK; + } + return OnLoadEnd(rv); + } + + mTransferred += aCount; + + //Notify the timer is the appropriate timeframe has passed + if (mTimerIsActive) { + mProgressEventWasDelayed = true; + } else { + rv = DispatchProgressEvent(NS_LITERAL_STRING(PROGRESS_STR)); + NS_ENSURE_SUCCESS(rv, rv); + + StartProgressEventTimer(); + } + + return NS_OK; +} + +nsresult +FileReader::OnLoadEnd(nsresult aStatus) +{ + // Cancel the progress event timer + ClearProgressEventTimer(); + + // FileReader must be in DONE stage after an operation + mReadyState = DONE; + + nsAutoString successEvent, termEvent; + nsresult rv = DoOnLoadEnd(aStatus, successEvent, termEvent); + NS_ENSURE_SUCCESS(rv, rv); + + // Set the status field as appropriate + if (NS_FAILED(aStatus)) { + DispatchError(aStatus, termEvent); + return NS_OK; + } + + // Dispatch event to signify end of a successful operation + DispatchProgressEvent(successEvent); + DispatchProgressEvent(termEvent); + + return NS_OK; +} + +void +FileReader::Abort(ErrorResult& aRv) +{ + if (mReadyState != LOADING) { + // XXX The spec doesn't say this + aRv.Throw(NS_ERROR_DOM_FILE_ABORT_ERR); + return; + } + + ClearProgressEventTimer(); + + mReadyState = DONE; + + // XXX The spec doesn't say this + mError = new DOMError(GetOwner(), NS_LITERAL_STRING("AbortError")); + + // Revert status and result attributes + SetDOMStringToNull(mResult); + mResultArrayBuffer = nullptr; + + mAsyncStream = nullptr; + mBlob = nullptr; + + //Clean up memory buffer + FreeFileData(); + + // Dispatch the events + DispatchProgressEvent(NS_LITERAL_STRING(ABORT_STR)); + DispatchProgressEvent(NS_LITERAL_STRING(LOADEND_STR)); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/base/nsDOMFileReader.h b/dom/base/FileReader.h similarity index 57% rename from dom/base/nsDOMFileReader.h rename to dom/base/FileReader.h index aa0ccace95c..7026d94a7f1 100644 --- a/dom/base/nsDOMFileReader.h +++ b/dom/base/FileReader.h @@ -4,70 +4,55 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef nsDOMFileReader_h__ -#define nsDOMFileReader_h__ +#ifndef mozilla_dom_FileReader_h +#define mozilla_dom_FileReader_h #include "mozilla/Attributes.h" -#include "nsISupportsUtils.h" -#include "nsString.h" -#include "nsWeakReference.h" -#include "nsIStreamListener.h" -#include "nsIInterfaceRequestor.h" -#include "nsJSUtils.h" -#include "nsTArray.h" -#include "prtime.h" -#include "nsITimer.h" -#include "nsIAsyncInputStream.h" +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/dom/DOMError.h" -#include "nsIDOMFileReader.h" -#include "nsIDOMFileList.h" #include "nsCOMPtr.h" +#include "nsIAsyncInputStream.h" +#include "nsIStreamListener.h" +#include "nsISupportsUtils.h" +#include "nsIInterfaceRequestor.h" +#include "nsITimer.h" +#include "nsJSUtils.h" +#include "nsString.h" +#include "nsTArray.h" +#include "nsWeakReference.h" +#include "prtime.h" -#include "FileIOObject.h" +#define NS_PROGRESS_EVENT_INTERVAL 50 namespace mozilla { namespace dom { + class Blob; -} // namespace dom -} // namespace mozilla -class nsDOMFileReader final : public mozilla::dom::FileIOObject, - public nsIDOMFileReader, - public nsIInterfaceRequestor, - public nsSupportsWeakReference +extern const uint64_t kUnknownSize; + +class FileReader final : public DOMEventTargetHelper, + public nsIInterfaceRequestor, + public nsSupportsWeakReference, + public nsIInputStreamCallback, + public nsITimerCallback { - typedef mozilla::ErrorResult ErrorResult; - typedef mozilla::dom::GlobalObject GlobalObject; - typedef mozilla::dom::Blob Blob; - public: - nsDOMFileReader(); + explicit FileReader(nsPIDOMWindow* aWindow); NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIDOMFILEREADER - - NS_REALLY_FORWARD_NSIDOMEVENTTARGET(mozilla::DOMEventTargetHelper) - - // nsIInterfaceRequestor + NS_DECL_NSITIMERCALLBACK + NS_DECL_NSIINPUTSTREAMCALLBACK NS_DECL_NSIINTERFACEREQUESTOR - // FileIOObject overrides - virtual void DoAbort(nsAString& aEvent) override; + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(FileReader, DOMEventTargetHelper) - virtual nsresult DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount) override; - - virtual nsresult DoOnLoadEnd(nsresult aStatus, nsAString& aSuccessEvent, - nsAString& aTerminationEvent) override; - - nsPIDOMWindow* GetParentObject() const - { - return GetOwner(); - } virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; // WebIDL - static already_AddRefed + static already_AddRefed Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); void ReadAsArrayBuffer(JSContext* aCx, Blob& aBlob, ErrorResult& aRv) { @@ -84,23 +69,26 @@ public: ReadFileContent(aBlob, EmptyString(), FILE_AS_DATAURL, aRv); } - using FileIOObject::Abort; + void Abort(ErrorResult& aRv); - // Inherited ReadyState(). + uint16_t ReadyState() const + { + return static_cast(mReadyState); + } + + DOMError* GetError() const + { + return mError; + } void GetResult(JSContext* aCx, JS::MutableHandle aResult, ErrorResult& aRv); - using FileIOObject::GetError; - IMPL_EVENT_HANDLER(loadstart) - using FileIOObject::GetOnprogress; - using FileIOObject::SetOnprogress; + IMPL_EVENT_HANDLER(progress) IMPL_EVENT_HANDLER(load) - using FileIOObject::GetOnabort; - using FileIOObject::SetOnabort; - using FileIOObject::GetOnerror; - using FileIOObject::SetOnerror; + IMPL_EVENT_HANDLER(abort) + IMPL_EVENT_HANDLER(error) IMPL_EVENT_HANDLER(loadend) void ReadAsBinaryString(Blob& aBlob, ErrorResult& aRv) @@ -108,15 +96,15 @@ public: ReadFileContent(aBlob, EmptyString(), FILE_AS_BINARY, aRv); } +private: + virtual ~FileReader(); - nsresult Init(); - - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsDOMFileReader, - FileIOObject) - void RootResultArrayBuffer(); - -protected: - virtual ~nsDOMFileReader(); + // This must be in sync with dom/webidl/FileReader.webidl + enum eReadyState { + EMPTY = 0, + LOADING = 1, + DONE = 2 + }; enum eDataFormat { FILE_AS_ARRAYBUFFER, @@ -125,6 +113,8 @@ protected: FILE_AS_DATAURL }; + void RootResultArrayBuffer(); + void ReadFileContent(Blob& aBlob, const nsAString &aCharset, eDataFormat aDataFormat, ErrorResult& aRv); @@ -134,7 +124,20 @@ protected: nsresult GetAsDataURL(Blob *aBlob, const char *aFileData, uint32_t aDataLen, nsAString &aResult); - void FreeFileData() { + nsresult OnLoadEnd(nsresult aStatus); + + void StartProgressEventTimer(); + void ClearProgressEventTimer(); + void DispatchError(nsresult rv, nsAString& finalEvent); + nsresult DispatchProgressEvent(const nsAString& aType); + + nsresult DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount); + + nsresult DoOnLoadEnd(nsresult aStatus, nsAString& aSuccessEvent, + nsAString& aTerminationEvent); + + void FreeFileData() + { free(mFileData); mFileData = nullptr; mDataLen = 0; @@ -150,6 +153,22 @@ protected: nsString mResult; JS::Heap mResultArrayBuffer; + + nsCOMPtr mProgressNotifier; + bool mProgressEventWasDelayed; + bool mTimerIsActive; + + nsCOMPtr mAsyncStream; + + RefPtr mError; + + eReadyState mReadyState; + + uint64_t mTotal; + uint64_t mTransferred; }; -#endif +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_FileReader_h diff --git a/dom/base/WindowOrientationObserver.cpp b/dom/base/WindowOrientationObserver.cpp index d7ce628448e..733a961adc3 100644 --- a/dom/base/WindowOrientationObserver.cpp +++ b/dom/base/WindowOrientationObserver.cpp @@ -9,6 +9,8 @@ #include "nsGlobalWindow.h" #include "mozilla/Hal.h" +using namespace mozilla::dom; + /** * This class is used by nsGlobalWindow to implement window.orientation * and window.onorientationchange. This class is defined in its own file diff --git a/dom/base/moz.build b/dom/base/moz.build index 44e1b43c774..3c5ba1d8d7f 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -16,7 +16,6 @@ XPIDL_SOURCES += [ 'nsIDOMDOMCursor.idl', 'nsIDOMDOMRequest.idl', 'nsIDOMFileList.idl', - 'nsIDOMFileReader.idl', 'nsIDOMFormData.idl', 'nsIDOMParser.idl', 'nsIDOMSerializer.idl', @@ -180,6 +179,7 @@ EXPORTS.mozilla.dom += [ 'EventSource.h', 'File.h', 'FileList.h', + 'FileReader.h', 'FragmentOrElement.h', 'FromParser.h', 'ImageEncoder.h', @@ -244,8 +244,8 @@ UNIFIED_SOURCES += [ 'Element.cpp', 'EventSource.cpp', 'File.cpp', - 'FileIOObject.cpp', 'FileList.cpp', + 'FileReader.cpp', 'FragmentOrElement.cpp', 'ImageEncoder.cpp', 'ImportManager.cpp', @@ -272,7 +272,6 @@ UNIFIED_SOURCES += [ 'nsDOMAttributeMap.cpp', 'nsDOMCaretPosition.cpp', 'nsDOMClassInfo.cpp', - 'nsDOMFileReader.cpp', 'nsDOMMutationObserver.cpp', 'nsDOMNavigationTiming.cpp', 'nsDOMScriptObjectFactory.cpp', diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index a446fad4f11..51628b1a337 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -2275,8 +2275,7 @@ struct InterfaceShimEntry { // interface that has interface constants that sites might be getting off // of Ci. const InterfaceShimEntry kInterfaceShimMap[] = -{ { "nsIDOMFileReader", "FileReader" }, - { "nsIXMLHttpRequest", "XMLHttpRequest" }, +{ { "nsIXMLHttpRequest", "XMLHttpRequest" }, { "nsIDOMDOMException", "DOMException" }, { "nsIDOMNode", "Node" }, { "nsIDOMCSSPrimitiveValue", "CSSPrimitiveValue" }, diff --git a/dom/base/nsIDOMFileReader.idl b/dom/base/nsIDOMFileReader.idl deleted file mode 100644 index 98b8559f873..00000000000 --- a/dom/base/nsIDOMFileReader.idl +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsIDOMEventTarget.idl" - -interface nsIDOMEventListener; -interface nsIDOMBlob; - -[builtinclass, uuid(2f34c719-bc14-4546-9fb9-2bab75e56e45)] -interface nsIDOMFileReader : nsIDOMEventTarget -{ - [implicit_jscontext] - void readAsArrayBuffer(in nsIDOMBlob filedata); - void readAsBinaryString(in nsIDOMBlob filedata); - void readAsText(in nsIDOMBlob filedata, [optional] in DOMString encoding); - void readAsDataURL(in nsIDOMBlob file); - - void abort(); - - const unsigned short EMPTY = 0; - const unsigned short LOADING = 1; - const unsigned short DONE = 2; - readonly attribute unsigned short readyState; - - [implicit_jscontext] - readonly attribute jsval result; - - // This is a DOMError - readonly attribute nsISupports error; - - [implicit_jscontext] attribute jsval onloadstart; - [implicit_jscontext] attribute jsval onprogress; - [implicit_jscontext] attribute jsval onload; - [implicit_jscontext] attribute jsval onabort; - [implicit_jscontext] attribute jsval onerror; - [implicit_jscontext] attribute jsval onloadend; -}; - -%{ C++ -#define NS_FILEREADER_CID \ -{0x06aa7c21, 0xfe05, 0x4cf2, \ -{0xb1, 0xc4, 0x0c, 0x71, 0x26, 0xa4, 0xf7, 0x13}} -#define NS_FILEREADER_CONTRACTID \ -"@mozilla.org/files/filereader;1" -%} diff --git a/dom/base/test/test_ipc_messagemanager_blob.html b/dom/base/test/test_ipc_messagemanager_blob.html index c8872c93eeb..fa8967888e2 100644 --- a/dom/base/test/test_ipc_messagemanager_blob.html +++ b/dom/base/test/test_ipc_messagemanager_blob.html @@ -26,9 +26,7 @@ return; } - let reader = - Components.classes["@mozilla.org/files/filereader;1"] - .createInstance(Components.interfaces.nsIDOMFileReader); + let reader = new FileReader(); reader.addEventListener("load", function() { let response = reader.result == "this is a great success!" ? message.json : diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index b5c2953d444..e6323eadad7 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -513,7 +513,6 @@ DOMInterfaces = { }, 'FileReader': { - 'nativeType': 'nsDOMFileReader', 'implicitJSContext': [ 'readAsArrayBuffer' ], }, diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 2c097761cc4..66d66c9fb87 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -2269,22 +2269,6 @@ class MethodDefiner(PropertyDefiner): "condition": MemberCondition() }) - # Output an @@iterator for generated iterator interfaces. This should - # not be necessary, but - # https://bugzilla.mozilla.org/show_bug.cgi?id=1091945 means that - # %IteratorPrototype%[@@iterator] is a broken puppy. - if (not static and - not unforgeable and - descriptor.interface.isIteratorInterface()): - self.regular.append({ - "name": "@@iterator", - "methodInfo": False, - "selfHostedName": "IteratorIdentity", - "length": 0, - "flags": "0", - "condition": MemberCondition() - }) - # Generate the maplike/setlike iterator, if one wasn't already # generated by a method. If we already have an @@iterator symbol, fail. if descriptor.interface.maplikeOrSetlikeOrIterable: diff --git a/dom/bindings/Makefile.in b/dom/bindings/Makefile.in index b76814f058a..8875e103518 100644 --- a/dom/bindings/Makefile.in +++ b/dom/bindings/Makefile.in @@ -45,8 +45,7 @@ codegen_dependencies := \ $(GLOBAL_DEPS) \ $(NULL) -# The 1 is to make codegen.pp not optional. -$(call include_deps,codegen.pp,1) +include codegen.pp codegen.pp: $(codegen_dependencies) $(call py_action,webidl,$(srcdir)) diff --git a/dom/indexedDB/test/test_message_manager_ipc.html b/dom/indexedDB/test/test_message_manager_ipc.html index 5a45b90a009..7fae1402eab 100644 --- a/dom/indexedDB/test/test_message_manager_ipc.html +++ b/dom/indexedDB/test/test_message_manager_ipc.html @@ -50,11 +50,6 @@ function childFrameScript() { sendAsyncMessage(mmName, { op: "done", result: result }); } - function createFileReader() { - return Cc["@mozilla.org/files/filereader;1"] - .createInstance(Ci.nsIDOMFileReader); - } - function grabAndContinue(arg) { testGenerator.send(arg); } @@ -78,7 +73,7 @@ function childFrameScript() { info("Reading blob"); - let reader = createFileReader(); + let reader = new FileReader(); reader.addEventListener("load", grabAndContinue); reader.readAsText(blob); @@ -94,7 +89,7 @@ function childFrameScript() { info("Reading slice"); - reader = createFileReader(); + reader = new FileReader(); reader.addEventListener("load", grabAndContinue); reader.readAsText(slice); @@ -155,7 +150,7 @@ function childFrameScript() { info("Reading blob"); - reader = createFileReader(); + reader = new FileReader(); reader.addEventListener("load", grabAndContinue); reader.readAsText(blob); @@ -190,7 +185,7 @@ function childFrameScript() { info("Reading Slice"); - reader = createFileReader(); + reader = new FileReader(); reader.addEventListener("load", grabAndContinue); reader.readAsText(slice); diff --git a/dom/indexedDB/test/unit/test_blob_file_backed.js b/dom/indexedDB/test/unit/test_blob_file_backed.js index 9dff9d081e5..40f49eeb131 100644 --- a/dom/indexedDB/test/unit/test_blob_file_backed.js +++ b/dom/indexedDB/test/unit/test_blob_file_backed.js @@ -7,11 +7,6 @@ var disableWorkerTest = "This test uses SpecialPowers"; var testGenerator = testSteps(); -function createFileReader() { - return SpecialPowers.Cc["@mozilla.org/files/filereader;1"] - .createInstance(SpecialPowers.Ci.nsIDOMFileReader); -} - function testSteps() { const fileIOFlags = 0x02 | // PR_WRONLY @@ -48,7 +43,7 @@ function testSteps() is(file.size, fileData.length, "Correct size"); is(file.type, fileType, "Correct type"); - let fileReader = createFileReader(); + let fileReader = new FileReader(); fileReader.onload = grabEventAndContinueHandler; fileReader.readAsText(file); @@ -85,7 +80,7 @@ function testSteps() is(file.size, fileData.length, "Correct size"); is(file.type, fileType, "Correct type"); - fileReader = createFileReader(); + fileReader = new FileReader(); fileReader.onload = grabEventAndContinueHandler; fileReader.readAsText(file); diff --git a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js index a159fd68a2c..a3050b19e42 100644 --- a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js +++ b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js @@ -52,7 +52,7 @@ if (!this.runTest) { enableExperimental(); } - Cu.importGlobalProperties(["indexedDB", "Blob", "File"]); + Cu.importGlobalProperties(["indexedDB", "Blob", "File", "FileReader"]); do_test_pending(); testGenerator.next(); @@ -339,12 +339,6 @@ function getFile(name, type, str) return new File([str], name, {type: type}); } -function getFileReader() -{ - return SpecialPowers.Cc["@mozilla.org/files/filereader;1"] - .createInstance(SpecialPowers.Ci.nsIDOMFileReader); -} - function compareBuffers(buffer1, buffer2) { if (buffer1.byteLength != buffer2.byteLength) { @@ -388,7 +382,7 @@ function verifyBlob(blob1, blob2) } if (!buffer2) { - let reader = getFileReader(); + let reader = new FileReader(); reader.readAsArrayBuffer(blob2); reader.onload = function(event) { buffer2 = event.target.result; @@ -400,7 +394,7 @@ function verifyBlob(blob1, blob2) } } - let reader = getFileReader(); + let reader = new FileReader(); reader.readAsArrayBuffer(blob1); reader.onload = function(event) { buffer1 = event.target.result; diff --git a/dom/ipc/tests/test_blob_sliced_from_parent_process.html b/dom/ipc/tests/test_blob_sliced_from_parent_process.html index 00f8b13110c..8a00ed5083b 100644 --- a/dom/ipc/tests/test_blob_sliced_from_parent_process.html +++ b/dom/ipc/tests/test_blob_sliced_from_parent_process.html @@ -44,11 +44,6 @@ function childFrameScript() { sendAsyncMessage(messageName, { op: "done", result: result }); } - function createFileReader() { - return Cc["@mozilla.org/files/filereader;1"] - .createInstance(Ci.nsIDOMFileReader); - } - function grabAndContinue(arg) { testGenerator.send(arg); } @@ -65,7 +60,7 @@ function childFrameScript() { info("Reading blob"); - let reader = createFileReader(); + let reader = new FileReader(); reader.addEventListener("load", grabAndContinue); reader.readAsText(blob); @@ -84,7 +79,7 @@ function childFrameScript() { info("Reading slice"); - reader = createFileReader(); + reader = new FileReader(); reader.addEventListener("load", grabAndContinue); reader.readAsText(slice); diff --git a/dom/media/mediasource/test/mochitest.ini b/dom/media/mediasource/test/mochitest.ini index b6e0fbeed77..9dfee51bed1 100644 --- a/dom/media/mediasource/test/mochitest.ini +++ b/dom/media/mediasource/test/mochitest.ini @@ -52,6 +52,8 @@ skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) [test_HaveMetadataUnbufferedSeek.html] [test_HaveMetadataUnbufferedSeek_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138 +[test_LoadedDataFired_mp4.html] +skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ [test_LoadedMetadataFired.html] [test_LoadedMetadataFired_mp4.html] skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ diff --git a/dom/media/mediasource/test/test_LoadedDataFired_mp4.html b/dom/media/mediasource/test/test_LoadedDataFired_mp4.html new file mode 100644 index 00000000000..5af5d51a909 --- /dev/null +++ b/dom/media/mediasource/test/test_LoadedDataFired_mp4.html @@ -0,0 +1,68 @@ + + + + MSE: Check that playback only starts once we have data at time = 0 + + + + + +
+
+
+ + diff --git a/dom/media/webaudio/AudioEventTimeline.h b/dom/media/webaudio/AudioEventTimeline.h index ded8a5f1984..135cc711b87 100644 --- a/dom/media/webaudio/AudioEventTimeline.h +++ b/dom/media/webaudio/AudioEventTimeline.h @@ -41,6 +41,7 @@ struct AudioTimelineEvent final double aDuration = 0.0, const float* aCurve = nullptr, uint32_t aCurveLength = 0) : mType(aType) + , mCurve(nullptr) , mTimeConstant(aTimeConstant) , mDuration(aDuration) #ifdef DEBUG diff --git a/dom/mobilemessage/gonk/MmsService.js b/dom/mobilemessage/gonk/MmsService.js index b9386c7de4c..a58ea4eb6de 100644 --- a/dom/mobilemessage/gonk/MmsService.js +++ b/dom/mobilemessage/gonk/MmsService.js @@ -8,7 +8,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; -Cu.importGlobalProperties(['Blob']); +Cu.importGlobalProperties(['Blob', 'FileReader']); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PhoneNumberUtils.jsm"); @@ -1303,8 +1303,7 @@ SendTransaction.prototype = Object.create(CancellableTransaction.prototype, { return; } - let fileReader = Cc["@mozilla.org/files/filereader;1"] - .createInstance(Ci.nsIDOMFileReader); + let fileReader = new FileReader(); fileReader.addEventListener("loadend", (aEvent) => { let arrayBuffer = aEvent.target.result; aPart.content = new Uint8Array(arrayBuffer); diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index 1742dbc1918..703fc48f16c 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -1254,12 +1254,22 @@ _retainobject(NPObject* npobj) void _releaseobject(NPObject* npobj) { + // If nothing is passed, just return, even if we're on the wrong thread. + if (!npobj) { + return; + } + + // THIS IS A KNOWN LEAK. SEE BUG 1221448. + // If releaseobject is called off the main thread and we have a valid pointer, + // we at least know it was created on the main thread (see _createobject + // implementation). However, forwarding the deletion back to the main thread + // without careful checking could cause bad memory management races. So, for + // now, we leak by warning and then just returning early. But it should fix + // java 7 crashes. if (!NS_IsMainThread()) { NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_releaseobject called from the wrong thread\n")); - MOZ_CRASH("NPN_releaseobject called from the wrong thread"); - } - if (!npobj) return; + } int32_t refCnt = PR_ATOMIC_DECREMENT((int32_t*)&npobj->referenceCount); NS_LOG_RELEASE(npobj, refCnt, "BrowserNPObject"); diff --git a/dom/webidl/FileReader.webidl b/dom/webidl/FileReader.webidl index 0032efeba3b..b4bea254058 100644 --- a/dom/webidl/FileReader.webidl +++ b/dom/webidl/FileReader.webidl @@ -10,7 +10,8 @@ * liability, trademark and document use rules apply. */ -[Constructor] +[Constructor, + Exposed=(Window,System)] interface FileReader : EventTarget { // async read methods [Throws] diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index 079772d858f..5efef6f3d9c 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -626,6 +626,12 @@ private: AssertIsOnMainThread(); MOZ_ASSERT(aIndex < mLoadInfos.Length()); + // If one load info cancels or hits an error, it can race with the start + // callback coming from another load info. + if (mCanceledMainThread || !mCacheCreator) { + return NS_ERROR_FAILURE; + } + ScriptLoadInfo& loadInfo = mLoadInfos[aIndex]; nsCOMPtr channel = do_QueryInterface(aRequest); diff --git a/editor/libeditor/tests/mochitest.ini b/editor/libeditor/tests/mochitest.ini index 2e34a3b8fe4..a6ced80b015 100644 --- a/editor/libeditor/tests/mochitest.ini +++ b/editor/libeditor/tests/mochitest.ini @@ -81,7 +81,7 @@ skip-if = toolkit == 'android' skip-if = toolkit == 'android' [test_bug596506.html] [test_bug597331.html] -skip-if = buildapp == 'mulet' +skip-if = buildapp == 'mulet' || toolkit == 'android' || asan # Bug 1211213 [test_bug597784.html] [test_bug599322.html] skip-if = toolkit == 'android' diff --git a/gfx/layers/LayerMetricsWrapper.h b/gfx/layers/LayerMetricsWrapper.h index e18fdeec37f..7a5ca6a002f 100644 --- a/gfx/layers/LayerMetricsWrapper.h +++ b/gfx/layers/LayerMetricsWrapper.h @@ -368,6 +368,17 @@ public: return sNoClipRect; } + float GetPresShellResolution() const + { + MOZ_ASSERT(IsValid()); + + if (AtTopLayer() && mLayer->AsContainerLayer()) { + return mLayer->AsContainerLayer()->GetPresShellResolution(); + } + + return 1.0f; + } + EventRegionsOverride GetEventRegionsOverride() const { MOZ_ASSERT(IsValid()); diff --git a/gfx/layers/LayersLogging.cpp b/gfx/layers/LayersLogging.cpp index 3b4eeff7aac..d9374ec640c 100644 --- a/gfx/layers/LayersLogging.cpp +++ b/gfx/layers/LayersLogging.cpp @@ -226,26 +226,6 @@ AppendToString(std::stringstream& aStream, const Matrix& m, aStream << sfx; } -void -AppendToString(std::stringstream& aStream, const Matrix4x4& m, - const char* pfx, const char* sfx) -{ - if (m.Is2D()) { - Matrix matrix = m.As2D(); - AppendToString(aStream, matrix, pfx, sfx); - return; - } - - aStream << pfx; - aStream << nsPrintfCString( - "[ %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g; ]", - m._11, m._12, m._13, m._14, - m._21, m._22, m._23, m._24, - m._31, m._32, m._33, m._34, - m._41, m._42, m._43, m._44).get(); - aStream << sfx; -} - void AppendToString(std::stringstream& aStream, const Matrix5x4& m, const char* pfx, const char* sfx) diff --git a/gfx/layers/LayersLogging.h b/gfx/layers/LayersLogging.h index 2288a169e5b..bf177d3b40d 100644 --- a/gfx/layers/LayersLogging.h +++ b/gfx/layers/LayersLogging.h @@ -7,7 +7,7 @@ #define GFX_LAYERSLOGGING_H #include "FrameMetrics.h" // for FrameMetrics, etc -#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4 +#include "mozilla/gfx/Matrix.h" // for Matrix4x4 #include "mozilla/gfx/Point.h" // for IntSize, etc #include "mozilla/gfx/Types.h" // for Filter, SurfaceFormat #include "mozilla/layers/CompositorTypes.h" // for TextureFlags @@ -185,9 +185,26 @@ void AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix& m, const char* pfx="", const char* sfx=""); +template void -AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix4x4& m, - const char* pfx="", const char* sfx=""); +AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix4x4Typed& m, + const char* pfx="", const char* sfx="") +{ + if (m.Is2D()) { + mozilla::gfx::Matrix matrix = m.As2D(); + AppendToString(aStream, matrix, pfx, sfx); + return; + } + + aStream << pfx; + aStream << nsPrintfCString( + "[ %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g; ]", + m._11, m._12, m._13, m._14, + m._21, m._22, m._23, m._24, + m._31, m._32, m._33, m._34, + m._41, m._42, m._43, m._44).get(); + aStream << sfx; +} void AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix5x4& m, diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index ad816df735b..718aa0eecef 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -797,6 +797,12 @@ public: return true; } + + void SetDestination(const nsPoint& aNewDestination) { + mXAxisModel.SetDestination(static_cast(aNewDestination.x)); + mYAxisModel.SetDestination(static_cast(aNewDestination.y)); + } + private: AsyncPanZoomController& mApzc; AxisPhysicsMSDModel mXAxisModel, mYAxisModel; @@ -3164,6 +3170,9 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri CancelAnimation(); mFrameMetrics = aLayerMetrics; + if (scrollOffsetUpdated) { + AcknowledgeScrollUpdate(); + } mLastDispatchedPaintMetrics = aLayerMetrics; ShareCompositorFrameMetrics(); @@ -3223,20 +3232,22 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri ToString(mFrameMetrics.GetScrollOffset()).c_str(), ToString(aLayerMetrics.GetScrollOffset()).c_str()); + // Send an acknowledgement with the new scroll generation so that any + // repaint requests later in this function go through. + // Because of the scroll generation update, any inflight paint requests are + // going to be ignored by layout, and so mLastDispatchedPaintMetrics + // becomes incorrect for the purposes of calculating the LD transform. To + // correct this we need to update mLastDispatchedPaintMetrics to be the + // last thing we know was painted by Gecko. mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics); + AcknowledgeScrollUpdate(); + mLastDispatchedPaintMetrics = aLayerMetrics; // Cancel the animation (which might also trigger a repaint request) // after we update the scroll offset above. Otherwise we can be left // in a state where things are out of sync. CancelAnimation(); - // Because of the scroll offset update, any inflight paint requests are - // going to be ignored by layout, and so mLastDispatchedPaintMetrics - // becomes incorrect for the purposes of calculating the LD transform. To - // correct this we need to update mLastDispatchedPaintMetrics to be the - // last thing we know was painted by Gecko. - mLastDispatchedPaintMetrics = aLayerMetrics; - // Since the scroll offset has changed, we need to recompute the // displayport margins and send them to layout. Otherwise there might be // scenarios where for example we scroll from the top of a page (where the @@ -3253,34 +3264,26 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri // thread. This flag will be reset by the main thread when it receives // the scroll update acknowledgement. - APZC_LOG("%p smooth scrolling from %s to %s\n", this, + APZC_LOG("%p smooth scrolling from %s to %s in state %d\n", this, Stringify(mFrameMetrics.GetScrollOffset()).c_str(), - Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str()); + Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str(), + mState); - CancelAnimation(); - - // It's important to not send repaint requests between this - // CopySmoothScrollInfo call and the sending of the scroll update - // acknowledgement below, otherwise that repaint request will get rejected - // on the main thread but mLastPaintRequestMetrics will have the new scroll - // generation; this would leave the main thread out of date. + // See comment on the similar code in the |if (scrollOffsetUpdated)| block + // above. mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics); + AcknowledgeScrollUpdate(); mLastDispatchedPaintMetrics = aLayerMetrics; - StartSmoothScroll(ScrollSource::DOM); - scrollOffsetUpdated = true; // Ensure that AcknowledgeScrollUpdate is called - } - - if (scrollOffsetUpdated) { - // Once layout issues a scroll offset update, it becomes impervious to - // scroll offset updates from APZ until we acknowledge the update it sent. - // This prevents APZ updates from clobbering scroll updates from other - // more "legitimate" sources like content scripts. - RefPtr controller = GetGeckoContentController(); - if (controller) { - APZC_LOG("%p sending scroll update acknowledgement with gen %u\n", this, aLayerMetrics.GetScrollGeneration()); - controller->AcknowledgeScrollUpdate(aLayerMetrics.GetScrollId(), - aLayerMetrics.GetScrollGeneration()); + if (mState == SMOOTH_SCROLL && mAnimation) { + APZC_LOG("%p updating destination on existing animation\n", this); + RefPtr animation( + static_cast(mAnimation.get())); + animation->SetDestination( + CSSPoint::ToAppUnits(aLayerMetrics.GetSmoothScrollOffset())); + } else { + CancelAnimation(); + StartSmoothScroll(ScrollSource::DOM); } } @@ -3290,6 +3293,21 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri UpdateSharedCompositorFrameMetrics(); } +void +AsyncPanZoomController::AcknowledgeScrollUpdate() const +{ + // Once layout issues a scroll offset update, it becomes impervious to + // scroll offset updates from APZ until we acknowledge the update it sent. + // This prevents APZ updates from clobbering scroll updates from other + // more "legitimate" sources like content scripts. + RefPtr controller = GetGeckoContentController(); + if (controller) { + APZC_LOG("%p sending scroll update acknowledgement with gen %u\n", this, mFrameMetrics.GetScrollGeneration()); + controller->AcknowledgeScrollUpdate(mFrameMetrics.GetScrollId(), + mFrameMetrics.GetScrollGeneration()); + } +} + const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() const { mMonitor.AssertCurrentThreadIn(); return mFrameMetrics; diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index 4bc1779cc7e..7185ccdcdfb 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -829,11 +829,6 @@ private: PanGestureBlockState* CurrentPanGestureBlock(); - /* =================================================================== - * The functions and members in this section are used to manage - * pan gestures. - */ - private: /* =================================================================== * The functions and members in this section are used to manage @@ -889,6 +884,8 @@ private: // Returns whether overscroll is allowed during an event. bool AllowScrollHandoffInCurrentBlock() const; + void AcknowledgeScrollUpdate() const; + /* =================================================================== * The functions and members in this section are used to make ancestor chains * out of APZC instances. These chains can only be walked or manipulated diff --git a/gfx/layers/client/ClientTiledPaintedLayer.cpp b/gfx/layers/client/ClientTiledPaintedLayer.cpp index 12fea208f8c..fc1db54d18a 100644 --- a/gfx/layers/client/ClientTiledPaintedLayer.cpp +++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp @@ -84,8 +84,8 @@ GetTransformToAncestorsParentLayer(Layer* aStart, const LayerMetricsWrapper& aAn // With containerless scrolling, the offending post-scale is on the // parent layer of the displayport-ancestor, which we don't reach in this // loop, so we don't need to worry about it. - const FrameMetrics& metrics = iter.Metrics(); - transform.PostScale(metrics.GetPresShellResolution(), metrics.GetPresShellResolution(), 1.f); + float presShellResolution = iter.GetPresShellResolution(); + transform.PostScale(presShellResolution, presShellResolution, 1.0f); } } return ViewAs(transform); diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 42b9fd7d89d..b01a663df76 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -120,6 +120,7 @@ class mozilla::gl::SkiaGLGlue : public GenericAtomicRefCounted { #include "mozilla/Attributes.h" #include "mozilla/Mutex.h" +#include "nsAlgorithm.h" #include "nsIGfxInfo.h" #include "nsIXULRuntime.h" #include "VsyncSource.h" @@ -1067,11 +1068,10 @@ gfxPlatform::ComputeTileSize() if (gfxPrefs::LayersTilesAdjust()) { gfx::IntSize screenSize = GetScreenSize(); if (screenSize.width > 0) { - // For the time being tiles larger than 512 probably do not make much - // sense. This is due to e.g. increased rasterisation time outweighing - // the decreased composition time, or large increases in memory usage - // for screens slightly wider than a higher power of two. - w = h = screenSize.width >= 512 ? 512 : 256; + // Choose a size so that there are between 2 and 4 tiles per screen width. + // FIXME: we should probably make sure this is within the max texture size, + // but I think everything should at least support 1024 + w = h = clamped(NextPowerOfTwo(screenSize.width) / 4, 256, 1024); } #ifdef MOZ_WIDGET_GONK diff --git a/js/public/Class.h b/js/public/Class.h index ceb9eb69d40..6d60e5c5959 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -719,7 +719,7 @@ struct JSClass { // application. #define JSCLASS_GLOBAL_APPLICATION_SLOTS 5 #define JSCLASS_GLOBAL_SLOT_COUNT \ - (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 35) + (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 36) #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \ (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n))) #define JSCLASS_GLOBAL_FLAGS \ diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 6a9f7da230c..338eb77076a 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -685,9 +685,6 @@ function CreateArrayIterator(obj, kind) { return CreateArrayIteratorAt(obj, kind, 0); } -function ArrayIteratorIdentity() { - return this; -} function ArrayIteratorNext() { if (!IsObject(this) || !IsArrayIterator(this)) { diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index 5e82aeea252..50bea4f3eb5 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -118,7 +118,6 @@ const Class MapIteratorObject::class_ = { }; const JSFunctionSpec MapIteratorObject::methods[] = { - JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0), JS_SELF_HOSTED_FN("next", "MapIteratorNext", 0, 0), JS_FS_END }; @@ -144,11 +143,9 @@ GlobalObject::initMapIteratorProto(JSContext* cx, Handle global) Rooted base(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global)); if (!base) return false; - Rooted proto(cx, - NewObjectWithGivenProto(cx, base)); + RootedPlainObject proto(cx, NewObjectWithGivenProto(cx, base)); if (!proto) return false; - proto->setSlot(MapIteratorObject::RangeSlot, PrivateValue(nullptr)); if (!JS_DefineFunctions(cx, proto, MapIteratorObject::methods)) return false; global->setReservedSlot(MAP_ITERATOR_PROTO, ObjectValue(*proto)); @@ -844,7 +841,6 @@ const Class SetIteratorObject::class_ = { }; const JSFunctionSpec SetIteratorObject::methods[] = { - JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0), JS_FN("next", next, 0, 0), JS_FS_END }; @@ -869,11 +865,9 @@ GlobalObject::initSetIteratorProto(JSContext* cx, Handle global) Rooted base(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global)); if (!base) return false; - Rooted proto(cx, - NewObjectWithGivenProto(cx, base)); + RootedPlainObject proto(cx, NewObjectWithGivenProto(cx, base)); if (!proto) return false; - proto->setSlot(SetIteratorObject::RangeSlot, PrivateValue(nullptr)); if (!JS_DefineFunctions(cx, proto, SetIteratorObject::methods)) return false; global->setReservedSlot(SET_ITERATOR_PROTO, ObjectValue(*proto)); diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index abe869be44f..53edb08d7f0 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2040,6 +2040,9 @@ ASTSerializer::declaration(ParseNode* pn, MutableHandleValue dst) case PNK_FUNCTION: return function(pn, AST_FUNC_DECL, dst); + case PNK_ANNEXB_FUNCTION: + return function(pn->pn_left, AST_FUNC_DECL, dst); + case PNK_VAR: return variableDeclaration(pn, false, dst); @@ -2411,6 +2414,9 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) case PNK_VAR: return declaration(pn, dst); + case PNK_ANNEXB_FUNCTION: + return declaration(pn->pn_left, dst); + case PNK_LETBLOCK: return letBlock(pn, dst); diff --git a/js/src/builtin/String.js b/js/src/builtin/String.js index 23a2e21af8f..0b23ce8932d 100644 --- a/js/src/builtin/String.js +++ b/js/src/builtin/String.js @@ -199,10 +199,6 @@ function String_iterator() { return iterator; } -function StringIteratorIdentity() { - return this; -} - function StringIteratorNext() { if (!IsObject(this) || !IsStringIterator(this)) { return callFunction(CallStringIteratorMethodIfWrapped, this, diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index dd06b00acf8..974fad07492 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1372,20 +1372,20 @@ BytecodeEmitter::emitVarIncDec(ParseNode* pn) } bool -BytecodeEmitter::atBodyLevel() const +BytecodeEmitter::atBodyLevel(StmtInfoBCE* stmt) const { // 'eval' and non-syntactic scripts are always under an invisible lexical // scope, but since it is not syntactic, it should still be considered at // body level. if (sc->staticScope()->is()) { - bool bl = !innermostStmt()->enclosing; - MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK); - MOZ_ASSERT_IF(bl, innermostStmt()->staticScope - ->as() - .enclosingStaticScope() == sc->staticScope()); + bool bl = !stmt->enclosing; + MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK); + MOZ_ASSERT_IF(bl, stmt->staticScope + ->as() + .enclosingStaticScope() == sc->staticScope()); return bl; } - return !innermostStmt() || sc->isModuleBox(); + return !stmt || sc->isModuleBox(); } uint32_t @@ -1738,7 +1738,7 @@ BytecodeEmitter::bindNameToSlotHelper(ParseNode* pn) MOZ_ASSERT(dn->isDefn()); pn->pn_dflags |= (dn->pn_dflags & PND_CONST); } else if (pn->isDefn()) { - dn = (Definition*) pn; + dn = &pn->as(); } else { return true; } @@ -2344,6 +2344,10 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) MOZ_ASSERT(pn->pn_count == 1); return checkSideEffects(pn->pn_head, answer); + case PNK_ANNEXB_FUNCTION: + MOZ_ASSERT(pn->isArity(PN_BINARY)); + return checkSideEffects(pn->pn_left, answer); + case PNK_ARGSBODY: *answer = true; return true; @@ -3084,11 +3088,23 @@ BytecodeEmitter::emitSwitch(ParseNode* pn) if (!enterBlockScope(&stmtInfo, cases->pn_objbox, JSOP_UNINITIALIZED, 0)) return false; - stmtInfo.type = StmtType::SWITCH; - stmtInfo.update = top = offset(); - // Advance |cases| to refer to the switch case list. cases = cases->expr(); + + // A switch statement may contain hoisted functions inside its + // cases. The PNX_FUNCDEFS flag is propagated from the STATEMENTLIST + // bodies of the cases to the case list. + if (cases->pn_xflags & PNX_FUNCDEFS) { + for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) { + if (caseNode->pn_right->pn_xflags & PNX_FUNCDEFS) { + if (!emitHoistedFunctionsInList(caseNode->pn_right)) + return false; + } + } + } + + stmtInfo.type = StmtType::SWITCH; + stmtInfo.update = top = offset(); } else { MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST)); top = offset(); @@ -4363,7 +4379,7 @@ BytecodeEmitter::emitVariables(ParseNode* pn, VarEmitOption emitOption) * i' to be hoisted out of the loop. */ MOZ_ASSERT(binding->isOp(JSOP_NOP)); - MOZ_ASSERT(emitOption != DefineVars); + MOZ_ASSERT(emitOption != DefineVars && emitOption != AnnexB); /* * To allow the front end to rewrite var f = x; as f = x; when a @@ -4424,13 +4440,18 @@ BytecodeEmitter::emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode op == JSOP_STRICTSETGNAME) { MOZ_ASSERT(emitOption != PushInitialValues); - JSOp bindOp; - if (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME) - bindOp = JSOP_BINDNAME; - else - bindOp = JSOP_BINDGNAME; - if (!emitIndex32(bindOp, atomIndex)) - return false; + if (op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME) { + if (!emitIndex32(JSOP_BINDGNAME, atomIndex)) + return false; + } else if (emitOption == AnnexB) { + // Annex B vars always go on the nearest variable environment, + // even if scopes on the chain contain same-named bindings. + if (!emit1(JSOP_BINDVAR)) + return false; + } else { + if (!emitIndex32(JSOP_BINDNAME, atomIndex)) + return false; + } } bool oldEmittingForInit = emittingForInit; @@ -4454,7 +4475,7 @@ BytecodeEmitter::emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode // If we are not initializing, nothing to pop. If we are initializing // lets, we must emit the pops. - if (emitOption == InitializeVars) { + if (emitOption == InitializeVars || emitOption == AnnexB) { MOZ_ASSERT_IF(binding->isDefn(), initializer == binding->pn_expr); if (!binding->pn_scopecoord.isFree()) { if (!emitVarOp(binding, op)) @@ -5292,6 +5313,28 @@ BytecodeEmitter::emitLetBlock(ParseNode* pnLet) return true; } +bool +BytecodeEmitter::emitHoistedFunctionsInList(ParseNode* list) +{ + MOZ_ASSERT(list->pn_xflags & PNX_FUNCDEFS); + + for (ParseNode* pn = list->pn_head; pn; pn = pn->pn_next) { + if (!sc->strict()) { + while (pn->isKind(PNK_LABEL)) + pn = pn->as().statement(); + } + + if (pn->isKind(PNK_ANNEXB_FUNCTION) || + (pn->isKind(PNK_FUNCTION) && pn->functionIsHoisted())) + { + if (!emitTree(pn)) + return false; + } + } + + return true; +} + // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See // the comment on emitSwitch. MOZ_NEVER_INLINE bool @@ -5303,7 +5346,17 @@ BytecodeEmitter::emitLexicalScope(ParseNode* pn) if (!enterBlockScope(&stmtInfo, pn->pn_objbox, JSOP_UNINITIALIZED, 0)) return false; - if (!emitTree(pn->pn_expr)) + ParseNode* body = pn->pn_expr; + + if (body->isKind(PNK_STATEMENTLIST) && body->pn_xflags & PNX_FUNCDEFS) { + // This block contains function statements whose definitions are + // hoisted to the top of the block. Emit these as a separate pass + // before the rest of the block. + if (!emitHoistedFunctionsInList(body)) + return false; + } + + if (!emitTree(body)) return false; if (!leaveNestedScope(&stmtInfo)) @@ -6164,6 +6217,12 @@ BytecodeEmitter::emitComprehensionFor(ParseNode* compFor) MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) { + ParseNode* assignmentForAnnexB = nullptr; + if (pn->isKind(PNK_ANNEXB_FUNCTION)) { + assignmentForAnnexB = pn->pn_right; + pn = pn->pn_left; + } + FunctionBox* funbox = pn->pn_funbox; RootedFunction fun(cx, funbox->function()); MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript()); @@ -6174,9 +6233,25 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) * function will be seen by emitFunction in two places. */ if (funbox->wasEmitted) { + // Annex B block-scoped functions are hoisted like any other + // block-scoped function to the top of their scope. When their + // definitions are seen for the second time, we need to emit the + // assignment that assigns the function to the outer 'var' binding. + if (assignmentForAnnexB) { + if (assignmentForAnnexB->isKind(PNK_VAR)) { + if (!emitVariables(assignmentForAnnexB, AnnexB)) + return false; + } else { + MOZ_ASSERT(assignmentForAnnexB->isKind(PNK_ASSIGN)); + if (!emitTree(assignmentForAnnexB)) + return false; + if (!emit1(JSOP_POP)) + return false; + } + } + MOZ_ASSERT_IF(fun->hasScript(), fun->nonLazyScript()); MOZ_ASSERT(pn->functionIsHoisted()); - MOZ_ASSERT(sc->isFunctionBox()); return true; } @@ -6284,10 +6359,28 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) * For modules, we record the function and instantiate the binding during * ModuleDeclarationInstantiation(), before the script is run. */ - if (sc->isGlobalContext()) { + + // Check for functions that were parsed under labeled statements per ES6 + // Annex B.3.2. + bool blockScopedFunction = !atBodyLevel(); + if (!sc->strict() && blockScopedFunction) { + StmtInfoBCE* stmt = innermostStmt(); + while (stmt && stmt->type == StmtType::LABEL) + stmt = stmt->enclosing; + blockScopedFunction = !atBodyLevel(stmt); + } + + if (blockScopedFunction) { + if (!emitIndexOp(JSOP_LAMBDA, index)) + return false; + MOZ_ASSERT(pn->getOp() == JSOP_INITLEXICAL); + if (!emitVarOp(pn, pn->getOp())) + return false; + if (!emit1(JSOP_POP)) + return false; + } else if (sc->isGlobalContext()) { MOZ_ASSERT(pn->pn_scopecoord.isFree()); MOZ_ASSERT(pn->getOp() == JSOP_NOP); - MOZ_ASSERT(atBodyLevel()); switchToPrologue(); if (!emitIndex32(JSOP_DEFFUN, index)) return false; @@ -7924,7 +8017,6 @@ BytecodeEmitter::emitArgsBody(ParseNode *pn) // Carefully emit everything in the right order: // 1. Defaults and Destructuring for each argument // 2. Functions - ParseNode* pnchild = pnlast->pn_head; bool hasDefaults = sc->asFunctionBox()->hasDefaults(); ParseNode* rest = nullptr; bool restIsDefn = false; @@ -7985,21 +8077,11 @@ BytecodeEmitter::emitArgsBody(ParseNode *pn) } } if (pnlast->pn_xflags & PNX_FUNCDEFS) { - // This block contains top-level function definitions. To ensure - // that we emit the bytecode defining them before the rest of code - // in the block we use a separate pass over functions. During the - // main pass later the emitter will add JSOP_NOP with source notes - // for the function to preserve the original functions position - // when decompiling. - // - // Currently this is used only for functions, as compile-as-we go - // mode for scripts does not allow separate emitter passes. - for (ParseNode* pn2 = pnchild; pn2; pn2 = pn2->pn_next) { - if (pn2->isKind(PNK_FUNCTION) && pn2->functionIsHoisted()) { - if (!emitTree(pn2)) - return false; - } - } + // This function contains top-level inner function definitions. To + // ensure that we emit the bytecode defining them before the rest + // of code in the block we use a separate pass over functions. + if (!emitHoistedFunctionsInList(pnlast)) + return false; } return emitTree(pnlast); } @@ -8218,6 +8300,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) switch (pn->getKind()) { case PNK_FUNCTION: + case PNK_ANNEXB_FUNCTION: if (!emitFunction(pn)) return false; break; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 125a59d08ec..8e452e23674 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -121,7 +121,12 @@ enum VarEmitOption { // Emit code to evaluate initializer expressions and leave those values on // the stack. This is used to implement `for (let/const ...;;)` and // deprecated `let` blocks. - PushInitialValues + PushInitialValues, + + // Like InitializeVars, but bind using BINDVAR instead of + // BINDNAME/BINDGNAME. Only used for emitting declarations synthesized for + // Annex B block-scoped function semantics. + AnnexB, }; struct BytecodeEmitter @@ -249,7 +254,10 @@ struct BytecodeEmitter return parser->blockScopes[dn->pn_blockid]; } - bool atBodyLevel() const; + bool atBodyLevel(StmtInfoBCE* stmt) const; + bool atBodyLevel() const { + return atBodyLevel(innermostStmt()); + } uint32_t computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut); bool isAliasedName(BytecodeEmitter* bceOfDef, ParseNode* pn); bool computeDefinitionIsAliased(BytecodeEmitter* bceOfDef, Definition* dn, JSOp* op); @@ -464,6 +472,8 @@ struct BytecodeEmitter MOZ_NEVER_INLINE bool emitFunction(ParseNode* pn, bool needsProto = false); MOZ_NEVER_INLINE bool emitObject(ParseNode* pn); + bool emitHoistedFunctionsInList(ParseNode* pn); + bool emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, PropListType type); // To catch accidental misuse, emitUint16Operand/emit3 assert that they are diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index f226310c209..1c80e99db7a 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -100,6 +100,11 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) *result = false; return true; + case PNK_ANNEXB_FUNCTION: + MOZ_ASSERT(node->isArity(PN_BINARY)); + *result = false; + return true; + case PNK_MODULE: *result = false; return true; @@ -1783,6 +1788,9 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo case PNK_FUNCTION: return FoldFunction(cx, pn, parser, inGenexpLambda); + case PNK_ANNEXB_FUNCTION: + return FoldFunction(cx, pn->pn_left, parser, inGenexpLambda); + case PNK_MODULE: return FoldModule(cx, pn, parser); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 031f78ec783..fd559007979 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -444,24 +444,40 @@ class FullParseHandler return pn; } + template + bool isFunctionStmt(ParseNode* stmt, PC* pc) { + if (!pc->sc->strict()) { + while (stmt->isKind(PNK_LABEL)) + stmt = stmt->as().statement(); + } + + return stmt->isKind(PNK_FUNCTION) || stmt->isKind(PNK_ANNEXB_FUNCTION); + } + template void addStatementToList(ParseNode* list, ParseNode* stmt, PC* pc) { MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST)); - if (stmt->isKind(PNK_FUNCTION)) { - if (pc->atBodyLevel()) { - // PNX_FUNCDEFS notifies the emitter that the block contains - // body-level function definitions that should be processed - // before the rest of nodes. - list->pn_xflags |= PNX_FUNCDEFS; - } else { - // General deoptimization was done in Parser::functionDef. - MOZ_ASSERT_IF(pc->sc->isFunctionBox(), - pc->sc->asFunctionBox()->hasExtensibleScope()); - } - } - list->append(stmt); + + if (isFunctionStmt(stmt, pc)) { + // PNX_FUNCDEFS notifies the emitter that the block contains + // body-level function definitions that should be processed + // before the rest of nodes. + list->pn_xflags |= PNX_FUNCDEFS; + } + } + + template + void addCaseStatementToList(ParseNode* list, ParseNode* casepn, PC* pc) { + MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST)); + MOZ_ASSERT(casepn->isKind(PNK_CASE)); + MOZ_ASSERT(casepn->pn_right->isKind(PNK_STATEMENTLIST)); + + list->append(casepn); + + if (casepn->pn_right->pn_xflags & PNX_FUNCDEFS) + list->pn_xflags |= PNX_FUNCDEFS; } bool prependInitialYield(ParseNode* stmtList, ParseNode* genName) { @@ -656,6 +672,11 @@ class FullParseHandler MOZ_ASSERT(pn->isKind(PNK_FUNCTION)); pn->pn_funbox = funbox; } + ParseNode* newFunctionDefinitionForAnnexB(ParseNode* pn, ParseNode* assignment) { + MOZ_ASSERT(pn->isKind(PNK_FUNCTION)); + MOZ_ASSERT(assignment->isKind(PNK_ASSIGN) || assignment->isKind(PNK_VAR)); + return new_(PNK_ANNEXB_FUNCTION, JSOP_NOP, pos(), pn, assignment); + } void addFunctionArgument(ParseNode* pn, ParseNode* argpn) { pn->pn_body->append(argpn); } @@ -727,7 +748,7 @@ class FullParseHandler return node->isKind(PNK_SUPERBASE); } - inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op); + inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init); inline void setLexicalDeclarationOp(ParseNode* pn, JSOp op); void setBeginPosition(ParseNode* pn, ParseNode* oth) { @@ -990,7 +1011,7 @@ FullParseHandler::setLastFunctionArgumentDestructuring(ParseNode* funcpn, ParseN } inline bool -FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op) +FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init) { if (pn->isUsed()) { pn = makeAssignment(pn, init); diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index cbc6578b1eb..d9f97c1c526 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -791,6 +791,12 @@ class NameResolver return false; break; + case PNK_ANNEXB_FUNCTION: + MOZ_ASSERT(cur->isArity(PN_BINARY)); + if (!resolve(cur->pn_left, prefix)) + return false; + break; + // Kinds that should be handled by parent node resolution. case PNK_IMPORT_SPEC: // by PNK_IMPORT_SPEC_LIST diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 245831c077f..0dfeb0cc4d9 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -283,7 +283,8 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_NEWTARGET: case PNK_SETTHIS: case PNK_FOR: - case PNK_COMPREHENSIONFOR: { + case PNK_COMPREHENSIONFOR: + case PNK_ANNEXB_FUNCTION: { MOZ_ASSERT(pn->isArity(PN_BINARY)); stack->push(pn->pn_left); stack->push(pn->pn_right); diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index d70a0689295..71308c1657d 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -165,6 +165,7 @@ class PackedScopeCoordinate F(FORIN) \ F(FOROF) \ F(FORHEAD) \ + F(ANNEXB_FUNCTION) \ F(ARGSBODY) \ F(SPREAD) \ F(MUTATEPROTO) \ @@ -271,6 +272,9 @@ IsDeleteKind(ParseNodeKind kind) * pn_scopecoord: hops and var index for function * pn_dflags: PND_* definition/use flags (see below) * pn_blockid: block id number + * PNK_ANNEXB_FUNCTION binary pn_left: PNK_FUNCTION + * pn_right: assignment for annex B semantics for + * block-scoped function * PNK_ARGSBODY list list of formal parameters with * PNK_NAME node with non-empty name for * SingleNameBinding without Initializer @@ -780,7 +784,8 @@ class ParseNode isOp(JSOP_DEFFUN) || // non-body-level function statement isOp(JSOP_NOP) || // body-level function stmt in global code isOp(JSOP_GETLOCAL) || // body-level function stmt in function code - isOp(JSOP_GETARG)); // body-level function redeclaring formal + isOp(JSOP_GETARG) || // body-level function redeclaring formal + isOp(JSOP_INITLEXICAL)); // block-level function stmt return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) && !isOp(JSOP_DEFFUN); } @@ -1607,6 +1612,8 @@ struct Definition : public ParseNode IMPORT }; + static bool test(const ParseNode& pn) { return pn.isDefn(); } + bool canHaveInitializer() { return int(kind()) <= int(ARG); } static const char* kindString(Kind kind); @@ -1615,6 +1622,8 @@ struct Definition : public ParseNode if (getKind() == PNK_FUNCTION) { if (isOp(JSOP_GETARG)) return ARG; + if (isOp(JSOP_INITLEXICAL)) + return LET; return VAR; } MOZ_ASSERT(getKind() == PNK_NAME); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 093b379b5b1..ba5f3f75157 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -103,9 +103,7 @@ ParseContext::checkLocalsOverflow(TokenStream& ts) static void MarkUsesAsHoistedLexical(ParseNode* pn) { - MOZ_ASSERT(pn->isDefn()); - - Definition* dn = (Definition*)pn; + Definition* dn = &pn->as(); ParseNode** pnup = &dn->dn_uses; ParseNode* pnu; unsigned start = pn->pn_blockid; @@ -218,6 +216,8 @@ ParseContext::define(TokenStream& ts, MOZ_ASSERT(!pn->isUsed()); MOZ_ASSERT_IF(pn->isDefn(), pn->isPlaceholder()); + pn->setDefn(true); + Definition* prevDef = nullptr; if (kind == Definition::LET || kind == Definition::CONSTANT) prevDef = decls_.lookupFirst(name); @@ -236,7 +236,7 @@ ParseContext::define(TokenStream& ts, while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) { MOZ_ASSERT(pnu->pn_blockid >= bodyid); MOZ_ASSERT(pnu->isUsed()); - pnu->pn_lexdef = (Definition*) pn; + pnu->pn_lexdef = &pn->as(); pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS; pnup = &pnu->pn_link; } @@ -254,12 +254,11 @@ ParseContext::define(TokenStream& ts, } MOZ_ASSERT_IF(kind != Definition::LET && kind != Definition::CONSTANT, !lexdeps->lookup(name)); - pn->setDefn(true); pn->pn_dflags &= ~PND_PLACEHOLDER; if (kind == Definition::CONSTANT) pn->pn_dflags |= PND_CONST; - Definition* dn = (Definition*)pn; + Definition* dn = &pn->as(); switch (kind) { case Definition::ARG: MOZ_ASSERT(sc->isFunctionBox()); @@ -389,7 +388,7 @@ ParseContext::updateDecl(TokenStream& ts, JSAtom* atom, Node pn) Definition* oldDecl = decls_.lookupFirst(atom); pn->setDefn(true); - Definition* newDecl = (Definition*)pn; + Definition* newDecl = &pn->template as(); decls_.updateFirst(atom, newDecl); if (sc->isGlobalContext() || oldDecl->isDeoptimized()) { @@ -1195,18 +1194,6 @@ template <> bool Parser::checkFunctionArguments() { - /* - * Non-top-level functions use JSOP_DEFFUN which is a dynamic scope - * operation which means it aliases any bindings with the same name. - */ - if (FuncStmtSet* set = pc->funcStmts) { - for (FuncStmtSet::Range r = set->all(); !r.empty(); r.popFront()) { - PropertyName* name = r.front()->asPropertyName(); - if (Definition* dn = pc->decls().lookupFirst(name)) - dn->pn_dflags |= PND_CLOSED; - } - } - /* Time to implement the odd semantics of 'arguments'. */ HandlePropertyName arguments = context->names().arguments; @@ -1401,7 +1388,7 @@ Parser::makeDefIntoUse(Definition* dn, ParseNode* pn, HandleAt for (ParseNode* pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) { MOZ_ASSERT(pnu->isUsed()); MOZ_ASSERT(!pnu->isDefn()); - pnu->pn_lexdef = (Definition*) pn; + pnu->pn_lexdef = &pn->as(); pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS; } pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS; @@ -1444,7 +1431,7 @@ Parser::makeDefIntoUse(Definition* dn, ParseNode* pn, HandleAt return false; pn->dn_uses = lhs; dn->pn_link = nullptr; - dn = (Definition*) lhs; + dn = &lhs->as(); } } @@ -1455,7 +1442,7 @@ Parser::makeDefIntoUse(Definition* dn, ParseNode* pn, HandleAt dn->setOp((CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETNAME : JSOP_GETNAME); dn->setDefn(false); dn->setUsed(true); - dn->pn_lexdef = (Definition*) pn; + dn->pn_lexdef = &pn->as(); dn->pn_scopecoord.makeFree(); dn->pn_dflags &= ~PND_BOUND; return true; @@ -1487,18 +1474,22 @@ struct BindData void initLexical(VarContext varContext, JSOp op, StaticBlockObject* blockObj, unsigned overflow) { - init(LexicalBinding, op, op == JSOP_DEFCONST); + init(LexicalBinding, op, op == JSOP_DEFCONST, false); letData_.varContext = varContext; letData_.blockObj = blockObj; letData_.overflow = overflow; } void initVar(JSOp op) { - init(VarBinding, op, false); + init(VarBinding, op, false, false); + } + + void initAnnexBVar() { + init(VarBinding, JSOP_DEFVAR, false, true); } void initDestructuring(JSOp op) { - init(DestructuringBinding, op, false); + init(DestructuringBinding, op, false, false); } void setNameNode(typename ParseHandler::Node pn) { @@ -1521,6 +1512,11 @@ struct BindData return isConst_; } + bool isAnnexB() { + MOZ_ASSERT(isInitialized()); + return isAnnexB_; + } + const LetData& letData() { MOZ_ASSERT(kind_ == LexicalBinding); return letData_; @@ -1557,17 +1553,19 @@ struct BindData JSOp op_; // Prologue bytecode or nop. bool isConst_; // Whether this is a const binding. + bool isAnnexB_; // Whether this is a synthesized 'var' binding for Annex B.3. LetData letData_; bool isInitialized() { return kind_ != Uninitialized; } - void init(BindingKind kind, JSOp op, bool isConst) { + void init(BindingKind kind, JSOp op, bool isConst, bool isAnnexB) { MOZ_ASSERT(!isInitialized()); kind_ = kind; op_ = op; isConst_ = isConst; + isAnnexB_ = isAnnexB; } }; @@ -2231,138 +2229,215 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn return true; } +template <> +bool +Parser::bindBodyLevelFunctionName(HandlePropertyName funName, + ParseNode** pn_) +{ + MOZ_ASSERT(pc->atBodyLevel() || !pc->sc->strict()); + + ParseNode*& pn = *pn_; + + /* + * Handle redeclaration and optimize cases where we can statically bind the + * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). + */ + if (Definition* dn = pc->decls().lookupFirst(funName)) { + MOZ_ASSERT(!dn->isUsed()); + MOZ_ASSERT(dn->isDefn()); + + if (dn->kind() == Definition::CONSTANT || dn->kind() == Definition::LET) + return reportRedeclaration(nullptr, Definition::VAR, funName); + + /* + * Body-level function statements are effectively variable + * declarations where the initialization is hoisted to the + * beginning of the block. This means that any other variable + * declaration with the same name is really just an assignment to + * the function's binding (which is mutable), so turn any existing + * declaration into a use. + */ + if (dn->kind() == Definition::ARG) { + // The exception to the above comment is when the function + // has the same name as an argument. Then the argument node + // remains a definition. But change the function node pn so + // that it knows where the argument is located. + pn->setOp(JSOP_GETARG); + pn->setDefn(true); + pn->pn_scopecoord = dn->pn_scopecoord; + pn->pn_blockid = dn->pn_blockid; + pn->pn_dflags |= PND_BOUND; + dn->markAsAssigned(); + } else { + if (!makeDefIntoUse(dn, pn, funName)) + return false; + } + } else { + /* + * If this function was used before it was defined, claim the + * pre-created definition node for this function that primaryExpr + * put in pc->lexdeps on first forward reference, and recycle pn. + */ + if (Definition* fn = pc->lexdeps.lookupDefn(funName)) { + MOZ_ASSERT(fn->isDefn()); + fn->setKind(PNK_FUNCTION); + fn->setArity(PN_CODE); + fn->pn_pos.begin = pn->pn_pos.begin; + fn->pn_pos.end = pn->pn_pos.end; + + fn->pn_body = nullptr; + fn->pn_scopecoord.makeFree(); + + pc->lexdeps->remove(funName); + handler.freeTree(pn); + pn = fn; + } + + if (!pc->define(tokenStream, funName, pn, Definition::VAR)) + return false; + } + + /* No further binding (in BindNameToSlot) is needed for functions. */ + pn->pn_dflags |= PND_BOUND; + + MOZ_ASSERT(pn->functionIsHoisted()); + MOZ_ASSERT(pc->sc->isGlobalContext() == pn->pn_scopecoord.isFree()); + + return true; +} + +template <> +bool +Parser::bindLexicalFunctionName(HandlePropertyName funName, + ParseNode* pn); + template <> bool Parser::checkFunctionDefinition(HandlePropertyName funName, ParseNode** pn_, FunctionSyntaxKind kind, - bool* pbodyProcessed) + bool* pbodyProcessed, + ParseNode** assignmentForAnnexBOut) { ParseNode*& pn = *pn_; *pbodyProcessed = false; - /* Function statements add a binding to the enclosing scope. */ - bool bodyLevel = pc->atBodyLevel(); - if (kind == Statement) { - /* - * Handle redeclaration and optimize cases where we can statically bind the - * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). - */ - if (Definition* dn = pc->decls().lookupFirst(funName)) { - MOZ_ASSERT(!dn->isUsed()); - MOZ_ASSERT(dn->isDefn()); + MOZ_ASSERT(assignmentForAnnexBOut); + *assignmentForAnnexBOut = nullptr; - bool throwRedeclarationError = dn->kind() == Definition::CONSTANT || - dn->kind() == Definition::LET; - if (options().extraWarningsOption || throwRedeclarationError) { - JSAutoByteString name; - ParseReportKind reporter = throwRedeclarationError - ? ParseError - : ParseExtraWarning; - if (!AtomToPrintableString(context, funName, &name) || - !report(reporter, false, nullptr, JSMSG_REDECLARED_VAR, - Definition::kindString(dn->kind()), name.ptr())) - { + // In sloppy mode, ES6 Annex B.3.2 allows labelled function + // declarations. Otherwise it is a parse error. + bool bodyLevelFunction = pc->atBodyLevel(); + if (!bodyLevelFunction) { + StmtInfoPC* stmt = pc->innermostStmt(); + if (stmt->type == StmtType::LABEL) { + if (pc->sc->strict()) { + report(ParseError, false, null(), JSMSG_FUNCTION_LABEL); return false; } - } - /* - * Body-level function statements are effectively variable - * declarations where the initialization is hoisted to the - * beginning of the block. This means that any other variable - * declaration with the same name is really just an assignment to - * the function's binding (which is mutable), so turn any existing - * declaration into a use. - */ - if (bodyLevel) { - if (dn->kind() == Definition::ARG) { - // The exception to the above comment is when the function - // has the same name as an argument. Then the argument node - // remains a definition. But change the function node pn so - // that it knows where the argument is located. - pn->setOp(JSOP_GETARG); - pn->setDefn(true); - pn->pn_scopecoord = dn->pn_scopecoord; - pn->pn_blockid = dn->pn_blockid; - pn->pn_dflags |= PND_BOUND; - dn->markAsAssigned(); - } else { - if (!makeDefIntoUse(dn, pn, funName)) - return false; + stmt = pc->innermostNonLabelStmt(); + // A switch statement is always braced, so it's okay to label + // functions in sloppy mode under switch. + if (stmt && stmt->type != StmtType::BLOCK && stmt->type != StmtType::SWITCH) { + report(ParseError, false, null(), JSMSG_SLOPPY_FUNCTION_LABEL); + return false; } + + bodyLevelFunction = pc->atBodyLevel(stmt); } - } else if (bodyLevel) { - /* - * If this function was used before it was defined, claim the - * pre-created definition node for this function that primaryExpr - * put in pc->lexdeps on first forward reference, and recycle pn. - */ - if (Definition* fn = pc->lexdeps.lookupDefn(funName)) { - MOZ_ASSERT(fn->isDefn()); - fn->setKind(PNK_FUNCTION); - fn->setArity(PN_CODE); - fn->pn_pos.begin = pn->pn_pos.begin; - fn->pn_pos.end = pn->pn_pos.end; - - fn->pn_body = nullptr; - fn->pn_scopecoord.makeFree(); - - pc->lexdeps->remove(funName); - handler.freeTree(pn); - pn = fn; - } - - if (!pc->define(tokenStream, funName, pn, Definition::VAR)) - return false; } - if (bodyLevel) { - MOZ_ASSERT(pn->functionIsHoisted()); - MOZ_ASSERT(pc->sc->isGlobalContext() == pn->pn_scopecoord.isFree()); + if (bodyLevelFunction) { + if (!bindBodyLevelFunctionName(funName, pn_)) + return false; } else { - /* - * As a SpiderMonkey-specific extension, non-body-level function - * statements (e.g., functions in an "if" or "while" block) are - * dynamically bound when control flow reaches the statement. - */ - MOZ_ASSERT(!pc->sc->strict()); - MOZ_ASSERT(pn->pn_scopecoord.isFree()); - if (pc->sc->isFunctionBox()) { - FunctionBox* funbox = pc->sc->asFunctionBox(); - funbox->setMightAliasLocals(); - funbox->setHasExtensibleScope(); - } - pn->setOp(JSOP_DEFFUN); + Definition* annexDef = nullptr; + Node synthesizedDeclarationList = null(); - /* - * Instead of setting bindingsAccessedDynamically, which would be - * overly conservative, remember the names of all function - * statements and mark any bindings with the same as aliased at the - * end of functionBody. - */ - if (!pc->funcStmts) { - pc->funcStmts = alloc.new_(alloc); - if (!pc->funcStmts || !pc->funcStmts->init()) { - ReportOutOfMemory(context); - return false; + if (!pc->sc->strict()) { + // Under non-strict mode, try ES6 Annex B.3.3 semantics. If + // making an additional 'var' binding of the same name does + // not throw an early error, do so. This 'var' binding would + // be assigned the function object when its declaration is + // reached, not at the start of the block. + + annexDef = pc->decls().lookupFirst(funName); + if (annexDef) { + if (annexDef->kind() == Definition::CONSTANT || + annexDef->kind() == Definition::LET) + { + // Do not emit Annex B assignment if we would've + // thrown a redeclaration error. + annexDef = nullptr; + } + } else { + // Synthesize a new 'var' binding if one does not exist. + ParseNode* varNode = newBindingNode(funName, /* functionScope = */ true); + if (!varNode) + return false; + + // Treat the 'var' binding as body level. Otherwise the + // lexical binding of the function name below would result + // in a redeclaration. That is, + // { var x; let x; } is an early error. + // var x; { let x; } is not. + varNode->pn_blockid = pc->bodyid; + + BindData data(context); + data.initAnnexBVar(); + data.setNameNode(varNode); + if (!data.bind(funName, this)) + return false; + + annexDef = &varNode->as(); + + synthesizedDeclarationList = handler.newDeclarationList(PNK_VAR, JSOP_DEFVAR); + if (!synthesizedDeclarationList) + return false; + handler.addList(synthesizedDeclarationList, annexDef); } } - if (!pc->funcStmts->put(funName)) + + if (!bindLexicalFunctionName(funName, pn)) return false; - /* - * Due to the implicit declaration mechanism, 'arguments' will not - * have decls and, even if it did, they will not be noted as closed - * in the emitter. Thus, in the corner case of function statements - * overridding arguments, flag the whole scope as dynamic. - */ - if (funName == context->names().arguments) - pc->sc->setBindingsAccessedDynamically(); - } + if (annexDef) { + MOZ_ASSERT(!pc->sc->strict()); - /* No further binding (in BindNameToSlot) is needed for functions. */ - pn->pn_dflags |= PND_BOUND; + // Synthesize an assignment assigning the lexical name to the + // 'var' name for Annex B. + + ParseNode* rhs = newName(funName); + if (!rhs) + return false; + if (!noteNameUse(funName, rhs)) + return false; + + // If we synthesized a new definition, emit the declaration to + // ensure DEFVAR is correctly emitted in global scripts. + // Otherwise, synthesize a simple assignment and emit that. + if (synthesizedDeclarationList) { + if (!handler.finishInitializerAssignment(annexDef, rhs)) + return false; + *assignmentForAnnexBOut = synthesizedDeclarationList; + } else { + ParseNode* lhs = newName(funName); + if (!lhs) + return false; + lhs->setOp(JSOP_SETNAME); + + // Manually link up the LHS with the non-lexical definition. + handler.linkUseToDef(lhs, annexDef); + + ParseNode* assign = handler.newAssignment(PNK_ASSIGN, lhs, rhs, pc, JSOP_NOP); + if (!assign) + return false; + + *assignmentForAnnexBOut = assign; + } + } + } } else { /* A function expression does not introduce any binding. */ pn->setOp(kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA); @@ -2471,7 +2546,8 @@ template <> bool Parser::checkFunctionDefinition(HandlePropertyName funName, Node* pn, FunctionSyntaxKind kind, - bool* pbodyProcessed) + bool* pbodyProcessed, + Node* assignmentForAnnexBOut) { *pbodyProcessed = false; @@ -2479,10 +2555,18 @@ Parser::checkFunctionDefinition(HandlePropertyName funName, bool bodyLevel = pc->atBodyLevel(); if (kind == Statement) { + *assignmentForAnnexBOut = null(); + + if (!bodyLevel) { + // Block-scoped functions cannot yet be parsed lazily. + return abortIfSyntaxParser(); + } + /* * Handle redeclaration and optimize cases where we can statically bind the * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). */ + if (DefinitionNode dn = pc->decls().lookupFirst(funName)) { if (dn == Definition::CONSTANT || dn == Definition::LET) { JSAutoByteString name; @@ -2493,16 +2577,13 @@ Parser::checkFunctionDefinition(HandlePropertyName funName, return false; } } - } else if (bodyLevel) { + } else { if (pc->lexdeps.lookupDefn(funName)) pc->lexdeps->remove(funName); if (!pc->define(tokenStream, funName, *pn, Definition::VAR)) return false; } - - if (!bodyLevel && funName == context->names().arguments) - pc->sc->setBindingsAccessedDynamically(); } if (kind == Arrow) { @@ -2583,7 +2664,8 @@ template typename ParseHandler::Node Parser::functionDef(InHandling inHandling, YieldHandling yieldHandling, HandlePropertyName funName, FunctionSyntaxKind kind, - GeneratorKind generatorKind, InvokedPrediction invoked) + GeneratorKind generatorKind, InvokedPrediction invoked, + Node* assignmentForAnnexBOut) { MOZ_ASSERT_IF(kind == Statement, funName); @@ -2591,12 +2673,13 @@ Parser::functionDef(InHandling inHandling, YieldHandling yieldHand Node pn = handler.newFunctionDefinition(); if (!pn) return null(); + handler.setBlockId(pn, pc->blockid()); if (invoked) pn = handler.setLikelyIIFE(pn); bool bodyProcessed; - if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed)) + if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed, assignmentForAnnexBOut)) return null(); if (bodyProcessed) @@ -2783,7 +2866,6 @@ Parser::functionArgsAndBody(InHandling inHandling, ParseNode* if (!addFreeVariablesFromLazyFunction(fun, pc)) return false; - pn->pn_blockid = outerpc->blockid(); PropagateTransitiveParseFlags(funbox, outerpc->sc); return true; } while (false); @@ -2801,8 +2883,6 @@ Parser::functionArgsAndBody(InHandling inHandling, ParseNode* if (!leaveFunction(pn, outerpc, kind)) return false; - pn->pn_blockid = outerpc->blockid(); - /* * Fruit of the poisonous tree: if a closure contains a dynamic name access * (eval, with, etc), we consider the parent to do the same. The reason is @@ -3044,6 +3124,24 @@ Parser::functionStmt(YieldHandling yieldHandling, DefaultHandling { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); + // ES6 Annex B.3.4 says we can parse function declarations unbraced under if or + // else as if it were braced. That is, |if (x) function f() {}| is parsed as + // |if (x) { function f() {} }|. + Maybe synthesizedStmtInfoForAnnexB; + Node synthesizedBlockForAnnexB = null(); + StmtInfoPC *stmt = pc->innermostStmt(); + if (!pc->sc->strict() && stmt) { + if (stmt->type == StmtType::IF || stmt->type == StmtType::ELSE) { + if (!abortIfSyntaxParser()) + return null(); + + synthesizedStmtInfoForAnnexB.emplace(*this, StmtType::BLOCK); + synthesizedBlockForAnnexB = pushLexicalScope(*synthesizedStmtInfoForAnnexB); + if (!synthesizedBlockForAnnexB) + return null(); + } + } + RootedPropertyName name(context); GeneratorKind generatorKind = NotGenerator; TokenKind tt; @@ -3071,12 +3169,35 @@ Parser::functionStmt(YieldHandling yieldHandling, DefaultHandling return null(); } - /* We forbid function statements in strict mode code. */ - if (!pc->atBodyLevel() && pc->sc->needStrictChecks() && - !report(ParseStrictError, pc->sc->strict(), null(), JSMSG_STRICT_FUNCTION_STATEMENT)) + Node assignmentForAnnexB; + Node fun = functionDef(InAllowed, yieldHandling, name, Statement, generatorKind, + PredictUninvoked, &assignmentForAnnexB); + if (!fun) return null(); - return functionDef(InAllowed, yieldHandling, name, Statement, generatorKind); + if (assignmentForAnnexB) { + fun = handler.newFunctionDefinitionForAnnexB(fun, assignmentForAnnexB); + if (!fun) + return null(); + } + + // Note that we may have synthesized a block for Annex B.3.4 without + // having synthesized an assignment for Annex B.3.3, e.g., + // + // let f = 1; + // { + // if (1) function f() {} + // } + if (synthesizedBlockForAnnexB) { + Node body = handler.newStatementList(pc->blockid(), handler.getPosition(fun)); + if (!body) + return null(); + handler.addStatementToList(body, fun, pc); + handler.setLexicalScopeBody(synthesizedBlockForAnnexB, body); + return synthesizedBlockForAnnexB; + } + + return fun; } template @@ -3680,19 +3801,24 @@ Parser::bindVar(BindData* data, StmtInfoPC* stmt = LexicalLookup(pc, name); if (stmt && stmt->type == StmtType::WITH) { - parser->handler.setFlag(pn, PND_DEOPTIMIZED); - if (pc->sc->isFunctionBox()) { - FunctionBox* funbox = pc->sc->asFunctionBox(); - funbox->setMightAliasLocals(); - } + // Do not deoptimize if we are binding a synthesized 'var' binding for + // Annex B.3.3, which states that the synthesized binding is to go on + // the nearest VariableEnvironment. Deoptimizing here would + // erroneously emit NAME ops when assigning to the Annex B 'var'. + if (!data->isAnnexB()) { + parser->handler.setFlag(pn, PND_DEOPTIMIZED); + if (pc->sc->isFunctionBox()) { + FunctionBox* funbox = pc->sc->asFunctionBox(); + funbox->setMightAliasLocals(); + } - /* - * Make sure to indicate the need to deoptimize the script's arguments - * object. Mark the function as if it contained a debugger statement, - * which will deoptimize arguments as much as possible. - */ - if (name == cx->names().arguments) - pc->sc->setHasDebuggerStatement(); + // Make sure to indicate the need to deoptimize the script's + // arguments object. Mark the function as if it contained a + // debugger statement, which will deoptimize arguments as much as + // possible. + if (name == cx->names().arguments) + pc->sc->setHasDebuggerStatement(); + } // Find the nearest enclosing non-with scope that defined name, if // any, for redeclaration checks below. @@ -3827,23 +3953,27 @@ Parser::noteNameUse(HandlePropertyName name, Node pn) template <> bool -Parser::bindUninitialized(BindData* data, ParseNode* pn) +Parser::bindUninitialized(BindData* data, HandlePropertyName name, + ParseNode* pn) { - MOZ_ASSERT(pn->isKind(PNK_NAME)); - - RootedPropertyName name(context, pn->pn_atom->asPropertyName()); - data->setNameNode(pn); - if (!data->bind(name, this)) - return false; - return true; + return data->bind(name, this); } template <> bool -Parser::bindInitialized(BindData* data, ParseNode* pn) +Parser::bindUninitialized(BindData* data, ParseNode* pn) { - if (!bindUninitialized(data, pn)) + RootedPropertyName name(context, pn->name()); + return bindUninitialized(data, name, pn); +} + +template <> +bool +Parser::bindInitialized(BindData* data, HandlePropertyName name, + ParseNode* pn) +{ + if (!bindUninitialized(data, name, pn)) return false; /* @@ -3864,6 +3994,14 @@ Parser::bindInitialized(BindData* data, Pars return true; } +template <> +bool +Parser::bindInitialized(BindData* data, ParseNode* pn) +{ + RootedPropertyName name(context, pn->name()); + return bindInitialized(data, name, pn); +} + template <> bool Parser::checkDestructuringName(BindData* data, ParseNode* expr) @@ -4412,7 +4550,7 @@ Parser::variables(YieldHandling yieldHandling, if (!bindBeforeInitializer && !data.bind(name, this)) return null(); - if (!handler.finishInitializerAssignment(pn2, init, data.op())) + if (!handler.finishInitializerAssignment(pn2, init)) return null(); } } @@ -4433,28 +4571,45 @@ Parser::variables(YieldHandling yieldHandling, template <> bool -Parser::checkAndPrepareLexical(bool isConst, const TokenPos& errorPos) +Parser::checkAndPrepareLexical(PrepareLexicalKind prepareWhat, + const TokenPos& errorPos) { /* - * This is a lexical declaration. We must be directly under a block per the - * proposed ES4 specs, but not an implicit block created due to - * 'for (let ...)'. If we pass this error test, make the enclosing - * StmtInfoPC be our scope. Further let declarations in this block will - * find this scope statement and use the same block object. + * This is a lexical declaration. We must be directly under a block for + * 'let' and 'const' declarations. If we pass this error test, make the + * enclosing StmtInfoPC be our scope. Further let declarations in this + * block will find this scope statement and use the same block object. + * + * Function declarations behave like 'let', except that they are allowed + * per ES6 Annex B.3.2 to be labeled, unlike plain 'let' and 'const' + * declarations. * * If we are the first let declaration in this block (i.e., when the * enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then * we also need to set pc->blockNode to be our PNK_LEXICALSCOPE. */ - StmtInfoPC* stmt = pc->innermostStmt(); + + // ES6 Annex B.3.2 does not apply in strict mode, and labeled functions in + // strict mode should have been rejected by checkFunctionDefinition. + MOZ_ASSERT_IF(pc->innermostStmt() && + pc->innermostStmt()->type == StmtType::LABEL && + prepareWhat == PrepareFunction, + !pc->sc->strict()); + + StmtInfoPC* stmt = prepareWhat == PrepareFunction + ? pc->innermostNonLabelStmt() + : pc->innermostStmt(); if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) { - reportWithOffset(ParseError, false, errorPos.begin, JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, - isConst ? "const" : "lexical"); + reportWithOffset(ParseError, false, errorPos.begin, + stmt->type == StmtType::LABEL + ? JSMSG_LEXICAL_DECL_LABEL + : JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, + prepareWhat == PrepareConst ? "const" : "lexical"); return false; } if (!stmt) { - MOZ_ASSERT(pc->atBodyLevel()); + MOZ_ASSERT_IF(prepareWhat != PrepareFunction, pc->atBodyLevel()); /* * Self-hosted code must be usable against *any* global object, @@ -4465,7 +4620,7 @@ Parser::checkAndPrepareLexical(bool isConst, const TokenPos& e bool isGlobal = !pc->sc->isFunctionBox() && stmt == pc->innermostScopeStmt(); if (options().selfHostingMode && isGlobal) { report(ParseError, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL, - isConst ? "'const'" : "'let'"); + prepareWhat == PrepareConst ? "'const'" : "'let'"); return false; } return true; @@ -4491,8 +4646,12 @@ Parser::checkAndPrepareLexical(bool isConst, const TokenPos& e * catch block (catch is a lexical scope by definition). */ MOZ_ASSERT(stmt->canBeBlockScope() && stmt->type != StmtType::CATCH); - - pc->stmtStack.makeInnermostLexicalScope(*blockObj); + if (prepareWhat == PrepareFunction) { + stmt->isBlockScope = true; + pc->stmtStack.linkAsInnermostScopeStmt(stmt, *blockObj); + } else { + pc->stmtStack.makeInnermostLexicalScope(*blockObj); + } MOZ_ASSERT(!blockScopes[stmt->blockid]); blockScopes[stmt->blockid].set(blockObj); @@ -4522,33 +4681,54 @@ CurrentLexicalStaticBlock(ParseContext* pc) } template <> -ParseNode* -Parser::makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, - const TokenPos& pos) +bool +Parser::prepareAndBindInitializedLexicalWithNode(HandlePropertyName name, + PrepareLexicalKind prepareWhat, + ParseNode* pn, + const TokenPos& pos) { BindData data(context); - if (!checkAndPrepareLexical(isConst, pos)) - return null(); - data.initLexical(HoistVars, isConst ? JSOP_DEFCONST : JSOP_DEFLET, + if (!checkAndPrepareLexical(prepareWhat, pos)) + return false; + data.initLexical(HoistVars, prepareWhat == PrepareConst ? JSOP_DEFCONST : JSOP_DEFLET, CurrentLexicalStaticBlock(pc), JSMSG_TOO_MANY_LOCALS); + return bindInitialized(&data, name, pn); +} + +template <> +ParseNode* +Parser::makeInitializedLexicalBinding(HandlePropertyName name, + PrepareLexicalKind prepareWhat, + const TokenPos& pos) +{ ParseNode* dn = newBindingNode(name, false); if (!dn) return null(); handler.setPosition(dn, pos); - if (!bindInitialized(&data, dn)) + if (!prepareAndBindInitializedLexicalWithNode(name, prepareWhat, dn, pos)) return null(); return dn; } +template <> +bool +Parser::bindLexicalFunctionName(HandlePropertyName funName, + ParseNode* pn) +{ + MOZ_ASSERT(!pc->atBodyLevel()); + pn->pn_blockid = pc->blockid(); + return prepareAndBindInitializedLexicalWithNode(funName, PrepareFunction, pn, pos()); +} + template <> ParseNode* Parser::lexicalDeclaration(YieldHandling yieldHandling, bool isConst) { handler.disableSyntaxParser(); - if (!checkAndPrepareLexical(isConst, pos())) + if (!checkAndPrepareLexical(isConst ? PrepareConst : PrepareLet, pos())) return null(); /* @@ -5060,7 +5240,7 @@ Parser::exportDeclaration() default: tokenStream.ungetToken(); RootedPropertyName name(context, context->names().starDefaultStar); - binding = makeInitializedLexicalBinding(name, true, pos()); + binding = makeInitializedLexicalBinding(name, PrepareConst, pos()); if (!binding) return null(); kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited); @@ -5807,7 +5987,7 @@ Parser::switchStatement(YieldHandling yieldHandling) afterReturn = true; } } - handler.addList(body, stmt); + handler.addStatementToList(body, stmt, pc); } // In ES6, lexical bindings cannot be accessed until initialized. If @@ -5826,7 +6006,7 @@ Parser::switchStatement(YieldHandling yieldHandling) Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body); if (!casepn) return null(); - handler.addList(caseList, casepn); + handler.addCaseStatementToList(caseList, casepn, pc); } /* @@ -6653,7 +6833,7 @@ Parser::classDefinition(YieldHandling yieldHandling, ParseNode* nameNode = null(); ParseNode* methodsOrBlock = classMethods; if (name) { - ParseNode* innerBinding = makeInitializedLexicalBinding(name, true, namePos); + ParseNode* innerBinding = makeInitializedLexicalBinding(name, PrepareConst, namePos); if (!innerBinding) return null(); @@ -6664,7 +6844,7 @@ Parser::classDefinition(YieldHandling yieldHandling, ParseNode* outerBinding = null(); if (classContext == ClassStatement) { - outerBinding = makeInitializedLexicalBinding(name, false, namePos); + outerBinding = makeInitializedLexicalBinding(name, PrepareLet, namePos); if (!outerBinding) return null(); } diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index aa51d63431a..a9cbcf1671f 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -53,7 +53,6 @@ struct StmtInfoPC : public StmtInfoBase {} }; -typedef HashSet, LifoAllocPolicy> FuncStmtSet; class SharedContext; typedef Vector DeclVector; @@ -236,10 +235,6 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext public: OwnedAtomDefnMapPtr lexdeps; /* unresolved lexical name dependencies */ - FuncStmtSet* funcStmts; /* Set of (non-top-level) function statements - that will alias any top-level bindings with - the same name. */ - // All inner functions in this context. Only filled in when parsing syntax. Rooted> innerFunctions; @@ -277,7 +272,6 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext parserPC(&prs->pc), oldpc(prs->pc), lexdeps(prs->context), - funcStmts(nullptr), innerFunctions(prs->context, TraceableVector(prs->context)), newDirectives(newDirectives), inDeclDestructuring(false) @@ -295,6 +289,7 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext StmtInfoPC* innermostStmt() const { return stmtStack.innermost(); } StmtInfoPC* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); } + StmtInfoPC* innermostNonLabelStmt() const { return stmtStack.innermostNonLabel(); } JSObject* innermostStaticScope() const { if (StmtInfoPC* stmt = innermostScopeStmt()) return stmt->staticScope; @@ -308,19 +303,23 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext // function f1() { function f2() { } } // if (cond) { function f3() { if (cond) { function f4() { } } } } // - bool atBodyLevel() { + bool atBodyLevel(StmtInfoPC* stmt) { // 'eval' and non-syntactic scripts are always under an invisible // lexical scope, but since it is not syntactic, it should still be // considered at body level. if (sc->staticScope()->is()) { - bool bl = !innermostStmt()->enclosing; - MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK); - MOZ_ASSERT_IF(bl, innermostStmt()->staticScope - ->template as() - .enclosingStaticScope() == sc->staticScope()); + bool bl = !stmt->enclosing; + MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK); + MOZ_ASSERT_IF(bl, stmt->staticScope + ->template as() + .enclosingStaticScope() == sc->staticScope()); return bl; } - return !innermostStmt(); + return !stmt; + } + + bool atBodyLevel() { + return atBodyLevel(innermostStmt()); } bool atGlobalLevel() { @@ -728,7 +727,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node functionDef(InHandling inHandling, YieldHandling uieldHandling, HandlePropertyName name, FunctionSyntaxKind kind, GeneratorKind generatorKind, - InvokedPrediction invoked = PredictUninvoked); + InvokedPrediction invoked = PredictUninvoked, + Node* assignmentForAnnexBOut = nullptr); bool functionArgsAndBody(InHandling inHandling, Node pn, HandleFunction fun, FunctionSyntaxKind kind, GeneratorKind generatorKind, Directives inheritedDirectives, Directives* newDirectives); @@ -794,8 +794,10 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node newThisName(); bool makeDefIntoUse(Definition* dn, Node pn, HandleAtom atom); + bool bindLexicalFunctionName(HandlePropertyName funName, ParseNode* pn); + bool bindBodyLevelFunctionName(HandlePropertyName funName, ParseNode** pn); bool checkFunctionDefinition(HandlePropertyName funName, Node* pn, FunctionSyntaxKind kind, - bool* pbodyProcessed); + bool* pbodyProcessed, Node* assignmentForAnnexBOut); bool finishFunctionDefinition(Node pn, FunctionBox* funbox, Node body); bool addFreeVariablesFromLazyFunction(JSFunction* fun, ParseContext* pc); @@ -844,8 +846,17 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node objectLiteral(YieldHandling yieldHandling); - bool checkAndPrepareLexical(bool isConst, const TokenPos& errorPos); - Node makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, const TokenPos& pos); + enum PrepareLexicalKind { + PrepareLet, + PrepareConst, + PrepareFunction + }; + bool checkAndPrepareLexical(PrepareLexicalKind prepareWhat, const TokenPos& errorPos); + bool prepareAndBindInitializedLexicalWithNode(HandlePropertyName name, + PrepareLexicalKind prepareWhat, + ParseNode* pn, const TokenPos& pos); + Node makeInitializedLexicalBinding(HandlePropertyName name, PrepareLexicalKind prepareWhat, + const TokenPos& pos); Node newBindingNode(PropertyName* name, bool functionScope, VarContext varContext = HoistVars); @@ -860,7 +871,9 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter bool checkDestructuringObject(BindData* data, Node objectPattern); bool checkDestructuringName(BindData* data, Node expr); + bool bindInitialized(BindData* data, HandlePropertyName name, Node pn); bool bindInitialized(BindData* data, Node pn); + bool bindUninitialized(BindData* data, HandlePropertyName name, Node pn); bool bindUninitialized(BindData* data, Node pn); bool makeSetCall(Node node, unsigned errnum); Node cloneDestructuringDefault(Node opn); diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 1c12dc319f3..4cb879f107d 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -580,6 +580,12 @@ class MOZ_STACK_CLASS StmtInfoStack StmtInfo* innermost() const { return innermostStmt_; } StmtInfo* innermostScopeStmt() const { return innermostScopeStmt_; } + StmtInfo* innermostNonLabel() const { + StmtInfo* stmt = innermost(); + while (stmt && stmt->type == StmtType::LABEL) + stmt = stmt->enclosing; + return stmt; + } void push(StmtInfo* stmt, StmtType type) { stmt->type = type; diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 3785ffd181d..0af35febb50 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -290,6 +290,7 @@ class SyntaxParseHandler Node newStatementList(unsigned blockid, const TokenPos& pos) { return NodeGeneric; } void addStatementToList(Node list, Node stmt, ParseContext* pc) {} + void addCaseStatementToList(Node list, Node stmt, ParseContext* pc) {} bool prependInitialYield(Node stmtList, Node gen) { return true; } Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; } @@ -333,6 +334,7 @@ class SyntaxParseHandler Node newFunctionDefinition() { return NodeHoistableDeclaration; } void setFunctionBody(Node pn, Node kid) {} void setFunctionBox(Node pn, FunctionBox* funbox) {} + Node newFunctionDefinitionForAnnexB(Node pn, Node assignment) { return NodeHoistableDeclaration; } void addFunctionArgument(Node pn, Node argpn) {} Node newForStatement(uint32_t begin, Node forHead, Node body, unsigned iflags) { @@ -354,7 +356,7 @@ class SyntaxParseHandler return NodeGeneric; } - bool finishInitializerAssignment(Node pn, Node init, JSOp op) { return true; } + bool finishInitializerAssignment(Node pn, Node init) { return true; } void setLexicalDeclarationOp(Node pn, JSOp op) {} void setBeginPosition(Node pn, Node oth) {} diff --git a/js/src/jit-test/tests/auto-regress/bug771027.js b/js/src/jit-test/tests/auto-regress/bug771027.js index 8c7b9a81a7e..d9038ad85df 100644 --- a/js/src/jit-test/tests/auto-regress/bug771027.js +++ b/js/src/jit-test/tests/auto-regress/bug771027.js @@ -4,6 +4,6 @@ // Flags: // -Array.prototype.iterator = (function() { { while(0) function Uint8ClampedArray() { } } }); +Array.prototype.iterator = (function() { { while(0) { function Uint8ClampedArray() { } } } }); var s = new Set(["testing", "testing", 123]); assertEq(s.size(), 2); diff --git a/js/src/jit-test/tests/baseline/bug1081850.js b/js/src/jit-test/tests/baseline/bug1081850.js deleted file mode 100644 index e77158a1c22..00000000000 --- a/js/src/jit-test/tests/baseline/bug1081850.js +++ /dev/null @@ -1,18 +0,0 @@ -// |jit-test| ion-eager - -var ARR = []; -try { - function f() { - ARR.push(eval.prototype) - } - f() - function eval()(0) - f() -} catch (e) {} - -if (ARR.length !== 2) - throw new Error("ERROR 1"); -if (typeof(ARR[0]) !== 'undefined') - throw new Error("ERROR 2"); -if (typeof(ARR[1]) !== 'object') - throw new Error("ERROR 3"); diff --git a/js/src/jit-test/tests/basic/bug667504-syntax.js b/js/src/jit-test/tests/basic/bug667504-syntax.js index c2e926d9639..60692202068 100644 --- a/js/src/jit-test/tests/basic/bug667504-syntax.js +++ b/js/src/jit-test/tests/basic/bug667504-syntax.js @@ -1,2 +1,3 @@ -for (var x in x) +for (var x in x) { function x() {} +} diff --git a/js/src/jit-test/tests/collections/iterator-1.js b/js/src/jit-test/tests/collections/iterator-1.js index 29cf20fb32c..4b656b879c3 100644 --- a/js/src/jit-test/tests/collections/iterator-1.js +++ b/js/src/jit-test/tests/collections/iterator-1.js @@ -1,11 +1,11 @@ -// collection.iterator() returns an Iterator object. +// collection.iterator() returns an iterator object. load(libdir + "iteration.js"); function test(obj, name) { var iter = obj[Symbol.iterator](); assertEq(typeof iter, "object"); - assertEq(iter instanceof Iterator, true); + assertEq(iter instanceof Iterator, false); // Not a legacy Iterator. assertEq(iter.toString(), "[object " + obj.constructor.name + " Iterator]"); } diff --git a/js/src/jit-test/tests/collections/iterator-proto-1.js b/js/src/jit-test/tests/collections/iterator-proto-1.js index f5dd2beb479..c9827a5746d 100644 --- a/js/src/jit-test/tests/collections/iterator-proto-1.js +++ b/js/src/jit-test/tests/collections/iterator-proto-1.js @@ -1,13 +1,16 @@ // All iterators of the same collection type share their immediate prototype. -// Those prototype objects in turn inherit directly from Iterator.prototype. +// Those prototype objects in turn inherit directly from %IteratorPrototype%. load(libdir + "iteration.js"); +// Get %IteratorPrototype%. +var iterProto = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())); + function test(obj0, obj1) { var iter0 = obj0[Symbol.iterator](), iter1 = obj1[Symbol.iterator](); var proto = Object.getPrototypeOf(iter0); assertEq(Object.getPrototypeOf(iter1), proto); - assertEq(Object.getPrototypeOf(proto), Iterator.prototype); + assertEq(Object.getPrototypeOf(proto), iterProto); } test([], [1]); diff --git a/js/src/jit-test/tests/collections/iterator-proto-surfaces.js b/js/src/jit-test/tests/collections/iterator-proto-surfaces.js index 0d83c7ab5dd..e40207fa8bb 100644 --- a/js/src/jit-test/tests/collections/iterator-proto-surfaces.js +++ b/js/src/jit-test/tests/collections/iterator-proto-surfaces.js @@ -3,22 +3,41 @@ load(libdir + "asserts.js"); load(libdir + "iteration.js"); -function test(constructor) { - var proto = Object.getPrototypeOf(new constructor()[Symbol.iterator]()); - var names = Object.getOwnPropertyNames(proto); - names.sort(); - assertDeepEq(names, ['next']); - assertEq(proto.hasOwnProperty(Symbol.iterator), true); +var iterProto = null; - var desc = Object.getOwnPropertyDescriptor(proto, 'next'); +function test(constructor) { + var iter = new constructor()[Symbol.iterator](); + assertDeepEq(Reflect.ownKeys(iter), []); + + // Iterator prototypes only have a .next property. + // At least until we support @@toStringTag. + var proto1 = Object.getPrototypeOf(iter); + + var names = Reflect.ownKeys(proto1); + names.sort(); + assertDeepEq(Reflect.ownKeys(proto1), ['next']); + + var desc = Object.getOwnPropertyDescriptor(proto1, 'next'); assertEq(desc.configurable, true); assertEq(desc.enumerable, false); assertEq(desc.writable, true); - assertEq(proto[Symbol.iterator](), proto); - assertIteratorDone(proto, undefined); + // %IteratorPrototype% + var proto2 = Object.getPrototypeOf(proto1); + assertEq(Object.getPrototypeOf(proto2), Object.prototype); + assertEq(Object.prototype.toString.call(proto2), "[object Object]"); + + assertDeepEq(Reflect.ownKeys(proto2), [Symbol.iterator]); + assertEq(proto2[Symbol.iterator](), proto2); + + // Check there's a single %IteratorPrototype% object. + if (iterProto === null) + iterProto = proto2; + else + assertEq(iterProto, proto2); } -//test(Array); +test(Array); +test(String); test(Map); test(Set); diff --git a/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js b/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js index 83d5b0572a1..a58ebef3790 100644 --- a/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js +++ b/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js @@ -3,11 +3,11 @@ load(libdir + "iteration.js"); var proto = Object.getPrototypeOf([][Symbol.iterator]()); -assertEq(Object.getPrototypeOf(proto), Iterator.prototype); +var iterProto = Object.getPrototypeOf(proto); proto = Object.getPrototypeOf([].keys()); -assertEq(Object.getPrototypeOf(proto), Iterator.prototype); +assertEq(Object.getPrototypeOf(proto), iterProto); proto = Object.getPrototypeOf([].entries()); -assertEq(Object.getPrototypeOf(proto), Iterator.prototype); +assertEq(Object.getPrototypeOf(proto), iterProto); function check(it) { assertEq(typeof it, 'object'); diff --git a/js/src/jit-test/tests/for-of/string-iterator-surfaces.js b/js/src/jit-test/tests/for-of/string-iterator-surfaces.js index c84e576af5d..1e7e8bb3c9f 100644 --- a/js/src/jit-test/tests/for-of/string-iterator-surfaces.js +++ b/js/src/jit-test/tests/for-of/string-iterator-surfaces.js @@ -53,15 +53,13 @@ assertBuiltinFunction(String.prototype, Symbol.iterator, 0); var iter = ""[Symbol.iterator](); var iterProto = Object.getPrototypeOf(iter); -// StringIterator.prototype inherits from Object.prototype -assertEq(Object.getPrototypeOf(iterProto), Object.prototype); +// StringIterator.prototype inherits from %IteratorPrototype%. Check it's the +// same object as %ArrayIteratorPrototype%'s proto. +assertEq(Object.getPrototypeOf(iterProto), + Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))); // Own properties for StringIterator.prototype: "next" arraysEqual(Object.getOwnPropertyNames(iterProto).sort(), ["next"]); -assertEq(iterProto.hasOwnProperty(Symbol.iterator), true); - -// StringIterator.prototype[@@iterator] is a built-in function -assertBuiltinFunction(iterProto, Symbol.iterator, 0); // StringIterator.prototype.next is a built-in function assertBuiltinFunction(iterProto, "next", 0); diff --git a/js/src/jit-test/tests/ion/bug1148973-1.js b/js/src/jit-test/tests/ion/bug1148973-1.js index b8fb3676806..34d0671446a 100644 --- a/js/src/jit-test/tests/ion/bug1148973-1.js +++ b/js/src/jit-test/tests/ion/bug1148973-1.js @@ -8,7 +8,11 @@ try { } }(), function() {})) } catch (e) {}; +var log = ""; +evaluate(` try { function x() {} assertEq(String(b), "function () {}"); -} catch (e) { throw (e); } +} catch (e) { log += "e"; } +`); +assertEq(log, "e"); diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index bc31d36c8aa..3615385e4a5 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -2259,6 +2259,26 @@ BaselineCompiler::emit_JSOP_BINDGNAME() return emit_JSOP_BINDNAME(); } +typedef JSObject* (*BindVarFn)(JSContext*, HandleObject); +static const VMFunction BindVarInfo = FunctionInfo(jit::BindVar); + +bool +BaselineCompiler::emit_JSOP_BINDVAR() +{ + frame.syncStack(0); + masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); + + prepareVMCall(); + pushArg(R0.scratchReg()); + + if (!callVM(BindVarInfo)) + return false; + + masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); + frame.push(R0); + return true; +} + bool BaselineCompiler::emit_JSOP_SETPROP() { diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 3edfa0af33e..e249d59e12f 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -141,6 +141,7 @@ namespace jit { _(JSOP_DELNAME) \ _(JSOP_GETIMPORT) \ _(JSOP_GETINTRINSIC) \ + _(JSOP_BINDVAR) \ _(JSOP_DEFVAR) \ _(JSOP_DEFCONST) \ _(JSOP_DEFLET) \ diff --git a/js/src/jit/BytecodeAnalysis.cpp b/js/src/jit/BytecodeAnalysis.cpp index ca4456e6483..19ce05e0c03 100644 --- a/js/src/jit/BytecodeAnalysis.cpp +++ b/js/src/jit/BytecodeAnalysis.cpp @@ -158,6 +158,7 @@ BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn) case JSOP_GETNAME: case JSOP_BINDNAME: + case JSOP_BINDVAR: case JSOP_SETNAME: case JSOP_STRICTSETNAME: case JSOP_DELNAME: diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index be83abc6f9f..03bcf047830 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -8428,6 +8428,16 @@ CodeGenerator::visitOutOfLineUnboxFloatingPoint(OutOfLineUnboxFloatingPoint* ool masm.jump(ool->rejoin()); } +typedef JSObject* (*BindVarFn)(JSContext*, HandleObject); +static const VMFunction BindVarInfo = FunctionInfo(jit::BindVar); + +void +CodeGenerator::visitCallBindVar(LCallBindVar* lir) +{ + pushArg(ToRegister(lir->scopeChain())); + callVM(BindVarInfo, lir); +} + typedef bool (*GetPropertyFn)(JSContext*, HandleValue, HandlePropertyName, MutableHandleValue); static const VMFunction GetPropertyInfo = FunctionInfo(GetProperty); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 096040fe7c2..0576c0cde7d 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -329,6 +329,7 @@ class CodeGenerator : public CodeGeneratorSpecific void visitSetDOMProperty(LSetDOMProperty* lir); void visitCallDOMNative(LCallDOMNative* lir); void visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir); + void visitCallBindVar(LCallBindVar* lir); void visitIsCallable(LIsCallable* lir); void visitOutOfLineIsCallable(OutOfLineIsCallable* ool); void visitIsObject(LIsObject* lir); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index e0eeb878dfd..f29e765ee70 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -1942,6 +1942,9 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_BINDNAME: return jsop_bindname(info().getName(pc)); + case JSOP_BINDVAR: + return jsop_bindvar(); + case JSOP_DUP: current->pushSlot(current->stackDepth() - 1); return true; @@ -8401,6 +8404,16 @@ IonBuilder::jsop_bindname(PropertyName* name) return resumeAfter(ins); } +bool +IonBuilder::jsop_bindvar() +{ + MOZ_ASSERT(analysis().usesScopeChain()); + MCallBindVar* ins = MCallBindVar::New(alloc(), current->scopeChain()); + current->add(ins); + current->push(ins); + return true; +} + static MIRType GetElemKnownType(bool needsHoleCheck, TemporaryTypeSet* types) { diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index c2d1b907240..586b2f8fb7c 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -689,6 +689,7 @@ class IonBuilder bool jsop_intrinsic(PropertyName* name); bool jsop_getimport(PropertyName* name); bool jsop_bindname(PropertyName* name); + bool jsop_bindvar(); bool jsop_getelem(); bool jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType); bool jsop_getelem_typed(MDefinition* obj, MDefinition* index, ScalarTypeDescr::Type arrayType); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 228aa212480..ab2fb2d4592 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3362,6 +3362,16 @@ LIRGenerator::visitBindNameCache(MBindNameCache* ins) assignSafepoint(lir, ins); } +void +LIRGenerator::visitCallBindVar(MCallBindVar* ins) +{ + MOZ_ASSERT(ins->scopeChain()->type() == MIRType_Object); + MOZ_ASSERT(ins->type() == MIRType_Object); + + LCallBindVar* lir = new(alloc()) LCallBindVar(useRegister(ins->scopeChain())); + define(lir, ins); +} + void LIRGenerator::visitGuardObjectIdentity(MGuardObjectIdentity* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 066788a1243..95a5c4a763f 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -230,6 +230,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitGetPropertyPolymorphic(MGetPropertyPolymorphic* ins); void visitSetPropertyPolymorphic(MSetPropertyPolymorphic* ins); void visitBindNameCache(MBindNameCache* ins); + void visitCallBindVar(MCallBindVar* ins); void visitGuardObjectIdentity(MGuardObjectIdentity* ins); void visitGuardClass(MGuardClass* ins); void visitGuardObject(MGuardObject* ins); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index cfa5e586c2f..eface1fdcee 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -10757,6 +10757,39 @@ class MBindNameCache } }; +class MCallBindVar + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + explicit MCallBindVar(MDefinition* scopeChain) + : MUnaryInstruction(scopeChain) + { + setResultType(MIRType_Object); + setMovable(); + } + + public: + INSTRUCTION_HEADER(CallBindVar) + + static MCallBindVar* New(TempAllocator& alloc, MDefinition* scopeChain) { + return new(alloc) MCallBindVar(scopeChain); + } + + MDefinition* scopeChain() const { + return getOperand(0); + } + + bool congruentTo(const MDefinition* ins) const override { + if (!ins->isCallBindVar()) + return false; + return congruentIfOperandsEqual(ins); + } + + AliasSet getAliasSet() const override { + return AliasSet::None(); + } +}; + // Guard on an object's shape. class MGuardShape : public MUnaryInstruction, diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 564d1be8128..1fd92ff2f7b 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -162,6 +162,7 @@ namespace jit { _(GetPropertyPolymorphic) \ _(SetPropertyPolymorphic) \ _(BindNameCache) \ + _(CallBindVar) \ _(GuardShape) \ _(GuardReceiverPolymorphic) \ _(GuardObjectGroup) \ diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 1ca98cc00e9..cf4a144abdf 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -167,14 +167,21 @@ CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame, return cx->runtime()->handleInterrupt(cx); } +JSObject* +BindVar(JSContext* cx, HandleObject scopeChain) +{ + JSObject* obj = scopeChain; + while (!obj->isQualifiedVarObj()) + obj = obj->enclosingScope(); + MOZ_ASSERT(obj); + return obj; +} + bool DefVar(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain) { // Given the ScopeChain, extract the VarObj. - RootedObject obj(cx, scopeChain); - while (!obj->isQualifiedVarObj()) - obj = obj->enclosingScope(); - + RootedObject obj(cx, BindVar(cx, scopeChain)); return DefVarOperation(cx, obj, dn, attrs); } @@ -185,10 +192,7 @@ DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject sc Rooted lexical(cx, &NearestEnclosingExtensibleLexicalScope(scopeChain)); // Find the variables object. - RootedObject varObj(cx, scopeChain); - while (!varObj->isQualifiedVarObj()) - varObj = varObj->enclosingScope(); - + RootedObject varObj(cx, BindVar(cx, scopeChain)); return DefLexicalOperation(cx, lexical, varObj, dn, attrs); } @@ -853,9 +857,8 @@ bool InitGlobalOrEvalScopeObjects(JSContext* cx, BaselineFrame* frame) { RootedScript script(cx, frame->script()); - RootedObject varObj(cx, frame->scopeChain()); - while (!varObj->isQualifiedVarObj()) - varObj = varObj->enclosingScope(); + RootedObject scopeChain(cx, frame->scopeChain()); + RootedObject varObj(cx, BindVar(cx, scopeChain)); if (script->isForEval()) { // Strict eval needs its own call object. @@ -866,13 +869,12 @@ InitGlobalOrEvalScopeObjects(JSContext* cx, BaselineFrame* frame) if (!frame->initStrictEvalScopeObjects(cx)) return false; } else { - RootedObject scopeChain(cx, frame->scopeChain()); if (!CheckEvalDeclarationConflicts(cx, script, scopeChain, varObj)) return false; } } else { Rooted lexicalScope(cx, - &NearestEnclosingExtensibleLexicalScope(frame->scopeChain())); + &NearestEnclosingExtensibleLexicalScope(scopeChain)); if (!CheckGlobalDeclarationConflicts(cx, script, lexicalScope, varObj)) return false; } diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 4a85d08bae6..bb5084d5ecc 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -588,6 +588,7 @@ bool CheckOverRecursed(JSContext* cx); bool CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame, uint32_t extra, uint32_t earlyCheck); +JSObject* BindVar(JSContext* cx, HandleObject scopeChain); bool DefVar(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain); bool DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain); bool DefGlobalLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 99751a69c58..b42df7cf88d 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -5775,6 +5775,22 @@ class LBindNameCache : public LInstructionHelper<1, 1, 0> } }; +class LCallBindVar : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(CallBindVar) + + explicit LCallBindVar(const LAllocation& scopeChain) { + setOperand(0, scopeChain); + } + const LAllocation* scopeChain() { + return getOperand(0); + } + const MCallBindVar* mir() const { + return mir_->toCallBindVar(); + } +}; + // Load a value from an object's dslots or a slots vector. class LLoadSlotV : public LInstructionHelper { diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 1bf4e2348e2..51b46862040 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -277,6 +277,7 @@ _(GetPropertyPolymorphicV) \ _(GetPropertyPolymorphicT) \ _(BindNameCache) \ + _(CallBindVar) \ _(CallGetProperty) \ _(GetNameCache) \ _(CallGetIntrinsicValue) \ diff --git a/js/src/js.msg b/js/src/js.msg index a0543737b18..2d976770719 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -270,6 +270,9 @@ MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found") MSG_DEF(JSMSG_LET_CLASS_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a class") MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable") MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block") +MSG_DEF(JSMSG_LEXICAL_DECL_LABEL, 1, JSEXN_SYNTAXERR, "{0} declarations cannot be labelled") +MSG_DEF(JSMSG_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions cannot be labelled") +MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks") MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW, 0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression") MSG_DEF(JSMSG_MALFORMED_ESCAPE, 1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence") MSG_DEF(JSMSG_MISSING_BINARY_DIGITS, 0, JSEXN_SYNTAXERR, "missing binary digits after '0b'") diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 245a430ea4d..9b27317683b 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -543,7 +543,7 @@ Compare(T* a, T* b, size_t c) return true; } -static bool iterator_next(JSContext* cx, unsigned argc, Value* vp); +static bool legacy_iterator_next(JSContext* cx, unsigned argc, Value* vp); static inline PropertyIteratorObject* NewPropertyIteratorObject(JSContext* cx, unsigned flags) @@ -580,7 +580,7 @@ NewPropertyIteratorObject(JSContext* cx, unsigned flags) // next method on the prototype doesn't break cross-global for .. in. // We don't have to do this for JSITER_ENUMERATE because that object always // takes an optimized path. - RootedFunction next(cx, NewNativeFunction(cx, iterator_next, 0, + RootedFunction next(cx, NewNativeFunction(cx, legacy_iterator_next, 0, HandlePropertyName(cx->names().next))); if (!next) return nullptr; @@ -1017,7 +1017,7 @@ IsIterator(HandleValue v) } MOZ_ALWAYS_INLINE bool -iterator_next_impl(JSContext* cx, const CallArgs& args) +legacy_iterator_next_impl(JSContext* cx, const CallArgs& args) { MOZ_ASSERT(IsIterator(args.thisv())); @@ -1040,15 +1040,15 @@ iterator_next_impl(JSContext* cx, const CallArgs& args) } static bool -iterator_next(JSContext* cx, unsigned argc, Value* vp) +legacy_iterator_next(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - return CallNonGenericMethod(cx, args); + return CallNonGenericMethod(cx, args); } -static const JSFunctionSpec iterator_methods[] = { +static const JSFunctionSpec legacy_iterator_methods[] = { JS_SELF_HOSTED_SYM_FN(iterator, "LegacyIteratorShim", 0, 0), - JS_FN("next", iterator_next, 0, 0), + JS_FN("next", legacy_iterator_next, 0, 0), JS_FS_END }; @@ -1109,7 +1109,6 @@ const Class ArrayIteratorObject::class_ = { }; static const JSFunctionSpec array_iterator_methods[] = { - JS_SELF_HOSTED_SYM_FN(iterator, "ArrayIteratorIdentity", 0, 0), JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0), JS_FS_END }; @@ -1131,7 +1130,6 @@ const Class StringIteratorObject::class_ = { }; static const JSFunctionSpec string_iterator_methods[] = { - JS_SELF_HOSTED_SYM_FN(iterator, "StringIteratorIdentity", 0, 0), JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0), JS_FS_END }; @@ -1462,6 +1460,25 @@ const Class StopIterationObject::class_ = { stopiter_hasInstance }; +static const JSFunctionSpec iterator_proto_methods[] = { + JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0), + JS_FS_END +}; + +/* static */ bool +GlobalObject::initIteratorProto(JSContext* cx, Handle global) +{ + if (global->getReservedSlot(ITERATOR_PROTO).isObject()) + return true; + + RootedObject proto(cx, global->createBlankPrototype(cx)); + if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, iterator_proto_methods)) + return false; + + global->setReservedSlot(ITERATOR_PROTO, ObjectValue(*proto)); + return true; +} + /* static */ bool GlobalObject::initArrayIteratorProto(JSContext* cx, Handle global) { @@ -1487,8 +1504,12 @@ GlobalObject::initStringIteratorProto(JSContext* cx, Handle globa if (global->getReservedSlot(STRING_ITERATOR_PROTO).isObject()) return true; + RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global)); + if (!iteratorProto) + return false; + const Class* cls = &StringIteratorPrototypeClass; - RootedObject proto(cx, global->createBlankPrototype(cx, cls)); + RootedObject proto(cx, global->createBlankPrototypeInheriting(cx, cls, iteratorProto)); if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, string_iterator_methods)) return false; @@ -1497,7 +1518,7 @@ GlobalObject::initStringIteratorProto(JSContext* cx, Handle globa } JSObject* -js::InitIteratorClass(JSContext* cx, HandleObject obj) +js::InitLegacyIteratorClass(JSContext* cx, HandleObject obj) { Handle global = obj.as(); @@ -1523,7 +1544,7 @@ js::InitIteratorClass(JSContext* cx, HandleObject obj) return nullptr; if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto)) return nullptr; - if (!DefinePropertiesAndFunctions(cx, iteratorProto, nullptr, iterator_methods)) + if (!DefinePropertiesAndFunctions(cx, iteratorProto, nullptr, legacy_iterator_methods)) return nullptr; if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Iterator, ctor, iteratorProto)) diff --git a/js/src/jsiter.h b/js/src/jsiter.h index 08b4f805d05..31bb95d9c81 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -214,7 +214,7 @@ extern JSObject* CreateItrResultObject(JSContext* cx, HandleValue value, bool done); extern JSObject* -InitIteratorClass(JSContext* cx, HandleObject obj); +InitLegacyIteratorClass(JSContext* cx, HandleObject obj); extern JSObject* InitStopIterationClass(JSContext* cx, HandleObject obj); diff --git a/js/src/jsprototypes.h b/js/src/jsprototypes.h index 9e34e78df4d..1a79ef67bf3 100644 --- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -75,7 +75,7 @@ real(SyntaxError, 16, InitViaClassSpec, ERROR_CLASP(JSEXN_SYNTAXERR)) \ real(TypeError, 17, InitViaClassSpec, ERROR_CLASP(JSEXN_TYPEERR)) \ real(URIError, 18, InitViaClassSpec, ERROR_CLASP(JSEXN_URIERR)) \ - real(Iterator, 19, InitIteratorClass, OCLASP(PropertyIterator)) \ + real(Iterator, 19, InitLegacyIteratorClass,OCLASP(PropertyIterator)) \ real(StopIteration, 20, InitStopIterationClass, OCLASP(StopIteration)) \ real(ArrayBuffer, 21, InitArrayBufferClass, &js::ArrayBufferObject::protoClass) \ real(Int8Array, 22, InitViaClassSpec, TYPED_ARRAY_CLASP(Int8)) \ diff --git a/js/src/tests/ecma_5/extensions/function-definition-with.js b/js/src/tests/ecma_5/extensions/function-definition-with.js index df0ab9e0bd7..2277ad45fc3 100644 --- a/js/src/tests/ecma_5/extensions/function-definition-with.js +++ b/js/src/tests/ecma_5/extensions/function-definition-with.js @@ -19,8 +19,9 @@ var called, obj; function inFile1() { return "in file"; } called = false; obj = { set inFile1(v) { called = true; } }; -with (obj) +with (obj) { function inFile1() { return "in file in with"; }; +} assertEq(inFile1(), "in file in with"); assertEq("set" in Object.getOwnPropertyDescriptor(obj, "inFile1"), true); assertEq(called, false); @@ -28,8 +29,9 @@ assertEq(called, false); evaluate("function notInFile1() { return 'not in file'; }"); called = false; obj = { set notInFile1(v) { called = true; return "not in file 2"; } }; -with (obj) +with (obj) { function notInFile1() { return "not in file in with"; }; +} assertEq(notInFile1(), "not in file in with"); assertEq("set" in Object.getOwnPropertyDescriptor(obj, "notInFile1"), true); assertEq(called, false); @@ -39,8 +41,9 @@ called = false; obj = Object.defineProperty({}, "inFile2", { value: 42, configurable: false, enumerable: false }); -with (obj) +with (obj) { function inFile2() { return "in file 2"; }; +} assertEq(inFile2(), "in file 2"); assertEq(obj.inFile2, 42); diff --git a/js/src/tests/ecma_5/extensions/strict-function-statements.js b/js/src/tests/ecma_5/extensions/strict-function-statements.js index 95b872faf62..e6dc1513948 100644 --- a/js/src/tests/ecma_5/extensions/strict-function-statements.js +++ b/js/src/tests/ecma_5/extensions/strict-function-statements.js @@ -9,46 +9,41 @@ assertEq(testLenientAndStrict("function f() { }", parsesSuccessfully), true); -// Function statements within blocks are forbidden in strict mode code. -assertEq(testLenientAndStrict("{ function f() { } }", - parsesSuccessfully, - parseRaisesException(SyntaxError)), - true); - // Lambdas are always permitted within blocks. assertEq(testLenientAndStrict("{ (function f() { }) }", parsesSuccessfully, parsesSuccessfully), true); -// Function statements within any sort of statement are forbidden in strict mode code. +// Function statements within unbraced blocks are forbidden in strict mode code. +// They are allowed only under if statements in sloppy mode. assertEq(testLenientAndStrict("if (true) function f() { }", parsesSuccessfully, parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("while (true) function f() { }", - parsesSuccessfully, + parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("do function f() { } while (true);", - parsesSuccessfully, + parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("for(;;) function f() { }", - parsesSuccessfully, + parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("for(x in []) function f() { }", - parsesSuccessfully, + parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("with(o) function f() { }", - parsesSuccessfully, + parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("switch(1) { case 1: function f() { } }", parsesSuccessfully, - parseRaisesException(SyntaxError)), + parsesSuccessfully), true); assertEq(testLenientAndStrict("x: function f() { }", parsesSuccessfully, @@ -56,7 +51,7 @@ assertEq(testLenientAndStrict("x: function f() { }", true); assertEq(testLenientAndStrict("try { function f() { } } catch (x) { }", parsesSuccessfully, - parseRaisesException(SyntaxError)), + parsesSuccessfully), true); // Lambdas are always permitted within any sort of statement. @@ -69,7 +64,7 @@ assertEq(testLenientAndStrict("if (true) (function f() { })", assertEq(parsesSuccessfully("function f() { function g() { } }"), true); -// Function statements are permitted in any statement within lenient functions. +// Function statements are permitted in if statement within lenient functions. assertEq(parsesSuccessfully("function f() { if (true) function g() { } }"), true); @@ -77,8 +72,7 @@ assertEq(parseRaisesException(SyntaxError) ("function f() { 'use strict'; if (true) function g() { } }"), true); -assertEq(parseRaisesException(SyntaxError) - ("function f() { 'use strict'; { function g() { } } }"), +assertEq(parsesSuccessfully("function f() { 'use strict'; { function g() { } } }"), true); assertEq(parsesSuccessfully("function f() { 'use strict'; if (true) (function g() { }) }"), @@ -94,7 +88,7 @@ assertEq(testLenientAndStrict("function f() { }", true); assertEq(testLenientAndStrict("{ function f() { } }", completesNormally, - raisesException(SyntaxError)), + completesNormally), true); reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/runtime.js b/js/src/tests/ecma_6/Generators/runtime.js index 9c5c3198caf..9e09075cc2a 100644 --- a/js/src/tests/ecma_6/Generators/runtime.js +++ b/js/src/tests/ecma_6/Generators/runtime.js @@ -59,10 +59,12 @@ TestGeneratorFunctionPrototype(); // Functions that we associate with generator objects are actually defined by // a common prototype. function TestGeneratorObjectPrototype() { + // %GeneratorPrototype% must inherit from %IteratorPrototype%. + var iterProto = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())); assertEq(Object.getPrototypeOf(GeneratorObjectPrototype), - Object.prototype); + iterProto); assertEq(Object.getPrototypeOf((function*(){yield 1}).prototype), - GeneratorObjectPrototype); + GeneratorObjectPrototype); var expected_property_names = ["next", "return", "throw", "constructor"]; var found_property_names = @@ -72,6 +74,9 @@ function TestGeneratorObjectPrototype() { found_property_names.sort(); assertDeepEq(found_property_names, expected_property_names); + + // No symbol properties, at least until we have @@toStringTag. + assertEq(Object.getOwnPropertySymbols(GeneratorObjectPrototype).length, 0); } TestGeneratorObjectPrototype(); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-eval.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-eval.js new file mode 100644 index 00000000000..380ebba831d --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-eval.js @@ -0,0 +1,38 @@ +var log = ""; + +function f() { + log += g(); + function g() { return "outer-g"; } + + var o = { g: function () { return "with-g"; } }; + with (o) { + // Annex B.3.3.3 says g should be set on the nearest VariableEnvironment, + // and so should not change o.g. + eval(`{ + function g() { return "eval-g"; } + }`); + } + + log += g(); + log += o.g(); +} + +f(); + +function h() { + eval(` + // Should return true, as var bindings introduced by eval are configurable. + log += (delete q); + { + function q() { log += "q"; } + // Should return false, as lexical bindings introduced by eval are not + // configurable. + log += (delete q); + } + `); + return q; +} + +h()(); + +reportCompare(log, "outer-geval-gwith-gtruefalseq"); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-if.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-if.js new file mode 100644 index 00000000000..ce63aeeea2c --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-if.js @@ -0,0 +1,42 @@ +var log = ""; + +function f(x) { + if (x) + function g() { return "g0"; } + else + function g() { return "g1"; } + + log += g(); + + if (x) + function g() { return "g2"; } + else { + } + + log += g(); + + if (x) { + } else + function g() { return "g3"; } + + log += g(); + + if (x) + function g() { return "g4"; } + + log += g(); +} + +f(true); +f(false); + +try { + eval(` + if (1) + l: function foo() {} + `); +} catch (e) { + log += "e"; +} + +reportCompare(log, "g0g2g2g4g1g1g3g3e"); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-label.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-label.js new file mode 100644 index 00000000000..0fe9f45fca3 --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-label.js @@ -0,0 +1,43 @@ +function expectSyntaxError(str) { + var threwSyntaxError; + try { + eval(str); + } catch (e) { + threwSyntaxError = e instanceof SyntaxError; + } + assertEq(threwSyntaxError, true); + + try { + eval('"use strict";' + str); + } catch (e) { + threwSyntaxError = e instanceof SyntaxError; + } + assertEq(threwSyntaxError, true); +} + +function expectSloppyPass(str) { + eval(str); + + try { + eval('"use strict";' + str); + } catch (e) { + threwSyntaxError = e instanceof SyntaxError; + } + assertEq(threwSyntaxError, true); +} + +expectSloppyPass(`l: function f1() {}`); +expectSloppyPass(`l0: l: function f1() {}`); +expectSloppyPass(`{ f1(); l: function f1() {} }`); +expectSloppyPass(`{ f1(); l0: l: function f1() {} }`); +expectSloppyPass(`{ f1(); l: function f1() { return 42; } } assertEq(f1(), 42);`); +expectSloppyPass(`eval("fe(); l: function fe() {}")`); +expectSyntaxError(`if (1) l: function f2() {}`); +expectSyntaxError(`if (1) {} else l: function f3() {}`); +expectSyntaxError(`do l: function f4() {} while (0)`); +expectSyntaxError(`while (0) l: function f5() {}`); +expectSyntaxError(`for (;;) l: function f6() {}`); +expectSloppyPass(`switch (1) { case 1: l: function f7() {} }`); +expectSloppyPass(`switch (1) { case 1: assertEq(f8(), 'f8'); case 2: l: function f8() { return 'f8'; } } assertEq(f8(), 'f8');`); + +reportCompare(0, 0); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-property.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-property.js new file mode 100644 index 00000000000..a295de5081b --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-property.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!xulRuntime.shell) + +// Define a global getter without a setter. +Object.defineProperty(this, "x", { + get: function () { return "get-x"; }, + configurable: true +}); + +// Simulate loading a 2nd script with evaluate, else we would DEFVAR the x and +// the above defineProperty would fail in trying to redefine a non-configurable +// property on the global. +evaluate(`{ + function x() { return "fun-x"; } +}`); + +// Annex B is supposed to be like an assignment. Should not blow away the +// existing setter-less getter. +reportCompare(x, "get-x"); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-with.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-with.js new file mode 100644 index 00000000000..d9bdd98dc97 --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-with.js @@ -0,0 +1,18 @@ +var o = { f: "string-f" }; +with (o) { + var desc = Object.getOwnPropertyDescriptor(this, "f"); + assertEq(desc.value, undefined); + assertEq(desc.writable, true); + assertEq(desc.enumerable, true); + assertEq(desc.configurable, false); + function f() { + return "fun-f"; + } +} + +// Annex B explicitly assigns to the nearest VariableEnvironment, so the +// with-object "o" should have its property unchanged. +assertEq(o.f, "string-f"); +assertEq(f(), "fun-f"); + +reportCompare(true, true) diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b.js new file mode 100644 index 00000000000..16c40774cad --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b.js @@ -0,0 +1,31 @@ +var log = ""; + +log += typeof f; + +{ + log += f(); + + function f() { + return "f1"; + } +} + +log += f(); + +function g() { + log += typeof h; + + { + log += h(); + + function h() { + return "h1"; + } + } + + log += h(); +} + +g(); + +reportCompare(log, "undefinedf1f1undefinedh1h1"); diff --git a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-strict.js b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-strict.js new file mode 100644 index 00000000000..2b780d7dd1f --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-strict.js @@ -0,0 +1,45 @@ +"use strict" + +var log = ""; + +function f() { + return "f0"; +} + +log += f(); + +{ + log += f(); + + function f() { + return "f1"; + } + + log += f(); +} + +log += f(); + +function g() { + function h() { + return "h0"; + } + + log += h(); + + { + log += h(); + + function h() { + return "h1"; + } + + log += h(); + } + + log += h(); +} + +g(); + +reportCompare(log, "f0f1f1f0h0h1h1h0"); diff --git a/js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js b/js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js index ea2494bdab9..9f6fd6467c0 100644 --- a/js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js +++ b/js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js @@ -18,26 +18,30 @@ print(BUGNUMBER + ": " + summary); * BEGIN TEST * **************/ -for (let x = 0; x < 9; ++x) +for (let x = 0; x < 9; ++x) { function q1() {} +} { - for (let x = 0; x < 9; ++x) + for (let x = 0; x < 9; ++x) { function q2() {} + } } function f1() { - for (let x = 0; x < 9; ++x) + for (let x = 0; x < 9; ++x) { function q3() {} + } } f1(); function f2() { { - for (let x = 0; x < 9; ++x) + for (let x = 0; x < 9; ++x) { function q4() {} + } } } f2(); diff --git a/js/src/tests/js1_5/Regress/regress-326453.js b/js/src/tests/js1_5/Regress/regress-326453.js deleted file mode 100644 index c9e39f9d6cb..00000000000 --- a/js/src/tests/js1_5/Regress/regress-326453.js +++ /dev/null @@ -1,21 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Blake Kaplan - */ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 326453; -var summary = 'Do not assert: while decompiling'; -var actual = 'No Crash'; -var expect = 'No Crash'; - -printBugNumber(BUGNUMBER); -printStatus (summary); - -function f() { with({})function g() { }; printStatus(); } - -printStatus(f.toString()); - -reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-245795.js b/js/src/tests/js1_5/extensions/regress-245795.js index af77f541922..a823fc2f608 100644 --- a/js/src/tests/js1_5/extensions/regress-245795.js +++ b/js/src/tests/js1_5/extensions/regress-245795.js @@ -12,23 +12,20 @@ var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); -if (typeof uneval != 'undefined') +function a() { - function a() - { - b = function() {}; - } - - var r = "function a() { b = function() {}; }"; - eval(uneval(a)); - - var v = a.toString().replace(/[ \n]+/g, ' '); - print(v) - - printStatus("[" + v + "]"); - - expect = r; - actual = v; - - reportCompare(expect, actual, summary); + b = function() {}; } + +var r = "function a() { b = function() {}; }"; +eval(uneval(a)); + +var v = a.toString().replace(/[ \n]+/g, ' '); +print(v) + +printStatus("[" + v + "]"); + +expect = r; +actual = v; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/js1_5/extensions/regress-406572.js b/js/src/tests/js1_5/extensions/regress-406572.js index e93448c8d1a..4911d05bc7b 100644 --- a/js/src/tests/js1_5/extensions/regress-406572.js +++ b/js/src/tests/js1_5/extensions/regress-406572.js @@ -22,12 +22,14 @@ if (typeof window != 'undefined') window = 1; reportCompare(windowString, String(window), "window should be readonly"); - actual = ""; // We should reach this line, and throw an exception after it - if (1) function window() { return 1; } - actual = "FAIL: this line should never be reached"; + // We should reach this line without throwing. Annex B means the + // block-scoped function above gets an assignment to 'window' in the + // nearest 'var' environment, but since 'window' is read-only, the + // assignment silently fails. + actual = ""; // The test harness might rely on window having its original value: // restore it. diff --git a/js/src/tests/js1_8_5/reflect-parse/declarations.js b/js/src/tests/js1_8_5/reflect-parse/declarations.js index 86f524fd394..480f58777be 100644 --- a/js/src/tests/js1_8_5/reflect-parse/declarations.js +++ b/js/src/tests/js1_8_5/reflect-parse/declarations.js @@ -4,7 +4,7 @@ function test() { // Bug 632056: constant-folding program([exprStmt(ident("f")), ifStmt(lit(1), - funDecl(ident("f"), [], blockStmt([])), + blockStmt([funDecl(ident("f"), [], blockStmt([]))]), null)]).assert(Reflect.parse("f; if (1) function f(){}")); // declarations @@ -86,4 +86,12 @@ assertProg("f.p = 1; var f; f.p; function f(){}", funDecl(ident("f"), [], blockStmt([]))]); } +assertBlockStmt("{ function f(x) {} }", + blockStmt([funDecl(ident("f"), [ident("x")], blockStmt([]))])); + +// Annex B semantics should not change parse tree. +assertBlockStmt("{ let f; { function f(x) {} } }", + blockStmt([letDecl([{ id: ident("f"), init: null }]), + blockStmt([funDecl(ident("f"), [ident("x")], blockStmt([]))])])); + runtest(test); diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index a65b67b1321..51038591460 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -235,7 +235,6 @@ const Class StarGeneratorObject::class_ = { }; static const JSFunctionSpec star_generator_methods[] = { - JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0), JS_SELF_HOSTED_FN("next", "StarGeneratorNext", 1, 0), JS_SELF_HOSTED_FN("throw", "StarGeneratorThrow", 1, 0), JS_SELF_HOSTED_FN("return", "StarGeneratorReturn", 1, 0), @@ -296,8 +295,12 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle global) if (global->getReservedSlot(STAR_GENERATOR_OBJECT_PROTO).isObject()) return true; - RootedObject genObjectProto(cx, NewSingletonObjectWithObjectPrototype(cx, global)); - if (!genObjectProto || !genObjectProto->setDelegate(cx)) + RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global)); + if (!iteratorProto) + return false; + + RootedPlainObject genObjectProto(cx, NewObjectWithGivenProto(cx, iteratorProto)); + if (!genObjectProto) return false; if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr, star_generator_methods)) return false; diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index de2768d94f9..e19eced9879 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -96,6 +96,7 @@ class GlobalObject : public NativeObject /* One-off properties stored after slots for built-ins. */ LEXICAL_SCOPE, + ITERATOR_PROTO, ARRAY_ITERATOR_PROTO, STRING_ITERATOR_PROTO, LEGACY_GENERATOR_OBJECT_PROTO, @@ -453,7 +454,7 @@ class GlobalObject : public NativeObject TypedObjectModuleObject& getTypedObjectModule() const; - JSObject* getIteratorPrototype() { + JSObject* getLegacyIteratorPrototype() { return &getPrototype(JSProto_Iterator).toObject(); } @@ -509,13 +510,9 @@ class GlobalObject : public NativeObject } public: - static NativeObject* getOrCreateIteratorPrototype(JSContext* cx, - Handle global) + static NativeObject* getOrCreateIteratorPrototype(JSContext* cx, Handle global) { - if (!ensureConstructor(cx, global, JSProto_Iterator)) - return nullptr; - size_t slot = APPLICATION_SLOTS + JSProto_LIMIT + JSProto_Iterator; - return &global->getSlot(slot).toObject().as(); + return MaybeNativeObject(global->getOrCreateObject(cx, ITERATOR_PROTO, initIteratorProto)); } static NativeObject* getOrCreateArrayIteratorPrototype(JSContext* cx, Handle global) @@ -678,6 +675,7 @@ class GlobalObject : public NativeObject bool valueIsEval(Value val); // Implemented in jsiter.cpp. + static bool initIteratorProto(JSContext* cx, Handle global); static bool initArrayIteratorProto(JSContext* cx, Handle global); static bool initStringIteratorProto(JSContext* cx, Handle global); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index d913f30c9e2..385acfe0ab2 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1754,7 +1754,6 @@ CASE(JSOP_UNUSED209) CASE(JSOP_UNUSED210) CASE(JSOP_UNUSED211) CASE(JSOP_UNUSED212) -CASE(JSOP_UNUSED213) CASE(JSOP_UNUSED219) CASE(JSOP_UNUSED220) CASE(JSOP_UNUSED221) @@ -2090,6 +2089,12 @@ CASE(JSOP_BINDNAME) } END_CASE(JSOP_BINDNAME) +CASE(JSOP_BINDVAR) +{ + PUSH_OBJECT(REGS.fp()->varObj()); +} +END_CASE(JSOP_BINDVAR) + #define BITWISE_OP(OP) \ JS_BEGIN_MACRO \ int32_t i, j; \ diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 4cdf366a0c1..ca4f5bc0ebb 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -2045,7 +2045,15 @@ macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED211, 211, "unused211", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED212, 212, "unused212", NULL, 1, 0, 0, JOF_BYTE) \ - macro(JSOP_UNUSED213, 213, "unused213", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Pushes the nearest 'var' environment. + * + * Category: Variables and Scopes + * Type: Free Variables + * Operands: + * Stack: => scope + */ \ + macro(JSOP_BINDVAR, 213, "bindvar", NULL, 1, 0, 1, JOF_BYTE) \ /* * Pushes the global scope onto the stack if the script doesn't have a * non-syntactic global scope. Otherwise will act like JSOP_BINDNAME. diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index e349358690c..7236b4eb226 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,11 +29,11 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 326; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 327; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); -static_assert(JSErr_Limit == 422, +static_assert(JSErr_Limit == 425, "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 " diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index 950be005a06..73ddd935a16 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -40,6 +40,7 @@ #ifdef MOZ_WEBRTC #include "mozilla/dom/RTCIdentityProviderRegistrar.h" #endif +#include "mozilla/dom/FileReaderBinding.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/TextDecoderBinding.h" #include "mozilla/dom/TextEncoderBinding.h" @@ -892,6 +893,8 @@ xpc::GlobalProperties::Parse(JSContext* cx, JS::HandleObject obj) fetch = true; } else if (!strcmp(name.ptr(), "caches")) { caches = true; + } else if (!strcmp(name.ptr(), "FileReader")) { + fileReader = true; } else { JS_ReportError(cx, "Unknown property name: %s", name.ptr()); return false; @@ -960,6 +963,9 @@ xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj) if (caches && !dom::cache::CacheStorage::DefineCaches(cx, obj)) return false; + if (fileReader && !dom::FileReaderBinding::GetConstructorObject(cx, obj)) + return false; + return true; } diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index d210885d4de..e754c5bfec9 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -3424,6 +3424,7 @@ struct GlobalProperties { bool rtcIdentityProvider : 1; bool fetch : 1; bool caches : 1; + bool fileReader: 1; }; // Infallible. diff --git a/js/xpconnect/tests/mochitest/test_bug790732.html b/js/xpconnect/tests/mochitest/test_bug790732.html index 9f3a7d1a3c2..7938a3bd8bd 100644 --- a/js/xpconnect/tests/mochitest/test_bug790732.html +++ b/js/xpconnect/tests/mochitest/test_bug790732.html @@ -20,7 +20,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=790732 // constants for a couple of interfaces, and then once it's pretty clear that // it's working as intended we just check that the objects themselves are the // same. - is(Ci.nsIDOMFileReader.DONE, FileReader.DONE); is(Ci.nsIXMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.HEADERS_RECEIVED); is(Ci.nsIDOMDOMException.DATA_CLONE_ERR, DOMException.DATA_CLONE_ERR); is(Ci.nsIDOMNode.DOCUMENT_NODE, Node.DOCUMENT_NODE); diff --git a/js/xpconnect/tests/unit/test_fileReader.js b/js/xpconnect/tests/unit/test_fileReader.js new file mode 100644 index 00000000000..cd485930177 --- /dev/null +++ b/js/xpconnect/tests/unit/test_fileReader.js @@ -0,0 +1,13 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function run_test() { + var Cu = Components.utils; + var sb = new Cu.Sandbox('http://www.example.com', + { wantGlobalProperties: ["FileReader"] }); + sb.do_check_true = do_check_true; + Cu.evalInSandbox('do_check_true((new FileReader()) instanceof FileReader);', + sb); + Cu.importGlobalProperties(["FileReader"]); + do_check_true((new FileReader()) instanceof FileReader); +} diff --git a/js/xpconnect/tests/unit/xpcshell.ini b/js/xpconnect/tests/unit/xpcshell.ini index 6d931eb88e4..fb8ab4107e0 100644 --- a/js/xpconnect/tests/unit/xpcshell.ini +++ b/js/xpconnect/tests/unit/xpcshell.ini @@ -109,6 +109,7 @@ skip-if = os == "android" # native test components aren't available on Android [test_textDecoder.js] [test_url.js] [test_URLSearchParams.js] +[test_fileReader.js] [test_crypto.js] [test_css.js] [test_rtcIdentityProvider.js] diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index ca4568f8296..d0670f2a3b9 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -35,6 +35,7 @@ #include "nsSVGIntegrationUtils.h" #include "mozilla/LayerTimelineMarker.h" +#include "mozilla/EffectCompositor.h" #include "mozilla/Move.h" #include "mozilla/ReverseIterator.h" #include "mozilla/gfx/2D.h" @@ -5107,7 +5108,7 @@ ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder, // for animation if (aContainerItem && aContainerItem->GetType() == nsDisplayItem::TYPE_TRANSFORM && - nsLayoutUtils::HasAnimationsForCompositor( + EffectCompositor::HasAnimationsForCompositor( aContainerFrame, eCSSProperty_transform)) { // Use the size of the nearest widget as the maximum size. This // is important since it might be a popup that is bigger than the diff --git a/layout/base/nsBidi.h b/layout/base/nsBidi.h index dee1f3cea6d..1d8731b7c98 100644 --- a/layout/base/nsBidi.h +++ b/layout/base/nsBidi.h @@ -156,8 +156,11 @@ typedef uint8_t DirProp; #define DIRPROP_FLAG_MULTI_RUNS (1UL<<31) /* are there any characters that are LTR or RTL? */ -#define MASK_LTR (DIRPROP_FLAG(L)|DIRPROP_FLAG(EN)|DIRPROP_FLAG(AN)|DIRPROP_FLAG(LRE)|DIRPROP_FLAG(LRO)|DIRPROP_FLAG(LRI)) -#define MASK_RTL (DIRPROP_FLAG(R)|DIRPROP_FLAG(AL)|DIRPROP_FLAG(RLE)|DIRPROP_FLAG(RLO)|DIRPROP_FLAG(RLI)) +#define MASK_LTR (DIRPROP_FLAG(L)|DIRPROP_FLAG(EN)|DIRPROP_FLAG(ENL)| \ + DIRPROP_FLAG(ENR)|DIRPROP_FLAG(AN)|DIRPROP_FLAG(LRE)| \ + DIRPROP_FLAG(LRO)|DIRPROP_FLAG(LRI)) +#define MASK_RTL (DIRPROP_FLAG(R)|DIRPROP_FLAG(AL)|DIRPROP_FLAG(RLE)| \ + DIRPROP_FLAG(RLO)|DIRPROP_FLAG(RLI)) #define MASK_R_AL (DIRPROP_FLAG(R)|DIRPROP_FLAG(AL)) /* explicit embedding codes */ diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 3b6080c8b29..59ab57d5489 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -780,7 +780,8 @@ void nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsRect overflowRect = aFrame->GetVisualOverflowRect(); if (aFrame->IsTransformed() && - nsLayoutUtils::HasAnimationsForCompositor(aFrame, eCSSProperty_transform)) { + EffectCompositor::HasAnimationsForCompositor(aFrame, + eCSSProperty_transform)) { /** * Add a fuzz factor to the overflow rectangle so that elements only just * out of view are pulled into the display list, so they can be @@ -4076,7 +4077,8 @@ nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder* aBuilder) if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, eCSSProperty_opacity) && !IsItemTooSmallForActiveLayer(this)) return true; - if (nsLayoutUtils::HasAnimationsForCompositor(mFrame, eCSSProperty_opacity)) { + if (EffectCompositor::HasAnimationsForCompositor(mFrame, + eCSSProperty_opacity)) { return true; } return false; @@ -5434,7 +5436,8 @@ nsDisplayTransform::ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBui // might have only just had its transform animated in which case // the ActiveLayerManager may not have been notified yet. if (!ActiveLayerTracker::IsStyleMaybeAnimated(aFrame, eCSSProperty_transform) && - !nsLayoutUtils::HasAnimationsForCompositor(aFrame, eCSSProperty_transform)) { + !EffectCompositor::HasAnimationsForCompositor(aFrame, + eCSSProperty_transform)) { if (aLogAnimations) { nsCString message; message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for transform animation"); @@ -5634,7 +5637,8 @@ nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder, if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, eCSSProperty_transform) && !IsItemTooSmallForActiveLayer(this)) return LAYER_ACTIVE; - if (nsLayoutUtils::HasAnimationsForCompositor(mFrame, eCSSProperty_transform)) { + if (EffectCompositor::HasAnimationsForCompositor(mFrame, + eCSSProperty_transform)) { return LAYER_ACTIVE; } diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index cb893d44c2b..0fca2a2b131 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -370,16 +370,6 @@ FloatLogicalValuesEnabledPrefChangeCallback(const char* aPrefName, isFloatLogicalValuesEnabled ? eCSSKeyword_inline_end : eCSSKeyword_UNKNOWN; } -bool -nsLayoutUtils::HasAnimationsForCompositor(const nsIFrame* aFrame, - nsCSSProperty aProperty) -{ - // XXX Bug 1230056 - Add EffectCompositor::HasAnimationsForCompositor to - // avoid allocating an array here only to throw it away. - return !EffectCompositor::GetAnimationsForCompositor(aFrame, - aProperty).IsEmpty(); -} - template static bool HasMatchingCurrentAnimations(const nsIFrame* aFrame, TestType&& aTest) diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 7fd15bb91f4..06fa26df6f8 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -2208,13 +2208,6 @@ public: nsIContent* &aContentResult, nsCSSPseudoElements::Type &aPseudoTypeResult); - /** - * Returns true if the frame has animations or transitions that can be - * performed on the compositor. - */ - static bool HasAnimationsForCompositor(const nsIFrame* aFrame, - nsCSSProperty aProperty); - /** * Returns true if the frame has current (i.e. running or scheduled-to-run) * animations or transitions for the property. diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 94d58fc7098..6d5ae658ea3 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -67,9 +67,6 @@ // view stuff #include "nsContentCreatorFunctions.h" -// DOM includes -#include "nsDOMFileReader.h" - #include "nsFormData.h" #include "nsHostObjectProtocolHandler.h" #include "nsHostObjectURI.h" @@ -301,7 +298,6 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(XPathEvaluator) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(txNodeSetAdaptor, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMSerializer) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsXMLHttpRequest, Init) -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDOMFileReader, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(nsFormData) NS_GENERIC_FACTORY_CONSTRUCTOR(nsBlobProtocolHandler) NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaStreamProtocolHandler) @@ -751,7 +747,6 @@ NS_DEFINE_NAMED_CID(TRANSFORMIIX_XSLT_PROCESSOR_CID); NS_DEFINE_NAMED_CID(TRANSFORMIIX_XPATH_EVALUATOR_CID); NS_DEFINE_NAMED_CID(TRANSFORMIIX_NODESET_CID); NS_DEFINE_NAMED_CID(NS_XMLSERIALIZER_CID); -NS_DEFINE_NAMED_CID(NS_FILEREADER_CID); NS_DEFINE_NAMED_CID(NS_FORMDATA_CID); NS_DEFINE_NAMED_CID(NS_BLOBPROTOCOLHANDLER_CID); NS_DEFINE_NAMED_CID(NS_MEDIASTREAMPROTOCOLHANDLER_CID); @@ -1059,7 +1054,6 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = { { &kTRANSFORMIIX_XPATH_EVALUATOR_CID, false, nullptr, XPathEvaluatorConstructor }, { &kTRANSFORMIIX_NODESET_CID, false, nullptr, txNodeSetAdaptorConstructor }, { &kNS_XMLSERIALIZER_CID, false, nullptr, nsDOMSerializerConstructor }, - { &kNS_FILEREADER_CID, false, nullptr, nsDOMFileReaderConstructor }, { &kNS_FORMDATA_CID, false, nullptr, nsFormDataConstructor }, { &kNS_BLOBPROTOCOLHANDLER_CID, false, nullptr, nsBlobProtocolHandlerConstructor }, { &kNS_MEDIASTREAMPROTOCOLHANDLER_CID, false, nullptr, nsMediaStreamProtocolHandlerConstructor }, @@ -1227,7 +1221,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { { NS_XPATH_EVALUATOR_CONTRACTID, &kTRANSFORMIIX_XPATH_EVALUATOR_CID }, { TRANSFORMIIX_NODESET_CONTRACTID, &kTRANSFORMIIX_NODESET_CID }, { NS_XMLSERIALIZER_CONTRACTID, &kNS_XMLSERIALIZER_CID }, - { NS_FILEREADER_CONTRACTID, &kNS_FILEREADER_CID }, { NS_FORMDATA_CONTRACTID, &kNS_FORMDATA_CID }, { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX BLOBURI_SCHEME, &kNS_BLOBPROTOCOLHANDLER_CID }, { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX MEDIASTREAMURI_SCHEME, &kNS_MEDIASTREAMPROTOCOLHANDLER_CID }, diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index f8faf82afca..d551dfe569f 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -87,6 +87,7 @@ #include "nsIFrameInlines.h" #include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/EffectCompositor.h" #include "mozilla/EventListenerManager.h" #include "mozilla/EventStateManager.h" #include "mozilla/EventStates.h" @@ -1083,8 +1084,8 @@ nsIFrame::IsTransformed() const (StyleDisplay()->HasTransform(this) || IsSVGTransformed() || (mContent && - nsLayoutUtils::HasAnimationsForCompositor(this, - eCSSProperty_transform) && + EffectCompositor::HasAnimationsForCompositor( + this, eCSSProperty_transform) && IsFrameOfType(eSupportsCSSTransforms) && mContent->GetPrimaryFrame() == this))); } @@ -1097,8 +1098,8 @@ nsIFrame::HasOpacityInternal(float aThreshold) const return StyleDisplay()->mOpacity < aThreshold || (displayStyle->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) || (mContent && - nsLayoutUtils::HasAnimationsForCompositor(this, - eCSSProperty_opacity) && + EffectCompositor::HasAnimationsForCompositor( + this, eCSSProperty_opacity) && mContent->GetPrimaryFrame() == this); } diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 6323743cb22..a2ece470769 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -2121,10 +2121,30 @@ ScrollFrameHelper::ScrollToWithOrigin(nsPoint aScrollPosition, } if (nsLayoutUtils::AsyncPanZoomEnabled(mOuter)) { + if (mApzSmoothScrollDestination == mDestination && + mScrollGeneration == sScrollGenerationCounter) { + // If we already sent APZ a smooth-scroll request to this + // destination with this generation (i.e. it was the last request + // we sent), then don't send another one because it is redundant. + // This is to avoid a scenario where pages do repeated scrollBy + // calls, incrementing the generation counter, and blocking APZ from + // syncing the scroll offset back to the main thread. + // Note that if we get two smooth-scroll requests to the same + // destination with some other scroll in between, mDestination will + // get reset and so we shouldn't have the problem where this check + // discards a legitimate smooth-scroll. + // Note: if there are two separate scrollframes both getting smooth + // scrolled at the same time, sScrollGenerationCounter can get + // incremented and this early-exit won't get taken. Bug 1231177 is + // on file for this. + return; + } + // The animation will be handled in the compositor, pass the // information needed to start the animation and skip the main-thread // animation for this scroll. mLastSmoothScrollOrigin = aOrigin; + mApzSmoothScrollDestination = mDestination; mScrollGeneration = ++sScrollGenerationCounter; if (!nsLayoutUtils::GetDisplayPort(mOuter->GetContent())) { diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index 4c3093ef250..e048980dea4 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -432,6 +432,7 @@ public: nsTArray mListeners; nsIAtom* mLastScrollOrigin; nsIAtom* mLastSmoothScrollOrigin; + nsPoint mApzSmoothScrollDestination; uint32_t mScrollGeneration; nsRect mScrollPort; // Where we're currently scrolling to, if we're scrolling asynchronously. diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index d172cc939b2..323e1aeb3d6 100644 --- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -29,7 +29,7 @@ using namespace mozilla; typedef nsGridContainerFrame::TrackSize TrackSize; const uint32_t nsGridContainerFrame::kTranslatedMaxLine = - uint32_t(nsStyleGridLine::kMaxLine - nsStyleGridLine::kMinLine - 1); + uint32_t(nsStyleGridLine::kMaxLine - nsStyleGridLine::kMinLine); const uint32_t nsGridContainerFrame::kAutoLine = kTranslatedMaxLine + 3457U; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(TrackSize::StateBits) @@ -813,16 +813,29 @@ IsNameWithStartSuffix(const nsString& aString, uint32_t* aIndex) return IsNameWithSuffix(aString, NS_LITERAL_STRING("-start"), aIndex); } +enum class GridLineSide { + eBeforeGridGap, + eAfterGridGap, +}; + static nscoord -GridLinePosition(uint32_t aLine, const nsTArray& aTrackSizes) +GridLineEdge(uint32_t aLine, const nsTArray& aTrackSizes, + GridLineSide aSide) { - if (aTrackSizes.Length() == 0) { + if (MOZ_UNLIKELY(aTrackSizes.IsEmpty())) { // https://drafts.csswg.org/css-grid/#grid-definition // "... the explicit grid still contains one grid line in each axis." MOZ_ASSERT(aLine == 0, "We should only resolve line 1 in an empty grid"); return nscoord(0); } MOZ_ASSERT(aLine <= aTrackSizes.Length(), "aTrackSizes is too small"); + if (aSide == GridLineSide::eBeforeGridGap) { + if (aLine == 0) { + return aTrackSizes[0].mPosition; + } + const TrackSize& sz = aTrackSizes[aLine - 1]; + return sz.mPosition + sz.mBase; + } if (aLine == aTrackSizes.Length()) { const TrackSize& sz = aTrackSizes[aLine - 1]; return sz.mPosition + sz.mBase; @@ -1581,7 +1594,7 @@ nsGridContainerFrame::PlaceAutoCol(uint32_t aStartCol, GridArea* aArea) const { MOZ_ASSERT(aArea->mRows.IsDefinite() && aArea->mCols.IsAuto()); uint32_t col = FindAutoCol(aStartCol, aArea->mRows.mStart, aArea); - aArea->mCols.ResolveAutoPosition(col); + aArea->mCols.ResolveAutoPosition(col, mExplicitGridOffsetCol); MOZ_ASSERT(aArea->IsDefinite()); } @@ -1619,7 +1632,7 @@ nsGridContainerFrame::PlaceAutoRow(uint32_t aStartRow, GridArea* aArea) const { MOZ_ASSERT(aArea->mCols.IsDefinite() && aArea->mRows.IsAuto()); uint32_t row = FindAutoRow(aArea->mCols.mStart, aStartRow, aArea); - aArea->mRows.ResolveAutoPosition(row); + aArea->mRows.ResolveAutoPosition(row, mExplicitGridOffsetRow); MOZ_ASSERT(aArea->IsDefinite()); } @@ -1643,8 +1656,8 @@ nsGridContainerFrame::PlaceAutoAutoInRowOrder(uint32_t aStartCol, } MOZ_ASSERT(row < gridRowEnd || col == 0, "expected column 0 for placing in a new row"); - aArea->mCols.ResolveAutoPosition(col); - aArea->mRows.ResolveAutoPosition(row); + aArea->mCols.ResolveAutoPosition(col, mExplicitGridOffsetCol); + aArea->mRows.ResolveAutoPosition(row, mExplicitGridOffsetRow); MOZ_ASSERT(aArea->IsDefinite()); } @@ -1668,8 +1681,8 @@ nsGridContainerFrame::PlaceAutoAutoInColOrder(uint32_t aStartCol, } MOZ_ASSERT(col < gridColEnd || row == 0, "expected row 0 for placing in a new column"); - aArea->mCols.ResolveAutoPosition(col); - aArea->mRows.ResolveAutoPosition(row); + aArea->mCols.ResolveAutoPosition(col, mExplicitGridOffsetCol); + aArea->mRows.ResolveAutoPosition(row, mExplicitGridOffsetRow); MOZ_ASSERT(aArea->IsDefinite()); } @@ -2784,13 +2797,15 @@ nsGridContainerFrame::LineRange::ToPositionAndLengthForAbsPos( // done } else { const nscoord endPos = *aPos + *aLength; - nscoord startPos = ::GridLinePosition(mStart, aTrackSizes); + nscoord startPos = + ::GridLineEdge(mStart, aTrackSizes, GridLineSide::eAfterGridGap); *aPos = aGridOrigin + startPos; *aLength = std::max(endPos - *aPos, 0); } } else { if (mStart == kAutoLine) { - nscoord endPos = ::GridLinePosition(mEnd, aTrackSizes); + nscoord endPos = + ::GridLineEdge(mEnd, aTrackSizes, GridLineSide::eBeforeGridGap); *aLength = std::max(aGridOrigin + endPos, 0); } else { nscoord pos; @@ -2998,6 +3013,21 @@ nsGridContainerFrame::Reflow(nsPresContext* aPresContext, LogicalSize(wm, computedISize, computedBSize), nsLayoutUtils::PREF_ISIZE); + // FIXME bug 1229180: Instead of doing this on every reflow, we should only + // set these properties if they are needed. + nsTArray colTrackSizes(gridReflowState.mCols.mSizes.Length()); + for (const TrackSize& sz : gridReflowState.mCols.mSizes) { + colTrackSizes.AppendElement(sz.mBase); + } + Properties().Set(GridColTrackSizes(), + new nsTArray(mozilla::Move(colTrackSizes))); + nsTArray rowTrackSizes(gridReflowState.mRows.mSizes.Length()); + for (const TrackSize& sz : gridReflowState.mRows.mSizes) { + rowTrackSizes.AppendElement(sz.mBase); + } + Properties().Set(GridRowTrackSizes(), + new nsTArray(mozilla::Move(rowTrackSizes))); + nscoord bSize = 0; if (computedBSize == NS_AUTOHEIGHT) { for (uint32_t i = 0; i < mGridRowEnd; ++i) { diff --git a/layout/generic/nsGridContainerFrame.h b/layout/generic/nsGridContainerFrame.h index 3113037b18f..261c8348783 100644 --- a/layout/generic/nsGridContainerFrame.h +++ b/layout/generic/nsGridContainerFrame.h @@ -91,6 +91,20 @@ public: NS_DECLARE_FRAME_PROPERTY(GridItemContainingBlockRect, DeleteValue) + NS_DECLARE_FRAME_PROPERTY(GridColTrackSizes, DeleteValue>) + + const nsTArray* GetComputedTemplateColumns() + { + return static_cast*>(Properties().Get(GridColTrackSizes())); + } + + NS_DECLARE_FRAME_PROPERTY(GridRowTrackSizes, DeleteValue>) + + const nsTArray* GetComputedTemplateRows() + { + return static_cast*>(Properties().Get(GridRowTrackSizes())); + } + protected: static const uint32_t kAutoLine; // The maximum line number, in the zero-based translated grid. @@ -161,17 +175,19 @@ protected: * Resolve this auto range to start at aStart, making it definite. * Precondition: this range IsAuto() */ - void ResolveAutoPosition(uint32_t aStart) + void ResolveAutoPosition(uint32_t aStart, uint32_t aExplicitGridOffset) { MOZ_ASSERT(IsAuto(), "Why call me?"); mStart = aStart; mEnd += aStart; - // Clamping per http://dev.w3.org/csswg/css-grid/#overlarge-grids : - if (MOZ_UNLIKELY(mStart >= kTranslatedMaxLine)) { - mEnd = kTranslatedMaxLine; + // Clamping to where kMaxLine is in the explicit grid, per + // http://dev.w3.org/csswg/css-grid/#overlarge-grids : + uint32_t translatedMax = aExplicitGridOffset + nsStyleGridLine::kMaxLine; + if (MOZ_UNLIKELY(mStart >= translatedMax)) { + mEnd = translatedMax; mStart = mEnd - 1; - } else if (MOZ_UNLIKELY(mEnd > kTranslatedMaxLine)) { - mEnd = kTranslatedMaxLine; + } else if (MOZ_UNLIKELY(mEnd > translatedMax)) { + mEnd = translatedMax; } } /** diff --git a/layout/reftests/bidi/1231175-1-ref.html b/layout/reftests/bidi/1231175-1-ref.html new file mode 100644 index 00000000000..b6238cf6be9 --- /dev/null +++ b/layout/reftests/bidi/1231175-1-ref.html @@ -0,0 +1,17 @@ + + + + +

The number in the red text should read "18" in all cases. +

+
    +
  1. 81 ديسمبر
  2. +
  3. 81 ديسمبر
  4. +
  5. 81 ديسمبر
  6. +
  7. 81 ديسمبر
  8. +
  9. 81 ديسمبر
  10. +
  11. 81 ديسمبر
  12. +
  13. 81 ديسمبر
  14. diff --git a/layout/reftests/bidi/1231175-1.html b/layout/reftests/bidi/1231175-1.html new file mode 100644 index 00000000000..d8f6d22a3dd --- /dev/null +++ b/layout/reftests/bidi/1231175-1.html @@ -0,0 +1,17 @@ + + + + +

    The number in the red text should read "18" in all cases. +

    +
      +
    1. 18 ديسمبر
    2. +
    3. 18 ديسمبر
    4. +
    5. 18 ديسمبر
    6. +
    7. 18 ديسمبر
    8. +
    9. ‫18 ديسمبر‬
    10. +
    11. ⁧18 ديسمبر⁩
    12. +
    13. ⁨18 ديسمبر⁩
    14. diff --git a/layout/reftests/bidi/reftest.list b/layout/reftests/bidi/reftest.list index 2b6970fb7e1..204acd14bcf 100644 --- a/layout/reftests/bidi/reftest.list +++ b/layout/reftests/bidi/reftest.list @@ -172,3 +172,4 @@ fuzzy-if(Android,254,231) == brackets-2c-rtl.html brackets-2c-rtl-ref.html == brackets-3b-rtl.html brackets-3b-rtl-ref.html == 1217833-1.html 1217833-1-ref.html == 1217833-2.html 1217833-2-ref.html +== 1231175-1.html 1231175-1-ref.html diff --git a/layout/reftests/css-grid/grid-abspos-items-013-ref.html b/layout/reftests/css-grid/grid-abspos-items-013-ref.html new file mode 100644 index 00000000000..56269a19947 --- /dev/null +++ b/layout/reftests/css-grid/grid-abspos-items-013-ref.html @@ -0,0 +1,113 @@ + + + + + Reference: grid abs.pos. child in grid with gutters + + + + + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + + + + diff --git a/layout/reftests/css-grid/grid-abspos-items-013.html b/layout/reftests/css-grid/grid-abspos-items-013.html new file mode 100644 index 00000000000..5297fecb29b --- /dev/null +++ b/layout/reftests/css-grid/grid-abspos-items-013.html @@ -0,0 +1,101 @@ + + + + + CSS Grid Test: grid abs.pos. child in grid with gutters + + + + + + + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + + + + diff --git a/layout/reftests/css-grid/grid-clamping-001-ref.html b/layout/reftests/css-grid/grid-clamping-001-ref.html new file mode 100644 index 00000000000..2e2b40e0512 --- /dev/null +++ b/layout/reftests/css-grid/grid-clamping-001-ref.html @@ -0,0 +1,68 @@ + + + + Reference: bug 1229999 + + + + +
      + + + + + + +
      + +
      + + + + + +
      + +
      + + + + +
      + +
      + + + + +
      + +
      + + + + +
      + + + + diff --git a/layout/reftests/css-grid/grid-clamping-001.html b/layout/reftests/css-grid/grid-clamping-001.html new file mode 100644 index 00000000000..7d96b2e8525 --- /dev/null +++ b/layout/reftests/css-grid/grid-clamping-001.html @@ -0,0 +1,78 @@ + + + + Testcase for bug 1229999 + + + + +
      + + + + + +
      + +
      + + + + + +
      + +
      + + + + + +
      + +
      + + + + + +
      + +
      + + + + + +
      + +
      + + + + +
      + + + + diff --git a/layout/reftests/css-grid/grid-placement-auto-implicit-001-ref.html b/layout/reftests/css-grid/grid-placement-auto-implicit-001-ref.html index 6d97fbb3706..9395b35cdf4 100644 --- a/layout/reftests/css-grid/grid-placement-auto-implicit-001-ref.html +++ b/layout/reftests/css-grid/grid-placement-auto-implicit-001-ref.html @@ -80,17 +80,17 @@ span {
      -b
      +b
      -b
      +b
      -b +b
      @@ -99,7 +99,7 @@ span {
      -b +b
    diff --git a/layout/reftests/css-grid/reftest.list b/layout/reftests/css-grid/reftest.list index ed94d3ee3ba..6fb94eaf8e0 100644 --- a/layout/reftests/css-grid/reftest.list +++ b/layout/reftests/css-grid/reftest.list @@ -26,6 +26,7 @@ fails == grid-whitespace-handling-1b.xhtml grid-whitespace-handling-1-ref.xhtml == grid-abspos-items-010.html grid-abspos-items-010-ref.html == grid-abspos-items-011.html grid-abspos-items-011-ref.html == grid-abspos-items-012.html grid-abspos-items-012-ref.html +== grid-abspos-items-013.html grid-abspos-items-013-ref.html == grid-order-abspos-items-001.html grid-order-abspos-items-001-ref.html == grid-order-placement-auto-001.html grid-order-placement-auto-001-ref.html == grid-order-placement-definite-001.html grid-order-placement-definite-001-ref.html @@ -94,3 +95,4 @@ fuzzy-if(winWidget,1,36) == grid-auto-min-sizing-definite-001.html grid-auto-min == grid-item-margin-right-auto-003.html grid-item-margin-right-auto-003-ref.html == grid-item-margin-right-auto-004.html grid-item-margin-right-auto-004-ref.html == grid-container-min-max-width-height-001.html grid-container-min-max-width-height-001-ref.html +== grid-clamping-001.html grid-clamping-001-ref.html diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index f70e9e61b8d..cb53785cd47 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -10316,7 +10316,7 @@ IsWebkitGradientCoordLarger(const nsCSSValue& aStartCoord, // // Note: linear gradients progress along a line between two points. The // -webkit-gradient(linear, ...) syntax lets the author precisely specify the -// starting and ending point point. However, our internal gradient structures +// starting and ending point. However, our internal gradient structures // only store one point, and the other point is implicitly its reflection // across the painted area's center. (The legacy -moz-linear-gradient syntax // also lets us store an angle.) diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index a0c3f24ab84..40fbe7d8d4a 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -25,6 +25,7 @@ #include "nsDOMCSSRGBColor.h" #include "nsDOMCSSValueList.h" #include "nsFlexContainerFrame.h" +#include "nsGridContainerFrame.h" #include "nsGkAtoms.h" #include "nsHTMLReflowState.h" #include "nsStyleUtil.h" @@ -2378,10 +2379,6 @@ CSSValue* nsComputedDOMStyle::GetGridTrackSize(const nsStyleCoord& aMinValue, const nsStyleCoord& aMaxValue) { - // FIXME bug 978212: for grid-template-columns and grid-template-rows - // (not grid-auto-columns and grid-auto-rows), if we have frame, - // every should be resolved into 'px' here, - // based on layout results. if (aMinValue == aMaxValue) { nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue; SetValueToCoord(val, aMinValue, true, @@ -2411,7 +2408,8 @@ nsComputedDOMStyle::GetGridTrackSize(const nsStyleCoord& aMinValue, } CSSValue* -nsComputedDOMStyle::GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrackList) +nsComputedDOMStyle::GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrackList, + const nsTArray* aTrackSizes) { if (aTrackList.mIsSubgrid) { NS_ASSERTION(aTrackList.mMinTrackSizingFunctions.IsEmpty() && @@ -2444,16 +2442,31 @@ nsComputedDOMStyle::GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrack // one before each track, plus one at the very end. MOZ_ASSERT(aTrackList.mLineNameLists.Length() == numSizes + 1, "Unexpected number of line name lists"); - for (uint32_t i = 0;; i++) { - const nsTArray& lineNames = aTrackList.mLineNameLists[i]; - if (!lineNames.IsEmpty()) { - valueList->AppendCSSValue(GetGridLineNames(lineNames)); + if (aTrackSizes) { + for (uint32_t i = 0;; i++) { + const nsTArray& lineNames = aTrackList.mLineNameLists[i]; + if (!lineNames.IsEmpty()) { + valueList->AppendCSSValue(GetGridLineNames(lineNames)); + } + if (i == numSizes) { + break; + } + nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue; + val->SetAppUnits(aTrackSizes->ElementAt(i)); + valueList->AppendCSSValue(val); } - if (i == numSizes) { - break; + } else { + for (uint32_t i = 0;; i++) { + const nsTArray& lineNames = aTrackList.mLineNameLists[i]; + if (!lineNames.IsEmpty()) { + valueList->AppendCSSValue(GetGridLineNames(lineNames)); + } + if (i == numSizes) { + break; + } + valueList->AppendCSSValue(GetGridTrackSize(aTrackList.mMinTrackSizingFunctions[i], + aTrackList.mMaxTrackSizingFunctions[i])); } - valueList->AppendCSSValue(GetGridTrackSize(aTrackList.mMinTrackSizingFunctions[i], - aTrackList.mMaxTrackSizingFunctions[i])); } return valueList; @@ -2490,13 +2503,31 @@ nsComputedDOMStyle::DoGetGridAutoRows() CSSValue* nsComputedDOMStyle::DoGetGridTemplateColumns() { - return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateColumns); + const nsTArray* trackSizes = nullptr; + if (mInnerFrame) { + nsIFrame* gridContainerCandidate = mInnerFrame->GetContentInsertionFrame(); + if (gridContainerCandidate && + gridContainerCandidate->GetType() == nsGkAtoms::gridContainerFrame) { + auto gridContainer = static_cast(gridContainerCandidate); + trackSizes = gridContainer->GetComputedTemplateColumns(); + } + } + return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateColumns, trackSizes); } CSSValue* nsComputedDOMStyle::DoGetGridTemplateRows() { - return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateRows); + const nsTArray* trackSizes = nullptr; + if (mInnerFrame) { + nsIFrame* gridContainerCandidate = mInnerFrame->GetContentInsertionFrame(); + if (gridContainerCandidate && + gridContainerCandidate->GetType() == nsGkAtoms::gridContainerFrame) { + auto gridContainer = static_cast(gridContainerCandidate); + trackSizes = gridContainer->GetComputedTemplateRows(); + } + } + return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateRows, trackSizes); } CSSValue* diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index fef3bd73d3e..ce7a6d02b4b 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -179,7 +179,8 @@ private: mozilla::dom::CSSValue* GetGridLineNames(const nsTArray& aLineNames); mozilla::dom::CSSValue* GetGridTrackSize(const nsStyleCoord& aMinSize, const nsStyleCoord& aMaxSize); - mozilla::dom::CSSValue* GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrackList); + mozilla::dom::CSSValue* GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrackList, + const nsTArray* aTrackSizes); mozilla::dom::CSSValue* GetGridLine(const nsStyleGridLine& aGridLine); bool GetLineHeightCoord(nscoord& aCoord); diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini index 4edf924c58d..c45ecde6578 100644 --- a/layout/style/test/mochitest.ini +++ b/layout/style/test/mochitest.ini @@ -181,6 +181,7 @@ skip-if = (toolkit == 'gonk' && debug) # Bug 1186440 [test_grid_container_shorthands.html] [test_grid_item_shorthands.html] [test_grid_shorthand_serialization.html] +[test_grid_computed_values.html] [test_group_insertRule.html] [test_hover_quirk.html] [test_html_attribute_computed_values.html] diff --git a/layout/style/test/test_grid_computed_values.html b/layout/style/test/test_grid_computed_values.html new file mode 100644 index 00000000000..0ee0689a08e --- /dev/null +++ b/layout/style/test/test_grid_computed_values.html @@ -0,0 +1,83 @@ + + + + + Test computed grid values + + + + + + + + +
    +
    +
    +
    +
    +
    + + + + diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index 6637def808a..43742bffba1 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -652,10 +652,10 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver, MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aThread); -#ifndef MOZILLA_EXTERNAL_LINKAGE - mThread = do_QueryInterface(aThread); -#endif - MOZ_ASSERT(mThread); + if (!mThread) { + mThread = do_QueryInterface(aThread); + MOZ_ASSERT(mThread); + } CheckThread(); mPCObserver = do_GetWeakReference(&aObserver); diff --git a/media/webrtc/trunk/webrtc/base/thread_checker_impl.cc b/media/webrtc/trunk/webrtc/base/thread_checker_impl.cc index 75ceb3646d0..95638ea49cf 100644 --- a/media/webrtc/trunk/webrtc/base/thread_checker_impl.cc +++ b/media/webrtc/trunk/webrtc/base/thread_checker_impl.cc @@ -40,18 +40,18 @@ PlatformThreadId CurrentThreadId() { #elif defined(WEBRTC_ANDROID) ret = gettid(); #elif defined(__NetBSD__) - return _lwp_self(); + ret = _lwp_self(); #elif defined(__DragonFly__) - return lwp_gettid(); + ret = lwp_gettid(); #elif defined(__OpenBSD__) - return reinterpret_cast (pthread_self()); + ret = reinterpret_cast (pthread_self()); #elif defined(__FreeBSD__) #if __FreeBSD_version > 900030 - return pthread_getthreadid_np(); + ret = pthread_getthreadid_np(); #else long lwpid; thr_self(&lwpid); - return lwpid; + ret = lwpid; #endif #else // Default implementation for nacl and solaris. diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 456bf93bca4..ab4fc879e7b 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1835,15 +1835,15 @@ pref("network.proxy.failover_timeout", 1800); // 30 minutes pref("network.proxy.use_direct_on_fail", true); pref("network.online", true); //online/offline -pref("network.cookie.cookieBehavior", 0); // 0-Accept, 1-dontAcceptForeign, 2-dontUse, 3-limitForeign +pref("network.cookie.cookieBehavior", 0); // 0-Accept, 1-dontAcceptForeign, 2-dontAcceptAny, 3-limitForeign #ifdef ANDROID pref("network.cookie.cookieBehavior", 0); // Keep the old default of accepting all cookies #endif pref("network.cookie.thirdparty.sessionOnly", false); -pref("network.cookie.lifetimePolicy", 0); // 0-accept, 2-acceptForSession, 3-acceptForNDays +pref("network.cookie.lifetimePolicy", 0); // 0-accept, 1-dontUse 2-acceptForSession, 3-acceptForNDays pref("network.cookie.alwaysAcceptSessionCookies", false); pref("network.cookie.prefsMigrated", false); -pref("network.cookie.lifetime.days", 90); +pref("network.cookie.lifetime.days", 90); // Ignored unless network.cookie.lifetimePolicy is 3. // The PAC file to load. Ignored unless network.proxy.type is 2. pref("network.proxy.autoconfig_url", ""); diff --git a/netwerk/cache2/CacheStorageService.cpp b/netwerk/cache2/CacheStorageService.cpp index bb506fbfa2f..99893d3bcbf 100644 --- a/netwerk/cache2/CacheStorageService.cpp +++ b/netwerk/cache2/CacheStorageService.cpp @@ -1746,8 +1746,7 @@ CacheStorageService::DoomStorageEntries(nsCSubstring const& aContextKey, sGlobalEntryTables->RemoveAndForget(memoryStorageID, memoryEntries); CacheEntryTable* diskEntries; - sGlobalEntryTables->Get(aContextKey, &diskEntries); - if (memoryEntries && diskEntries) { + if (memoryEntries && sGlobalEntryTables->Get(aContextKey, &diskEntries)) { for (auto iter = memoryEntries->Iter(); !iter.Done(); iter.Next()) { auto entry = iter.Data(); RemoveExactEntry(diskEntries, iter.Key(), entry, false); diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index 9dd4d284822..1ae60f4f66d 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -3031,6 +3031,8 @@ nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport, nsIAsyncOutputStream **outstream, bool isBackup) { + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); + nsresult rv; const char *socketTypes[1]; uint32_t typeCount = 0; @@ -3134,6 +3136,10 @@ nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport, rv = socketTransport->SetSecurityCallbacks(this); NS_ENSURE_SUCCESS(rv, rv); + Telemetry::Accumulate(Telemetry::HTTP_CONNECTION_ENTRY_CACHE_HIT_1, + mEnt->mUsedForConnection); + mEnt->mUsedForConnection = true; + nsCOMPtr sout; rv = socketTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, @@ -3592,6 +3598,7 @@ nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci) , mInPreferredHash(false) , mPreferIPv4(false) , mPreferIPv6(false) + , mUsedForConnection(false) { MOZ_COUNT_CTOR(nsConnectionEntry); if (gHttpHandler->GetPipelineAggressive()) { diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h index c31656cb0cb..368e7509489 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.h +++ b/netwerk/protocol/http/nsHttpConnectionMgr.h @@ -369,15 +369,15 @@ private: // To have the UsingSpdy flag means some host with the same connection // entry has done NPN=spdy/* at some point. It does not mean every // connection is currently using spdy. - bool mUsingSpdy; + bool mUsingSpdy : 1; // mTestedSpdy is set after NPN negotiation has occurred and we know // with confidence whether a host speaks spdy or not (which is reflected // in mUsingSpdy). Before mTestedSpdy is set, handshake parallelism is // minimized so that we can multiplex on a single spdy connection. - bool mTestedSpdy; + bool mTestedSpdy : 1; - bool mInPreferredHash; + bool mInPreferredHash : 1; // Flags to remember our happy-eyeballs decision. // Reset only by Ctrl-F5 reload. @@ -388,6 +388,9 @@ private: // initially false. bool mPreferIPv6 : 1; + // True if this connection entry has initiated a socket + bool mUsedForConnection : 1; + // Set the IP family preference flags according the connected family void RecordIPFamilyPreference(uint16_t family); // Resets all flags to their default values diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index ff4a1653f05..42da26f9f76 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -710,7 +710,7 @@ class RecursiveMakeBackend(CommonBackend): root_mk.add_statement('compile_targets := %s' % ' '.join(sorted( set(self._compile_graph.keys()) | all_compile_deps))) - root_mk.add_statement('$(call include_deps,root-deps.mk)') + root_mk.add_statement('include root-deps.mk') with self._write_file( mozpath.join(self.environment.topobjdir, 'root.mk')) as root: diff --git a/testing/taskcluster/scripts/tester/test-linux.sh b/testing/taskcluster/scripts/tester/test-linux.sh index c52cbced502..15716ed3962 100644 --- a/testing/taskcluster/scripts/tester/test-linux.sh +++ b/testing/taskcluster/scripts/tester/test-linux.sh @@ -27,7 +27,7 @@ if [[ -z ${MOZHARNESS_URL} ]]; then exit 1; fi if [[ -z ${MOZHARNESS_SCRIPT} ]]; then exit 1; fi if [[ -z ${MOZHARNESS_CONFIG} ]]; then exit 1; fi -mkdir -p ~/artifacts +mkdir -p ~/artifacts/public cleanup() { if [ -n "$xvfb_pid" ]; then diff --git a/toolkit/components/osfile/tests/xpcshell/xpcshell.ini b/toolkit/components/osfile/tests/xpcshell/xpcshell.ini index c8efff08f3f..70cd91d89e5 100644 --- a/toolkit/components/osfile/tests/xpcshell/xpcshell.ini +++ b/toolkit/components/osfile/tests/xpcshell/xpcshell.ini @@ -17,6 +17,7 @@ support-files = [test_osfile_async_flush.js] [test_osfile_async_largefiles.js] [test_osfile_async_setDates.js] +skip-if = (toolkit == 'gonk' && debug) # Bug 1208199 [test_removeEmptyDir.js] [test_makeDir.js] [test_file_URL_conversion.js] diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 23c41387066..5406b0d734f 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -1352,7 +1352,7 @@ "expires_in_version": "never", "kind": "enumerated", "n_values": 16, - "description": "NPN Results (0=none, 1=negotiated, 2=no-overlap)" + "description": "NPN Results (0=none, 1=negotiated, 2=no-overlap, 3=selected(alpn))" }, "SSL_RESUMED_SESSION": { "expires_in_version": "never", @@ -1585,6 +1585,11 @@ "n_values": 32, "description": "H2: goaway reason from peer from rfc 7540. 31 is none received." }, + "HTTP_CONNECTION_ENTRY_CACHE_HIT_1" : { + "expires_in_version": "never", + "kind": "boolean", + "description": "Fraction of sockets that used a nsConnectionEntry with history - size 300." + }, "DISK_CACHE_CORRUPT_DETAILS": { "expires_in_version": "40", "kind": "enumerated", diff --git a/toolkit/components/thumbnails/PageThumbs.jsm b/toolkit/components/thumbnails/PageThumbs.jsm index 50e1927f512..711d8cf8f23 100644 --- a/toolkit/components/thumbnails/PageThumbs.jsm +++ b/toolkit/components/thumbnails/PageThumbs.jsm @@ -33,6 +33,8 @@ Cu.import("resource://gre/modules/PromiseWorker.jsm", this); Cu.import("resource://gre/modules/Promise.jsm", this); Cu.import("resource://gre/modules/osfile.jsm", this); +Cu.importGlobalProperties(['FileReader']); + XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); @@ -83,9 +85,9 @@ const TaskUtils = { */ readBlob: function readBlob(blob) { let deferred = Promise.defer(); - let reader = Cc["@mozilla.org/files/filereader;1"].createInstance(Ci.nsIDOMFileReader); + let reader = new FileReader(); reader.onloadend = function onloadend() { - if (reader.readyState != Ci.nsIDOMFileReader.DONE) { + if (reader.readyState != FileReader.DONE) { deferred.reject(reader.error); } else { deferred.resolve(reader.result); @@ -305,8 +307,7 @@ this.PageThumbs = { mm.removeMessageListener("Browser:Thumbnail:Response", thumbFunc); let imageBlob = aMsg.data.thumbnail; let doc = aBrowser.parentElement.ownerDocument; - let reader = Cc["@mozilla.org/files/filereader;1"]. - createInstance(Ci.nsIDOMFileReader); + let reader = new FileReader(); reader.addEventListener("loadend", function() { let image = doc.createElementNS(PageThumbUtils.HTML_NAMESPACE, "img"); image.onload = function () { diff --git a/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js b/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js index dddbbcefdf3..dcbc7a81fa4 100644 --- a/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js +++ b/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js @@ -4,7 +4,7 @@ var { classes: Cc, interfaces: Ci, utils: Cu } = Components; -Cu.importGlobalProperties(['Blob']); +Cu.importGlobalProperties(['Blob', 'FileReader']); Cu.import("resource://gre/modules/PageThumbUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); @@ -141,8 +141,7 @@ const backgroundPageThumbsContent = { _finishCurrentCapture: function () { let capture = this._currentCapture; - let fileReader = Cc["@mozilla.org/files/filereader;1"]. - createInstance(Ci.nsIDOMFileReader); + let fileReader = new FileReader(); fileReader.onloadend = () => { sendAsyncMessage("BackgroundPageThumbs:didCapture", { id: capture.id, diff --git a/toolkit/modules/PropertyListUtils.jsm b/toolkit/modules/PropertyListUtils.jsm index 9c90b11f05a..adc300e5a65 100644 --- a/toolkit/modules/PropertyListUtils.jsm +++ b/toolkit/modules/PropertyListUtils.jsm @@ -61,7 +61,7 @@ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; -Cu.importGlobalProperties(['File']); +Cu.importGlobalProperties(['File', 'FileReader']); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ctypes", @@ -100,8 +100,7 @@ this.PropertyListUtils = Object.freeze({ file = new File(file); } - let fileReader = Cc["@mozilla.org/files/filereader;1"]. - createInstance(Ci.nsIDOMFileReader); + let fileReader = new FileReader(); let onLoadEnd = function() { let root = null; try { diff --git a/tools/mercurial/hgsetup/config.py b/tools/mercurial/hgsetup/config.py index 55bbc259b0a..da2bc1b442d 100644 --- a/tools/mercurial/hgsetup/config.py +++ b/tools/mercurial/hgsetup/config.py @@ -209,3 +209,54 @@ class MercurialConfig(object): for k in ('password', 'userid', 'cookie'): if k in b: del b[k] + + def have_clonebundles(self): + return 'clonebundles' in self._c.get('experimental', {}) + + def activate_clonebundles(self): + exp = self._c.setdefault('experimental', {}) + exp['clonebundles'] = 'true' + + # bundleclone is redundant with clonebundles. Remove it if it + # is installed. + ext = self._c.get('extensions', {}) + try: + del ext['bundleclone'] + except KeyError: + pass + + def have_wip(self): + return 'wip' in self._c.get('alias', {}) + + def install_wip_alias(self): + """hg wip shows a concise view of work in progress.""" + alias = self._c.setdefault('alias', {}) + alias['wip'] = 'log --graph --rev=wip --template=wip' + + revsetalias = self._c.setdefault('revsetalias', {}) + revsetalias['wip'] = ('(' + 'parents(not public()) ' + 'or not public() ' + 'or . ' + 'or (head() and branch(default))' + ') and (not obsolete() or unstable()^) ' + 'and not closed()') + + templates = self._c.setdefault('templates', {}) + templates['wip'] = ("'" + # prefix with branch name + '{label("log.branch", branches)} ' + # rev:node + '{label("changeset.{phase}", rev)}' + '{label("changeset.{phase}", ":")}' + '{label("changeset.{phase}", short(node))} ' + # just the username part of the author, for brevity + '{label("grep.user", author|user)}' + # tags and bookmarks + '{label("log.tag", if(tags," {tags}"))} ' + '{label("log.bookmark", if(bookmarks," {bookmarks}"))}' + '\\n' + # first line of commit message + '{label(ifcontains(rev, revset("."), "desc.here"),desc|firstline)}' + "'" + ) diff --git a/tools/mercurial/hgsetup/wizard.py b/tools/mercurial/hgsetup/wizard.py index 4ac840d22a4..77df4974164 100644 --- a/tools/mercurial/hgsetup/wizard.py +++ b/tools/mercurial/hgsetup/wizard.py @@ -8,6 +8,7 @@ import difflib import errno import os import shutil +import ssl import stat import sys import subprocess @@ -41,7 +42,7 @@ are up to date and you won't have to do anything. To begin, press the enter/return key. '''.strip() -OLDEST_NON_LEGACY_VERSION = LooseVersion('3.2.4') +OLDEST_NON_LEGACY_VERSION = LooseVersion('3.5.2') LEGACY_MERCURIAL = ''' You are running an out of date Mercurial client (%s). @@ -119,7 +120,7 @@ Your Mercurial should now be properly configured and recommended extensions should be up to date! '''.strip() -REVIEWBOARD_MINIMUM_VERSION = LooseVersion('3.1') +REVIEWBOARD_MINIMUM_VERSION = LooseVersion('3.3') REVIEWBOARD_INCOMPATIBLE = ''' Your Mercurial is too old to use the reviewboard extension, which is necessary @@ -161,7 +162,7 @@ if you have enabled 2 Factor Authentication in Bugzilla. All consumers formerly looking at these options should support API Keys. '''.lstrip() -BZPOST_MINIMUM_VERSION = LooseVersion('3.1') +BZPOST_MINIMUM_VERSION = LooseVersion('3.3') BZPOST_INFO = ''' The bzpost extension automatically records the URLs of pushed commits to @@ -172,7 +173,7 @@ referenced Bugzilla bugs after push. Would you like to activate bzpost '''.strip() -FIREFOXTREE_MINIMUM_VERSION = LooseVersion('3.1') +FIREFOXTREE_MINIMUM_VERSION = LooseVersion('3.3') FIREFOXTREE_INFO = ''' The firefoxtree extension makes interacting with the multiple Firefox @@ -211,6 +212,15 @@ they can push to try without depending on mq or other workarounds. Would you like to activate push-to-try '''.strip() +CLONEBUNDLES_INFO = ''' +Mercurial 3.6 and hg.mozilla.org support transparently cloning from a CDN, +making clones faster and more reliable. + +(Relevant config option: experimental.clonebundles) + +Would you like to activate this feature and have faster clones +'''.strip() + BUNDLECLONE_MINIMUM_VERSION = LooseVersion('3.1') BUNDLECLONE_INFO = ''' @@ -223,6 +233,31 @@ We highly recommend you activate this extension. Would you like to activate bundleclone '''.strip() +WIP_INFO = ''' +It is common to want a quick view of changesets that are in progress. + +The ``hg wip`` command provides should a view. + +Example Usage: + + $ hg wip + o 4084:fcfa34d0387b dminor @ + | mozreview: use repository name when displaying treeherder results (bug 1230548) r=mcote + | @ 4083:786baf6d476a gps + | | mozreview: create child review requests from batch API + | o 4082:3f100fa4a94f gps + | | mozreview: copy more read-only processing code; r?smacleod + | o 4081:939417680cbe gps + |/ mozreview: add web API to submit an entire series of commits (bug 1229468); r?smacleod + +(Not shown are the colors that help denote the state each changeset +is in.) + +(Relevant config options: alias.wip, revsetalias.wip, templates.wip) + +Would you like to install the `hg wip` alias? +'''.strip() + FILE_PERMISSIONS_WARNING = ''' Your hgrc file is currently readable by others. @@ -359,12 +394,22 @@ class MercurialSetupWizard(object): if hg_version >= FIREFOXTREE_MINIMUM_VERSION: self.prompt_external_extension(c, 'firefoxtree', FIREFOXTREE_INFO) - if hg_version >= BUNDLECLONE_MINIMUM_VERSION: + # Functionality from bundleclone is experimental in Mercurial 3.6. + # There was a bug in 3.6, so look for 3.6.1. + if hg_version >= LooseVersion('3.6.1'): + if not c.have_clonebundles() and self._prompt_yn(CLONEBUNDLES_INFO): + c.activate_clonebundles() + print('Enabled the clonebundles feature.\n') + elif hg_version >= BUNDLECLONE_MINIMUM_VERSION: self.prompt_external_extension(c, 'bundleclone', BUNDLECLONE_INFO) if hg_version >= PUSHTOTRY_MINIMUM_VERSION: self.prompt_external_extension(c, 'push-to-try', PUSHTOTRY_INFO) + if not c.have_wip(): + if self._prompt_yn(WIP_INFO): + c.install_wip_alias() + if 'mq' in c.extensions: self.prompt_external_extension(c, 'mqext', MQEXT_INFO) @@ -422,7 +467,15 @@ class MercurialSetupWizard(object): print('Cleaning up old repository: %s' % path) shutil.rmtree(path) - c.add_mozilla_host_fingerprints() + # Python + Mercurial didn't have terrific TLS handling until Python + # 2.7.9 and Mercurial 3.4. For this reason, it was recommended to pin + # certificates in Mercurial config files. In modern versions of + # Mercurial, the system CA store is used and old, legacy TLS protocols + # are disabled. The default connection/security setting should + # be sufficient and pinning certificates is no longer needed. + have_modern_ssl = hasattr(ssl.SSLContext, 'load_default_certs') + if hg_version < LooseVersion('3.4') or not have_modern_ssl: + c.add_mozilla_host_fingerprints() # References to multiple version-control-tools checkouts can confuse # version-control-tools, since various Mercurial extensions resolve diff --git a/widget/cocoa/nsNativeThemeCocoa.mm b/widget/cocoa/nsNativeThemeCocoa.mm index 32650cd300b..97008e93e88 100644 --- a/widget/cocoa/nsNativeThemeCocoa.mm +++ b/widget/cocoa/nsNativeThemeCocoa.mm @@ -6,6 +6,7 @@ #include "nsNativeThemeCocoa.h" #include "mozilla/gfx/2D.h" +#include "mozilla/gfx/Helpers.h" #include "nsDeviceContext.h" #include "nsLayoutUtils.h" #include "nsObjCExceptions.h" @@ -2428,19 +2429,14 @@ nsNativeThemeCocoa::DrawWidgetBackground(nsRenderingContext* aContext, if (nativeWidgetRect.IsEmpty()) return NS_OK; // Don't attempt to draw invisible widgets. - gfxContext* thebesCtx = aContext->ThebesContext(); - if (!thebesCtx) - return NS_ERROR_FAILURE; - - gfxContextMatrixAutoSaveRestore save(thebesCtx); + AutoRestoreTransform autoRestoreTransform(&aDrawTarget); bool hidpi = IsHiDPIContext(aFrame->PresContext()); if (hidpi) { // Use high-resolution drawing. nativeWidgetRect.Scale(0.5f); nativeDirtyRect.Scale(0.5f); - thebesCtx->SetMatrix( - thebesCtx->CurrentMatrix().Scale(2.0f, 2.0f)); + aDrawTarget.SetTransform(aDrawTarget.GetTransform().PreScale(2.0f, 2.0f)); } gfxQuartzNativeDrawing nativeDrawing(aDrawTarget, nativeDirtyRect); diff --git a/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp b/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp index 9a2548269e0..868986c769f 100644 --- a/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp +++ b/xpcom/reflect/xptinfo/ShimInterfaceInfo.cpp @@ -44,7 +44,6 @@ #include "nsIDOMEvent.h" #include "nsIDOMEventTarget.h" #include "nsIDOMFileList.h" -#include "nsIDOMFileReader.h" #include "nsIDOMFocusEvent.h" #include "nsIDOMFormData.h" #include "nsIDOMGeoPositionError.h" @@ -179,7 +178,6 @@ #include "mozilla/dom/EventBinding.h" #include "mozilla/dom/EventTargetBinding.h" #include "mozilla/dom/FileListBinding.h" -#include "mozilla/dom/FileReaderBinding.h" #include "mozilla/dom/FocusEventBinding.h" #include "mozilla/dom/FormDataBinding.h" #include "mozilla/dom/HistoryBinding.h" @@ -368,7 +366,6 @@ const ComponentsInterfaceShimEntry kComponentsInterfaceShimMap[] = DEFINE_SHIM(Element), DEFINE_SHIM(Event), DEFINE_SHIM(EventTarget), - DEFINE_SHIM(FileReader), DEFINE_SHIM(FileList), DEFINE_SHIM(FocusEvent), DEFINE_SHIM(FormData),