From 243ee711e1085e2ee6bf640c1d8e6a4a63e776e2 Mon Sep 17 00:00:00 2001 From: Axel Hecht Date: Thu, 22 Dec 2011 00:10:06 +0100 Subject: [PATCH 01/26] bug 708015, support both native and xul l10n, r=stas, dougt --HG-- rename : mobile/android/locales/Makefile.in => mobile/locales/Makefile.in rename : mobile/android/locales/en-US/chrome/region.properties => mobile/locales/en-US/chrome/region.properties rename : mobile/android/locales/en-US/chrome/overrides/appstrings.properties => mobile/locales/en-US/overrides/appstrings.properties rename : mobile/android/locales/en-US/chrome/overrides/netError.dtd => mobile/locales/en-US/overrides/netError.dtd rename : mobile/android/locales/en-US/chrome/overrides/passwordmgr.properties => mobile/locales/en-US/overrides/passwordmgr.properties rename : mobile/android/locales/en-US/profile/bookmarks.inc => mobile/locales/en-US/profile/bookmarks.inc rename : mobile/android/locales/en-US/searchplugins/amazondotcom.xml => mobile/locales/en-US/searchplugins/amazondotcom.xml rename : mobile/android/locales/en-US/searchplugins/google.xml => mobile/locales/en-US/searchplugins/google.xml rename : mobile/android/locales/en-US/searchplugins/list.txt => mobile/locales/en-US/searchplugins/list.txt rename : mobile/android/locales/en-US/searchplugins/twitter.xml => mobile/locales/en-US/searchplugins/twitter.xml rename : mobile/android/locales/en-US/searchplugins/wikipedia.xml => mobile/locales/en-US/searchplugins/wikipedia.xml rename : mobile/android/locales/en-US/searchplugins/yahoo.xml => mobile/locales/en-US/searchplugins/yahoo.xml rename : mobile/android/locales/filter.py => mobile/locales/filter.py rename : mobile/android/locales/generic/profile/bookmarks.json.in => mobile/locales/generic/profile/bookmarks.json.in rename : mobile/android/locales/jar.mn => mobile/locales/jar.mn rename : mobile/android/locales/l10n.ini => mobile/locales/l10n.ini --- embedding/android/Makefile.in | 29 --- embedding/android/locales/Makefile.in | 27 +++ embedding/android/locales/jar.mn | 4 - mobile/android/Makefile.in | 2 +- mobile/android/base/Makefile.in | 2 - mobile/android/base/locales/l10n.ini | 5 - mobile/android/locales/Makefile.in | 88 +-------- .../crashreporter/crashreporter-override.ini | 10 - .../android/locales/en-US/installer/setup.ini | 17 -- .../android/locales/en-US/updater/updater.ini | 4 - mobile/android/locales/filter.py | 53 ++++-- mobile/android/locales/jar.mn | 11 -- mobile/android/locales/l10n.ini | 7 +- mobile/locales/Makefile.in | 117 ++++++++++++ .../locales/en-US/chrome/region.properties | 0 .../en-US}/overrides/appstrings.properties | 0 .../en-US}/overrides/netError.dtd | 0 .../en-US}/overrides/passwordmgr.properties | 0 .../locales/en-US/profile/bookmarks.inc | 0 .../en-US/searchplugins/amazondotcom.xml | 0 .../locales/en-US/searchplugins/google.xml | 0 .../locales/en-US/searchplugins/list.txt | 0 .../locales/en-US/searchplugins/twitter.xml | 0 .../locales/en-US/searchplugins/wikipedia.xml | 0 .../locales/en-US/searchplugins/yahoo.xml | 0 mobile/locales/filter.py | 77 ++++++++ .../locales/generic/profile/bookmarks.json.in | 0 mobile/locales/jar.mn | 15 ++ mobile/locales/l10n.ini | 16 ++ mobile/xul/Makefile.in | 2 +- mobile/xul/locales/Makefile.in | 53 +----- .../chrome/overrides/appstrings.properties | 68 ------- .../en-US/chrome/overrides/netError.dtd | 172 ------------------ .../chrome/overrides/passwordmgr.properties | 72 -------- .../locales/en-US/chrome/region.properties | 32 ---- mobile/xul/locales/en-US/installer/setup.ini | 17 -- .../xul/locales/en-US/profile/bookmarks.inc | 30 --- .../en-US/searchplugins/amazondotcom.xml | 12 -- .../locales/en-US/searchplugins/google.xml | 15 -- .../xul/locales/en-US/searchplugins/list.txt | 4 - .../locales/en-US/searchplugins/twitter.xml | 9 - .../locales/en-US/searchplugins/wikipedia.xml | 14 -- .../xul/locales/en-US/searchplugins/yahoo.xml | 13 -- mobile/xul/locales/filter.py | 51 ++++-- mobile/xul/locales/jar.mn | 11 -- mobile/xul/locales/l10n.ini | 4 + 46 files changed, 334 insertions(+), 729 deletions(-) delete mode 100644 embedding/android/locales/jar.mn delete mode 100644 mobile/android/base/locales/l10n.ini delete mode 100644 mobile/android/locales/en-US/crashreporter/crashreporter-override.ini delete mode 100644 mobile/android/locales/en-US/installer/setup.ini delete mode 100644 mobile/android/locales/en-US/updater/updater.ini create mode 100644 mobile/locales/Makefile.in rename mobile/{android => }/locales/en-US/chrome/region.properties (100%) rename mobile/{android/locales/en-US/chrome => locales/en-US}/overrides/appstrings.properties (100%) rename mobile/{android/locales/en-US/chrome => locales/en-US}/overrides/netError.dtd (100%) rename mobile/{android/locales/en-US/chrome => locales/en-US}/overrides/passwordmgr.properties (100%) rename mobile/{android => }/locales/en-US/profile/bookmarks.inc (100%) rename mobile/{android => }/locales/en-US/searchplugins/amazondotcom.xml (100%) rename mobile/{android => }/locales/en-US/searchplugins/google.xml (100%) rename mobile/{android => }/locales/en-US/searchplugins/list.txt (100%) rename mobile/{android => }/locales/en-US/searchplugins/twitter.xml (100%) rename mobile/{android => }/locales/en-US/searchplugins/wikipedia.xml (100%) rename mobile/{android => }/locales/en-US/searchplugins/yahoo.xml (100%) create mode 100644 mobile/locales/filter.py rename mobile/{android => }/locales/generic/profile/bookmarks.json.in (100%) create mode 100644 mobile/locales/jar.mn create mode 100644 mobile/locales/l10n.ini delete mode 100644 mobile/xul/locales/en-US/chrome/overrides/appstrings.properties delete mode 100644 mobile/xul/locales/en-US/chrome/overrides/netError.dtd delete mode 100644 mobile/xul/locales/en-US/chrome/overrides/passwordmgr.properties delete mode 100644 mobile/xul/locales/en-US/chrome/region.properties delete mode 100644 mobile/xul/locales/en-US/installer/setup.ini delete mode 100644 mobile/xul/locales/en-US/profile/bookmarks.inc delete mode 100644 mobile/xul/locales/en-US/searchplugins/amazondotcom.xml delete mode 100644 mobile/xul/locales/en-US/searchplugins/google.xml delete mode 100644 mobile/xul/locales/en-US/searchplugins/list.txt delete mode 100644 mobile/xul/locales/en-US/searchplugins/twitter.xml delete mode 100644 mobile/xul/locales/en-US/searchplugins/wikipedia.xml delete mode 100644 mobile/xul/locales/en-US/searchplugins/yahoo.xml diff --git a/embedding/android/Makefile.in b/embedding/android/Makefile.in index 71920c4bdbf5..10a7a6039460 100644 --- a/embedding/android/Makefile.in +++ b/embedding/android/Makefile.in @@ -133,15 +133,8 @@ RES_LAYOUT = \ RES_VALUES = res/values/colors.xml res/values/themes.xml -AB_rCD = $(shell echo $(AB_CD) | sed -e s/-/-r/) - JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar -DEFAULT_BRANDPATH = $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales/en-US/brand.dtd -DEFAULT_STRINGSPATH = locales/en-US/android_strings.dtd -LOCALIZED_BRANDPATH = $(DEPTH)/dist/bin/chrome/$(AB_CD)/locale/branding/brand.dtd -LOCALIZED_STRINGSPATH = $(DEPTH)/dist/bin/chrome/android-res/res/values-$(AB_CD)/android_strings.dtd - ifdef MOZ_CRASHREPORTER PROCESSEDJAVAFILES += CrashReporter.java MOZ_ANDROID_DRAWABLES += embedding/android/resources/drawable/crash_reporter.png @@ -154,10 +147,6 @@ MOZ_ANDROID_DRAWABLES += $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTOR include $(topsrcdir)/config/rules.mk -ifneq ($(AB_CD),en-US) -LOCALIZED_STRINGS_XML = res/values-$(AB_rCD)/strings.xml -endif - # Override the Java settings with some specific android settings include $(topsrcdir)/config/android-common.mk @@ -200,23 +189,5 @@ R.java: $(MOZ_APP_ICON) $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) res/drawable gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) res/values/strings.xml FORCE $(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@ -res/values/strings.xml: $(DEFAULT_BRANDPATH) $(DEFAULT_STRINGSPATH) - mkdir -p res/values - $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \ - -DBRANDPATH="$(DEFAULT_BRANDPATH)" \ - -DSTRINGSPATH="$(DEFAULT_STRINGSPATH)" \ - $(srcdir)/strings.xml.in \ - > $@ - -res/values-$(AB_rCD)/strings.xml: $(LOCALIZED_BRANDPATH) $(LOCALIZED_STRINGSPATH) - mkdir -p res/values-$(AB_rCD) - $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \ - -DBRANDPATH="$(call core_abspath,$(LOCALIZED_BRANDPATH))" \ - -DSTRINGSPATH="$(call core_abspath,$(LOCALIZED_STRINGSPATH))" \ - $(srcdir)/strings.xml.in \ - > $@ - -chrome:: $(LOCALIZED_STRINGS_XML) - libs:: classes.dex $(INSTALL) classes.dex $(FINAL_TARGET) diff --git a/embedding/android/locales/Makefile.in b/embedding/android/locales/Makefile.in index b4ad413e88e5..e31b46d2296d 100644 --- a/embedding/android/locales/Makefile.in +++ b/embedding/android/locales/Makefile.in @@ -43,6 +43,33 @@ relativesrcdir = embedding/android/locales include $(DEPTH)/config/autoconf.mk +# special case some locale codes, he and id +# http://code.google.com/p/android/issues/detail?id=3639 +AB_rCD = $(if $(filter he, $(AB_CD)),iw,$(if $(filter id, $(AB_CD)),in,$(subst -,-r,$(AB_CD)))) + +STRINGSPATH = $(call core_abspath,$(call MERGE_FILE,android_strings.dtd)) +ifeq (,$(XPI_NAME)) +BRANDPATH = $(call core_abspath,$(DEPTH)/dist/bin/chrome/$(AB_CD)/locale/branding/brand.dtd) +else +BRANDPATH = $(call core_abspath,$(DIST)/xpi-stage/$(XPI_NAME)/chrome/$(AB_CD)/locale/branding/brand.dtd) +endif + DEFINES += -DAB_CD=$(AB_CD) +libs realchrome:: ../res/values/strings.xml ; + +chrome-%:: AB_CD=$* +chrome-%:: + @$(MAKE) ../res/values-$(AB_rCD)/strings.xml AB_CD=$* + +%/strings.xml: FORCE + $(NSINSTALL) -D $* + # we don't have branding yet, but we need it. Call it explicitly + @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales realchrome + $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \ + -DBRANDPATH="$(BRANDPATH)" \ + -DSTRINGSPATH="$(STRINGSPATH)" \ + $(srcdir)/../strings.xml.in \ + > $@ + include $(topsrcdir)/config/rules.mk diff --git a/embedding/android/locales/jar.mn b/embedding/android/locales/jar.mn deleted file mode 100644 index 452e9d251544..000000000000 --- a/embedding/android/locales/jar.mn +++ /dev/null @@ -1,4 +0,0 @@ -#filter substitution - -android-res.jar: - res/values-@AB_CD@/android_strings.dtd (%android_strings.dtd) diff --git a/mobile/android/Makefile.in b/mobile/android/Makefile.in index c1b073125221..32def7ace4f3 100644 --- a/mobile/android/Makefile.in +++ b/mobile/android/Makefile.in @@ -43,7 +43,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -DIRS = locales base chrome components modules themes/core app +DIRS = ../locales locales base chrome components modules themes/core app ifndef LIBXUL_SDK PARALLEL_DIRS += $(DEPTH)/xulrunner/tools/redit diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index b81ae1ae9c66..4567b10d6c4a 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -455,8 +455,6 @@ RES_MENU = \ res/menu/awesomebar_contextmenu.xml \ $(NULL) -AB_rCD = $(shell echo $(AB_CD) | sed -e s/-/-r/) - JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar ifdef MOZ_CRASHREPORTER diff --git a/mobile/android/base/locales/l10n.ini b/mobile/android/base/locales/l10n.ini deleted file mode 100644 index f05d68e2da33..000000000000 --- a/mobile/android/base/locales/l10n.ini +++ /dev/null @@ -1,5 +0,0 @@ -[general] -depth = ../../../.. - -[compare] -dirs = mobile/android/base diff --git a/mobile/android/locales/Makefile.in b/mobile/android/locales/Makefile.in index f691b61f6356..7d422185a2af 100644 --- a/mobile/android/locales/Makefile.in +++ b/mobile/android/locales/Makefile.in @@ -45,55 +45,14 @@ relativesrcdir = mobile/android/locales include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/config.mk -ifdef LOCALE_MERGEDIR -vpath book%.inc $(LOCALE_MERGEDIR)/mobile/android/profile -endif -vpath book%.inc $(LOCALE_SRCDIR)/profile -ifdef LOCALE_MERGEDIR -vpath book%.inc @srcdir@/en-US/profile -endif - -ifdef LOCALE_MERGEDIR -vpath crashreporter%.ini $(LOCALE_MERGEDIR)/mobile/android/crashreporter -endif -vpath crashreporter%.ini $(LOCALE_SRCDIR)/crashreporter -ifdef LOCALE_MERGEDIR -vpath crashreporter%.ini @srcdir@/en-US/crashreporter -endif - SUBMAKEFILES += \ $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/Makefile \ $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales/Makefile \ $(NULL) -MOZ_LANGPACK_EID=langpack-$(AB_CD)@firefox-mobile.mozilla.org PREF_JS_EXPORTS = $(firstword $(wildcard $(LOCALE_SRCDIR)/mobile-l10n.js) \ @srcdir@/en-US/mobile-l10n.js ) -# Shouldn't := DEB_BUILD_ARCH despite the $(shell ) as deb isn't everywhere -DEB_BUILD_ARCH = $(shell dpkg-architecture -qDEB_BUILD_ARCH) -DATASTAGE = $(CURDIR)/data-stage - -SEARCH_PLUGINS = $(shell cat \ - $(firstword $(wildcard $(LOCALE_SRCDIR)/searchplugins/list.txt) \ - @srcdir@/en-US/searchplugins/list.txt ) ) - -tmp-search.jar.mn:: - printf "$(AB_CD).jar:" > $@ - printf "$(foreach plugin,$(SEARCH_PLUGINS),$(subst __PLUGIN_SUBST__,$(plugin), \n locale/$(AB_CD)/browser/searchplugins/__PLUGIN_SUBST__.xml (__PLUGIN_SUBST__.xml)))" >> $@ - @echo >> $@ - -searchplugins: tmp-search.jar.mn - $(PYTHON) $(MOZILLA_DIR)/config/JarMaker.py \ - $(QUIET) -j $(FINAL_TARGET)/chrome \ - -s $(topsrcdir)/$(relativesrcdir)/en-US/searchplugins \ - -s $(LOCALE_SRCDIR)/searchplugins \ - $(MAKE_JARS_FLAGS) tmp-search.jar.mn - -export:: searchplugins - -GARBAGE += tmp-search.jar.mn - include $(topsrcdir)/config/rules.mk include $(topsrcdir)/toolkit/locales/l10n.mk @@ -117,24 +76,16 @@ clobber-stage: $(RM) $(DEPTH)/mobile/android/base/res/values-*/strings.xml libs-%: - $(NSINSTALL) -D $(DIST)/install - @$(MAKE) -C $(DEPTH)/toolkit/locales libs-$* BOTH_MANIFESTS=1 - @$(MAKE) -C $(DEPTH)/services/sync/locales AB_CD=$* XPI_NAME=locale-$* BOTH_MANIFESTS=1 - @$(MAKE) -C $(DEPTH)/intl/locales AB_CD=$* XPI_NAME=locale-$* BOTH_MANIFESTS=1 - @$(MAKE) -B bookmarks.json AB_CD=$* - @$(MAKE) -B searchplugins AB_CD=$* XPI_NAME=locale-$* + @$(MAKE) -C $(DEPTH)/mobile/locales libs-$* BOTH_MANIFESTS=1 @$(MAKE) libs AB_CD=$* XPI_NAME=locale-$* PREF_DIR=defaults/pref BOTH_MANIFESTS=1 - @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales AB_CD=$* XPI_NAME=locale-$* BOTH_MANIFESTS=1 +ifeq ($(OS_TARGET),Android) @$(MAKE) -C $(DEPTH)/mobile/android/base/locales AB_CD=$* XPI_NAME=locale-$* BOTH_MANIFESTS=1 +endif # Tailored target to just add the chrome processing for multi-locale builds chrome-%: - @$(MAKE) -C $(DEPTH)/toolkit/locales chrome-$* - @$(MAKE) -C $(DEPTH)/services/sync/locales chrome AB_CD=$* - @$(MAKE) -B bookmarks.json AB_CD=$* - @$(MAKE) -B searchplugins AB_CD=$* + @$(MAKE) -C $(DEPTH)/mobile/locales chrome-$* @$(MAKE) chrome AB_CD=$* - @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales chrome AB_CD=$* ifeq ($(OS_TARGET),Android) @$(MAKE) -C $(DEPTH)/mobile/android/base/locales chrome-$* endif @@ -145,37 +96,6 @@ endif installers-%: clobber-stage repackage-zip-% @echo "repackaging done" -NO_JA_JP_MAC_AB_CD := $(if $(filter ja-JP-mac, $(AB_CD)),ja,$(AB_CD)) - -bookmarks.json: bookmarks.inc generic/profile/bookmarks.json.in - $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ - -I $< \ - -DAB_CD=$(NO_JA_JP_MAC_AB_CD) \ - $(srcdir)/generic/profile/bookmarks.json.in \ - > $@ - -export:: bookmarks.json - -ifdef MOZ_UPDATER -ifdef LOCALE_MERGEDIR -UPDATER_INI := $(firstword $(wildcard $(LOCALE_MERGEDIR)/updater/updater.ini) \ - $(wildcard $(LOCALE_SRCDIR)/updater/updater.ini) \ - $(srcdir)/en-US/updater/updater.ini ) -else -UPDATER_INI := $(addprefix $(LOCALE_SRCDIR)/,updater/updater.ini) -endif -libs:: $(UPDATER_INI) - cat $< | \ - sed -e "s/^InfoText=/Info=/" -e "s/^TitleText=/Title=/" | \ - sed -e "s/%MOZ_APP_DISPLAYNAME%/$(MOZ_APP_DISPLAYNAME)/" > \ - $(FINAL_TARGET)/updater.ini -endif - -ifdef MOZ_CRASHREPORTER -libs:: crashreporter-override.ini - $(SYSINSTALL) $(IFLAGS1) $^ $(FINAL_TARGET) -endif - # When we unpack fennec on MacOS X the platform.ini and application.ini are in slightly # different locations that on all other platforms ifeq (Darwin, $(OS_ARCH)) diff --git a/mobile/android/locales/en-US/crashreporter/crashreporter-override.ini b/mobile/android/locales/en-US/crashreporter/crashreporter-override.ini deleted file mode 100644 index 2ee9649d5fd2..000000000000 --- a/mobile/android/locales/en-US/crashreporter/crashreporter-override.ini +++ /dev/null @@ -1,10 +0,0 @@ -# This file is in the UTF-8 encoding -[Strings] -# LOCALIZATION NOTE (CrashReporterProductErrorText2): %s is replaced with another string containing detailed information. -CrashReporterProductErrorText2=Firefox has crashed. Unfortunately the crash reporter is unable to submit a crash report.\n\nDetails: %s -CrashReporterDescriptionText2=Firefox has crashed. Your tabs will be listed on the Firefox Start page when you restart.\n\nPlease help us fix the problem! -# LOCALIZATION NOTE (CheckSendReport): The %s is replaced with the vendor name. -CheckSendReport=Send %s a crash report -CheckIncludeURL=Include the page address -Quit2=Quit Firefox -Restart=Restart Firefox diff --git a/mobile/android/locales/en-US/installer/setup.ini b/mobile/android/locales/en-US/installer/setup.ini deleted file mode 100644 index 1ed7de333fd8..000000000000 --- a/mobile/android/locales/en-US/installer/setup.ini +++ /dev/null @@ -1,17 +0,0 @@ -; This file is in the UTF-8 encoding -[Strings] -AppShortName=%MOZ_APP_DISPLAYNAME% -AppLongName=Mozilla %MOZ_APP_DISPLAYNAME% -WindowCaption=Mozilla %MOZ_APP_DISPLAYNAME% Setup -InstallTo=Install %MOZ_APP_DISPLAYNAME% to -Install=Install -Cancel=Cancel -InstalledSuccessfully=Mozilla %MOZ_APP_DISPLAYNAME% has been installed successfully. -ExtractionError=Archive extraction error: -ThereWereErrors=There were errors during installation: -CreatingUserProfile=Creating user profile. Please wait... -UninstallCaption=Mozilla %MOZ_APP_DISPLAYNAME% Uninstall -FilesWillBeRemoved=All files will be removed from -AreYouSure=Are you sure? -InstallationNotFound=Mozilla %MOZ_APP_DISPLAYNAME% installation not found. -UninstalledSuccessfully=Mozilla %MOZ_APP_DISPLAYNAME% has been uninstalled successfully. diff --git a/mobile/android/locales/en-US/updater/updater.ini b/mobile/android/locales/en-US/updater/updater.ini deleted file mode 100644 index dfa025730656..000000000000 --- a/mobile/android/locales/en-US/updater/updater.ini +++ /dev/null @@ -1,4 +0,0 @@ -; This file is in the UTF-8 encoding -[Strings] -TitleText=%MOZ_APP_DISPLAYNAME% Update -InfoText=%MOZ_APP_DISPLAYNAME% is installing your updates and will start in a few moments… diff --git a/mobile/android/locales/filter.py b/mobile/android/locales/filter.py index 03f4209f8ca0..79598419cba9 100644 --- a/mobile/android/locales/filter.py +++ b/mobile/android/locales/filter.py @@ -35,30 +35,43 @@ # # ***** END LICENSE BLOCK ***** +"""This routine controls which localizable files and entries are +reported and l10n-merged. +It's common to all of mobile, mobile/android and mobile/xul, so +those three versions need to stay in sync. +""" def test(mod, path, entity = None): import re # ignore anything but mobile, which is our local repo checkout name if mod not in ("netwerk", "dom", "toolkit", "security/manager", - "services/sync", "mobile/android/base", - "mobile/android"): - return False + "services/sync", "mobile", + "mobile/android/base", "mobile/android", + "mobile/xul"): + return "ignore" - if mod != "mobile/android": - # we only have exceptions for mobile - return True - if not entity: - return not (re.match(r"searchplugins\/.+\.xml", path) or - re.match(r"mobile-l10n.js", path) or - re.match(r"defines.inc", path)) - if path == "defines.inc": - return entity != "MOZ_LANGPACK_CONTRIBUTORS" + if mod not in ("mobile", "mobile/android", "mobile/xul"): + # we only have exceptions for mobile* + return "error" + if mod in ("mobile/android", "mobile/xul"): + if not entity: + if (re.match(r"mobile-l10n.js", path) or + re.match(r"defines.inc", path)): + return "ignore" + if path == "defines.inc": + if entity == "MOZ_LANGPACK_CONTRIBUTORS": + return "ignore" + return "error" - if path != "chrome/region.properties": - # only region.properties exceptions remain, compare all others - return True - - return not (re.match(r"browser\.search\.order\.[1-9]", entity) or - re.match(r"browser\.contentHandlers\.types\.[0-5]", entity) or - re.match(r"gecko\.handlerService\.schemes\.", entity) or - re.match(r"gecko\.handlerService\.defaultHandlersVersion", entity)) + # we're in mod == "mobile" + if re.match(r"searchplugins\/.+\.xml", path): + return "ignore" + if path == "chrome/region.properties": + # only region.properties exceptions remain + if (re.match(r"browser\.search\.order\.[1-9]", entity) or + re.match(r"browser\.contentHandlers\.types\.[0-5]", entity) or + re.match(r"gecko\.handlerService\.schemes\.", entity) or + re.match(r"gecko\.handlerService\.defaultHandlersVersion", entity)): + return "ignore" + + return "error" diff --git a/mobile/android/locales/jar.mn b/mobile/android/locales/jar.mn index e44f2e222a3a..24b8f593400b 100644 --- a/mobile/android/locales/jar.mn +++ b/mobile/android/locales/jar.mn @@ -10,7 +10,6 @@ locale/@AB_CD@/browser/config.dtd (%chrome/config.dtd) locale/@AB_CD@/browser/config.properties (%chrome/config.properties) locale/@AB_CD@/browser/localepicker.properties (%chrome/localepicker.properties) - locale/@AB_CD@/browser/region.properties (%chrome/region.properties) locale/@AB_CD@/browser/aboutHome.dtd (%chrome/aboutHome.dtd) locale/@AB_CD@/browser/checkbox.dtd (%chrome/checkbox.dtd) locale/@AB_CD@/browser/notification.dtd (%chrome/notification.dtd) @@ -20,13 +19,3 @@ locale/@AB_CD@/browser/webapps.dtd (%chrome/webapps.dtd) locale/@AB_CD@/browser/feedback.dtd (%chrome/feedback.dtd) locale/@AB_CD@/browser/phishing.dtd (%chrome/phishing.dtd) - locale/@AB_CD@/browser/bookmarks.json (bookmarks.json) - locale/@AB_CD@/browser/searchplugins/list.txt (%searchplugins/list.txt) - -# Fennec-specific overrides of generic strings -* locale/@AB_CD@/browser/netError.dtd (%chrome/overrides/netError.dtd) -% override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd -* locale/@AB_CD@/browser/appstrings.properties (%chrome/overrides/appstrings.properties) -% override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties -* locale/@AB_CD@/browser/passwordmgr.properties (%chrome/overrides/passwordmgr.properties) -% override chrome://passwordmgr/locale/passwordmgr.properties chrome://browser/locale/passwordmgr.properties diff --git a/mobile/android/locales/l10n.ini b/mobile/android/locales/l10n.ini index 3d2f6ea96643..95877156712f 100644 --- a/mobile/android/locales/l10n.ini +++ b/mobile/android/locales/l10n.ini @@ -1,11 +1,14 @@ +# Control which directories and modules are part of mobile/android. +# Changes here should be reflected in mobile/locales/l10n.ini so +# that the dashboard picks them up. + [general] depth = ../../.. all = mobile/android/locales/all-locales [compare] -dirs = mobile/android +dirs = mobile mobile/android mobile/android/base [includes] toolkit = toolkit/locales/l10n.ini services_sync = services/sync/locales/l10n.ini -android_base = mobile/android/base/locales/l10n.ini diff --git a/mobile/locales/Makefile.in b/mobile/locales/Makefile.in new file mode 100644 index 000000000000..45e4ed691aff --- /dev/null +++ b/mobile/locales/Makefile.in @@ -0,0 +1,117 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2007 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Finkle +# Axel Hecht +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = mobile/locales + +include $(DEPTH)/config/autoconf.mk +include $(topsrcdir)/config/config.mk + +ifdef LOCALE_MERGEDIR +vpath book%.inc $(LOCALE_MERGEDIR)/mobile/profile +endif +vpath book%.inc $(LOCALE_SRCDIR)/profile +ifdef LOCALE_MERGEDIR +vpath book%.inc @srcdir@/en-US/profile +endif + +SUBMAKEFILES += \ + $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/Makefile \ + $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales/Makefile \ + $(NULL) + +DEFINES += \ + -DAB_CD=$(AB_CD) \ + $(NULL) + +SEARCH_PLUGINS = $(shell cat \ + $(firstword $(wildcard $(LOCALE_SRCDIR)/searchplugins/list.txt) \ + @srcdir@/en-US/searchplugins/list.txt ) ) + +tmp-search.jar.mn:: + printf "$(AB_CD).jar:" > $@ + printf "$(foreach plugin,$(SEARCH_PLUGINS),$(subst __PLUGIN_SUBST__,$(plugin), \n locale/$(AB_CD)/browser/searchplugins/__PLUGIN_SUBST__.xml (__PLUGIN_SUBST__.xml)))" >> $@ + @echo >> $@ + +searchplugins: tmp-search.jar.mn + $(PYTHON) $(MOZILLA_DIR)/config/JarMaker.py \ + $(QUIET) -j $(FINAL_TARGET)/chrome \ + -s $(topsrcdir)/$(relativesrcdir)/en-US/searchplugins \ + -s $(LOCALE_SRCDIR)/searchplugins \ + $(MAKE_JARS_FLAGS) tmp-search.jar.mn + +export:: searchplugins + +GARBAGE += tmp-search.jar.mn + +include $(topsrcdir)/config/rules.mk + +libs-%: + $(NSINSTALL) -D $(DIST)/install + @$(MAKE) -C $(DEPTH)/toolkit/locales libs-$* BOTH_MANIFESTS=1 + @$(MAKE) -C $(DEPTH)/services/sync/locales AB_CD=$* XPI_NAME=locale-$* BOTH_MANIFESTS=1 + @$(MAKE) -C $(DEPTH)/intl/locales AB_CD=$* XPI_NAME=locale-$* BOTH_MANIFESTS=1 + @$(MAKE) -B bookmarks.json AB_CD=$* + @$(MAKE) -B searchplugins AB_CD=$* XPI_NAME=locale-$* + @$(MAKE) libs AB_CD=$* XPI_NAME=locale-$* PREF_DIR=defaults/pref BOTH_MANIFESTS=1 + @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales AB_CD=$* XPI_NAME=locale-$* BOTH_MANIFESTS=1 + +# Tailored target to just add the chrome processing for multi-locale builds +chrome-%: + @$(MAKE) -C $(DEPTH)/toolkit/locales chrome-$* + @$(MAKE) -C $(DEPTH)/services/sync/locales chrome AB_CD=$* + @$(MAKE) -B bookmarks.json AB_CD=$* + @$(MAKE) -B searchplugins AB_CD=$* + @$(MAKE) chrome AB_CD=$* + @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales chrome AB_CD=$* + +NO_JA_JP_MAC_AB_CD := $(if $(filter ja-JP-mac, $(AB_CD)),ja,$(AB_CD)) + +bookmarks.json: bookmarks.inc generic/profile/bookmarks.json.in + $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ + -I $< \ + -DAB_CD=$(NO_JA_JP_MAC_AB_CD) \ + $(srcdir)/generic/profile/bookmarks.json.in \ + > $@ + +export:: bookmarks.json + +GARBAGE += bookmarks.json diff --git a/mobile/android/locales/en-US/chrome/region.properties b/mobile/locales/en-US/chrome/region.properties similarity index 100% rename from mobile/android/locales/en-US/chrome/region.properties rename to mobile/locales/en-US/chrome/region.properties diff --git a/mobile/android/locales/en-US/chrome/overrides/appstrings.properties b/mobile/locales/en-US/overrides/appstrings.properties similarity index 100% rename from mobile/android/locales/en-US/chrome/overrides/appstrings.properties rename to mobile/locales/en-US/overrides/appstrings.properties diff --git a/mobile/android/locales/en-US/chrome/overrides/netError.dtd b/mobile/locales/en-US/overrides/netError.dtd similarity index 100% rename from mobile/android/locales/en-US/chrome/overrides/netError.dtd rename to mobile/locales/en-US/overrides/netError.dtd diff --git a/mobile/android/locales/en-US/chrome/overrides/passwordmgr.properties b/mobile/locales/en-US/overrides/passwordmgr.properties similarity index 100% rename from mobile/android/locales/en-US/chrome/overrides/passwordmgr.properties rename to mobile/locales/en-US/overrides/passwordmgr.properties diff --git a/mobile/android/locales/en-US/profile/bookmarks.inc b/mobile/locales/en-US/profile/bookmarks.inc similarity index 100% rename from mobile/android/locales/en-US/profile/bookmarks.inc rename to mobile/locales/en-US/profile/bookmarks.inc diff --git a/mobile/android/locales/en-US/searchplugins/amazondotcom.xml b/mobile/locales/en-US/searchplugins/amazondotcom.xml similarity index 100% rename from mobile/android/locales/en-US/searchplugins/amazondotcom.xml rename to mobile/locales/en-US/searchplugins/amazondotcom.xml diff --git a/mobile/android/locales/en-US/searchplugins/google.xml b/mobile/locales/en-US/searchplugins/google.xml similarity index 100% rename from mobile/android/locales/en-US/searchplugins/google.xml rename to mobile/locales/en-US/searchplugins/google.xml diff --git a/mobile/android/locales/en-US/searchplugins/list.txt b/mobile/locales/en-US/searchplugins/list.txt similarity index 100% rename from mobile/android/locales/en-US/searchplugins/list.txt rename to mobile/locales/en-US/searchplugins/list.txt diff --git a/mobile/android/locales/en-US/searchplugins/twitter.xml b/mobile/locales/en-US/searchplugins/twitter.xml similarity index 100% rename from mobile/android/locales/en-US/searchplugins/twitter.xml rename to mobile/locales/en-US/searchplugins/twitter.xml diff --git a/mobile/android/locales/en-US/searchplugins/wikipedia.xml b/mobile/locales/en-US/searchplugins/wikipedia.xml similarity index 100% rename from mobile/android/locales/en-US/searchplugins/wikipedia.xml rename to mobile/locales/en-US/searchplugins/wikipedia.xml diff --git a/mobile/android/locales/en-US/searchplugins/yahoo.xml b/mobile/locales/en-US/searchplugins/yahoo.xml similarity index 100% rename from mobile/android/locales/en-US/searchplugins/yahoo.xml rename to mobile/locales/en-US/searchplugins/yahoo.xml diff --git a/mobile/locales/filter.py b/mobile/locales/filter.py new file mode 100644 index 000000000000..79598419cba9 --- /dev/null +++ b/mobile/locales/filter.py @@ -0,0 +1,77 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla. +# +# The Initial Developer of the Original Code is +# Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Axel Hecht +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +"""This routine controls which localizable files and entries are +reported and l10n-merged. +It's common to all of mobile, mobile/android and mobile/xul, so +those three versions need to stay in sync. +""" + +def test(mod, path, entity = None): + import re + # ignore anything but mobile, which is our local repo checkout name + if mod not in ("netwerk", "dom", "toolkit", "security/manager", + "services/sync", "mobile", + "mobile/android/base", "mobile/android", + "mobile/xul"): + return "ignore" + + if mod not in ("mobile", "mobile/android", "mobile/xul"): + # we only have exceptions for mobile* + return "error" + if mod in ("mobile/android", "mobile/xul"): + if not entity: + if (re.match(r"mobile-l10n.js", path) or + re.match(r"defines.inc", path)): + return "ignore" + if path == "defines.inc": + if entity == "MOZ_LANGPACK_CONTRIBUTORS": + return "ignore" + return "error" + + # we're in mod == "mobile" + if re.match(r"searchplugins\/.+\.xml", path): + return "ignore" + if path == "chrome/region.properties": + # only region.properties exceptions remain + if (re.match(r"browser\.search\.order\.[1-9]", entity) or + re.match(r"browser\.contentHandlers\.types\.[0-5]", entity) or + re.match(r"gecko\.handlerService\.schemes\.", entity) or + re.match(r"gecko\.handlerService\.defaultHandlersVersion", entity)): + return "ignore" + + return "error" diff --git a/mobile/android/locales/generic/profile/bookmarks.json.in b/mobile/locales/generic/profile/bookmarks.json.in similarity index 100% rename from mobile/android/locales/generic/profile/bookmarks.json.in rename to mobile/locales/generic/profile/bookmarks.json.in diff --git a/mobile/locales/jar.mn b/mobile/locales/jar.mn new file mode 100644 index 000000000000..fde2598c80f6 --- /dev/null +++ b/mobile/locales/jar.mn @@ -0,0 +1,15 @@ +#filter substitution + +@AB_CD@.jar: +% locale browser @AB_CD@ %locale/@AB_CD@/browser/ + locale/@AB_CD@/browser/region.properties (%chrome/region.properties) + locale/@AB_CD@/browser/bookmarks.json (bookmarks.json) + locale/@AB_CD@/browser/searchplugins/list.txt (%searchplugins/list.txt) + +# Fennec-specific overrides of generic strings +* locale/@AB_CD@/browser/netError.dtd (%overrides/netError.dtd) +% override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd +* locale/@AB_CD@/browser/appstrings.properties (%overrides/appstrings.properties) +% override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties +* locale/@AB_CD@/browser/passwordmgr.properties (%overrides/passwordmgr.properties) +% override chrome://passwordmgr/locale/passwordmgr.properties chrome://browser/locale/passwordmgr.properties diff --git a/mobile/locales/l10n.ini b/mobile/locales/l10n.ini new file mode 100644 index 000000000000..f6dfdcf39865 --- /dev/null +++ b/mobile/locales/l10n.ini @@ -0,0 +1,16 @@ +# Control which directories and modules are part of mobile +# on the l10n dashboard. +# Changes here should be triggered by changes in either +# mobile/android/locales/l10n.ini or mobile/xul/locales/l10n.ini. + +[general] +depth = ../.. +all = mobile/android/locales/all-locales + +[compare] +dirs = mobile mobile/android mobile/android/base mobile/xul + +[includes] +toolkit = toolkit/locales/l10n.ini +services_sync = services/sync/locales/l10n.ini +embedding_android = embedding/android/locales/l10n.ini diff --git a/mobile/xul/Makefile.in b/mobile/xul/Makefile.in index 803213b44e0e..ffcc50d47087 100644 --- a/mobile/xul/Makefile.in +++ b/mobile/xul/Makefile.in @@ -43,7 +43,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -DIRS = chrome locales components modules themes/core app +DIRS = ../locales chrome locales components modules themes/core app ifndef LIBXUL_SDK PARALLEL_DIRS += $(DEPTH)/xulrunner/tools/redit diff --git a/mobile/xul/locales/Makefile.in b/mobile/xul/locales/Makefile.in index 24b2d21bd7a9..662d0cdbbbcc 100644 --- a/mobile/xul/locales/Makefile.in +++ b/mobile/xul/locales/Makefile.in @@ -45,14 +45,6 @@ relativesrcdir = mobile/xul/locales include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/config.mk -ifdef LOCALE_MERGEDIR -vpath book%.inc $(LOCALE_MERGEDIR)/mobile/profile -endif -vpath book%.inc $(LOCALE_SRCDIR)/profile -ifdef LOCALE_MERGEDIR -vpath book%.inc @srcdir@/en-US/profile -endif - ifdef LOCALE_MERGEDIR vpath crashreporter%.ini $(LOCALE_MERGEDIR)/mobile/crashreporter endif @@ -74,26 +66,6 @@ PREF_JS_EXPORTS = $(firstword $(wildcard $(LOCALE_SRCDIR)/mobile-l10n.js) \ DEB_BUILD_ARCH = $(shell dpkg-architecture -qDEB_BUILD_ARCH) DATASTAGE = $(CURDIR)/data-stage -SEARCH_PLUGINS = $(shell cat \ - $(firstword $(wildcard $(LOCALE_SRCDIR)/searchplugins/list.txt) \ - @srcdir@/en-US/searchplugins/list.txt ) ) - -tmp-search.jar.mn:: - printf "$(AB_CD).jar:" > $@ - printf "$(foreach plugin,$(SEARCH_PLUGINS),$(subst __PLUGIN_SUBST__,$(plugin), \n locale/$(AB_CD)/browser/searchplugins/__PLUGIN_SUBST__.xml (__PLUGIN_SUBST__.xml)))" >> $@ - @echo >> $@ - -searchplugins: tmp-search.jar.mn - $(PYTHON) $(MOZILLA_DIR)/config/JarMaker.py \ - $(QUIET) -j $(FINAL_TARGET)/chrome \ - -s $(topsrcdir)/$(relativesrcdir)/en-US/searchplugins \ - -s $(LOCALE_SRCDIR)/searchplugins \ - $(MAKE_JARS_FLAGS) tmp-search.jar.mn - -export:: searchplugins - -GARBAGE += tmp-search.jar.mn - include $(topsrcdir)/config/rules.mk include $(topsrcdir)/toolkit/locales/l10n.mk @@ -108,23 +80,13 @@ clobber-zip: $(STAGEDIST)/chrome/$(AB_CD) libs-%: - $(NSINSTALL) -D $(DIST)/install - @$(MAKE) -C ../../toolkit/locales libs-$* BOTH_MANIFESTS=1 - @$(MAKE) -C ../../services/sync/locales AB_CD=$* XPI_NAME=locale-$* BOTH_MANIFESTS=1 - @$(MAKE) -C ../../intl/locales AB_CD=$* XPI_NAME=locale-$* BOTH_MANIFESTS=1 - @$(MAKE) -B bookmarks.json AB_CD=$* - @$(MAKE) -B searchplugins AB_CD=$* XPI_NAME=locale-$* + @$(MAKE) -C $(DEPTH)/mobile/locales libs-$* BOTH_MANIFESTS=1 @$(MAKE) libs AB_CD=$* XPI_NAME=locale-$* PREF_DIR=defaults/preferences BOTH_MANIFESTS=1 - @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales AB_CD=$* XPI_NAME=locale-$* BOTH_MANIFESTS=1 # Tailored target to just add the chrome processing for multi-locale builds chrome-%: - @$(MAKE) -C $(DEPTH)/toolkit/locales chrome-$* - @$(MAKE) -C $(DEPTH)/services/sync/locales chrome AB_CD=$* - @$(MAKE) -B bookmarks.json AB_CD=$* - @$(MAKE) -B searchplugins AB_CD=$* + @$(MAKE) -C $(DEPTH)/mobile/locales chrome-$* @$(MAKE) chrome AB_CD=$* - @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales chrome AB_CD=$* ifeq ($(OS_TARGET),Android) @$(MAKE) -C $(DEPTH)/embedding/android chrome AB_CD=$* endif @@ -135,17 +97,6 @@ endif installers-%: clobber-% langpack-% repackage-zip-% @echo "repackaging done" -NO_JA_JP_MAC_AB_CD := $(if $(filter ja-JP-mac, $(AB_CD)),ja,$(AB_CD)) - -bookmarks.json: bookmarks.inc generic/profile/bookmarks.json.in - $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ - -I $< \ - -DAB_CD=$(NO_JA_JP_MAC_AB_CD) \ - $(srcdir)/generic/profile/bookmarks.json.in \ - > $@ - -export:: bookmarks.json - ifdef MOZ_UPDATER ifdef LOCALE_MERGEDIR UPDATER_INI := $(firstword $(wildcard $(LOCALE_MERGEDIR)/updater/updater.ini) \ diff --git a/mobile/xul/locales/en-US/chrome/overrides/appstrings.properties b/mobile/xul/locales/en-US/chrome/overrides/appstrings.properties deleted file mode 100644 index 87e9538f7a69..000000000000 --- a/mobile/xul/locales/en-US/chrome/overrides/appstrings.properties +++ /dev/null @@ -1,68 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -malformedURI=The URL is not valid and cannot be loaded. -fileNotFound=Firefox can't find the file at %S. -dnsNotFound=Firefox can't find the server at %S. -protocolNotFound=Firefox doesn't know how to open this address, because the protocol (%S) isn't associated with any program. -connectionFailure=Firefox can't establish a connection to the server at %S. -netInterrupt=The connection to %S was interrupted while the page was loading. -netTimeout=The server at %S is taking too long to respond. -redirectLoop=Firefox has detected that the server is redirecting the request for this address in a way that will never complete. -## LOCALIZATION NOTE (confirmRepostPrompt): In this item, don't translate "%S" -confirmRepostPrompt=To display this page, %S must send information that will repeat any action (such as a search or order confirmation) that was performed earlier. -resendButton.label=Resend -unknownSocketType=Firefox doesn't know how to communicate with the server. -netReset=The connection to the server was reset while the page was loading. -notCached=This document is no longer available. -netOffline=Firefox is currently in offline mode and can't browse the Web. -isprinting=The document cannot change while Printing or in Print Preview. -deniedPortAccess=This address uses a network port which is normally used for purposes other than Web browsing. Firefox has canceled the request for your protection. -proxyResolveFailure=Firefox is configured to use a proxy server that can't be found. -proxyConnectFailure=Firefox is configured to use a proxy server that is refusing connections. -contentEncodingError=The page you are trying to view cannot be shown because it uses an invalid or unsupported form of compression. -unsafeContentType=The page you are trying to view cannot be shown because it is contained in a file type that may not be safe to open. Please contact the website owners to inform them of this problem. -externalProtocolTitle=External Protocol Request -externalProtocolPrompt=An external application must be launched to handle %1$S: links.\n\n\nRequested link:\n\n%2$S\n\nApplication: %3$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n -#LOCALIZATION NOTE (externalProtocolUnknown): The following string is shown if the application name can't be determined -externalProtocolUnknown= -externalProtocolChkMsg=Remember my choice for all links of this type. -externalProtocolLaunchBtn=Launch application -malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences. -phishingBlocked=The website at %S has been reported as a web forgery designed to trick users into sharing personal or financial information. -cspFrameAncestorBlocked=This page has a content security policy that prevents it from being embedded in this way. -corruptedContentError=The page you are trying to view cannot be shown because an error in the data transmission was detected. -remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox. diff --git a/mobile/xul/locales/en-US/chrome/overrides/netError.dtd b/mobile/xul/locales/en-US/chrome/overrides/netError.dtd deleted file mode 100644 index 10687d275592..000000000000 --- a/mobile/xul/locales/en-US/chrome/overrides/netError.dtd +++ /dev/null @@ -1,172 +0,0 @@ - -%brandDTD; - - - - - - - - - - - - - - -
  • Check the address for typing errors such as - ww.example.com instead of - www.example.com
  • -
  • If you are unable to load any pages, check your device's data or Wi-Fi connection.
  • - -"> - - - -
  • Check the file name for capitalization or other typing errors.
  • -
  • Check to see if the file was moved, renamed or deleted.
  • - -"> - - - -&brandShortName; can't load this page for some reason.

    -"> - - - -
  • Web addresses are usually written like - http://www.example.com/
  • -
  • Make sure that you're using forward slashes (i.e. - /).
  • - -"> - - - - - -The requested document is not available in &brandShortName;'s cache.

    • As a security precaution, &brandShortName; does not automatically re-request sensitive documents.
    • Click Try Again to re-request the document from the website.
    "> - - - -
  • Try again. &brandShortName; will attempt to open a connection and reload the page.
  • - -"> - - - -
  • Please contact the website owners to inform them of this problem.
  • - -"> - - - -
  • Please contact the website owners to inform them of this problem.
  • - -"> - - - - - - - - - -
  • You might need to install other software to open this address.
  • - -"> - - - -
  • Check the proxy settings to make sure that they are correct.
  • -
  • Contact your network administrator to make sure the proxy server is - working.
  • - -"> - - - -
  • Check the proxy settings to make sure that they are correct.
  • -
  • Check to make sure your device has a working data or Wi-Fi connection.
  • - -"> - - - -
  • This problem can sometimes be caused by disabling or refusing to accept - cookies.
  • - -"> - - - -
  • Check to make sure your system has the Personal Security Manager - installed.
  • -
  • This might be due to a non-standard configuration on the server.
  • - -"> - - - -
  • The page you are trying to view can not be shown because the authenticity of the received data could not be verified.
  • -
  • Please contact the website owners to inform them of this problem. Alternatively, use the command found in the help menu to report this broken site.
  • - -"> - - - -
  • This could be a problem with the server's configuration, or it could be -someone trying to impersonate the server.
  • -
  • If you have connected to this server successfully in the past, the error may -be temporary, and you can try again later.
  • - -"> - - -
  • The site could be temporarily unavailable or too busy. Try again in a few moments.
  • -
  • If you are unable to load any pages, check your mobile device's data or Wi-Fi connection.
  • - -"> - - -&brandShortName; prevented this page from loading in this way because the page has a content security policy that disallows it.

    "> - - -The page you are trying to view cannot be shown because an error in the data transmission was detected.

    • Please contact the website owners to inform them of this problem.
    "> - - - - - - - -You should not add an exception if you are using an internet connection that you do not trust completely or if you are not used to seeing a warning for this server.

    - - - -"> - - -
    • Please contact the website owners to inform them of this problem.

    "> - diff --git a/mobile/xul/locales/en-US/chrome/overrides/passwordmgr.properties b/mobile/xul/locales/en-US/chrome/overrides/passwordmgr.properties deleted file mode 100644 index 775ddd85911e..000000000000 --- a/mobile/xul/locales/en-US/chrome/overrides/passwordmgr.properties +++ /dev/null @@ -1,72 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Password Manager. -# -# The Initial Developer of the Original Code is -# Brian Ryner. -# Portions created by the Initial Developer are Copyright (C) 2003 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Brian Ryner -# Ehsan Akhgari -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -rememberValue = Use Password Manager to remember this value. -rememberPassword = Use Password Manager to remember this password. -savePasswordTitle = Confirm -# 1st string is product name, 2nd is the username for the login, 3rd is the -# login's hostname. Note that long usernames may be truncated. -saveLoginText = Do you want %1$S to remember the password for "%2$S" on %3$S? -# 1st string is product name, 2nd is the login's hostname -saveLoginTextNoUsername = Do you want %1$S to remember this password on %2$S? -notNowButtonText = &Not Now -notifyBarNotNowButtonText = Not Now -notifyBarNotNowButtonAccessKey = -neverForSiteButtonText = Ne&ver for This Site -notifyBarNeverForSiteButtonText = Never -notifyBarNeverForSiteButtonAccessKey = -rememberButtonText = &Remember -notifyBarRememberButtonText = Remember -notifyBarRememberButtonAccessKey = -passwordChangeTitle = Confirm Password Change -passwordChangeText = Would you like to change the stored password for %S? -passwordChangeTextNoUser = Would you like to change the stored password for this login? -notifyBarChangeButtonText = Change -notifyBarChangeButtonAccessKey = -notifyBarDontChangeButtonText = Don't Change -notifyBarDontChangeButtonAccessKey = -userSelectText = Please confirm which user you are changing the password for -hidePasswords=Hide Passwords -hidePasswordsAccessKey=P -showPasswords=Show Passwords -showPasswordsAccessKey=P -noMasterPasswordPrompt=Are you sure you wish to show your passwords? -removeAllPasswordsPrompt=Are you sure you wish to remove all passwords? -removeAllPasswordsTitle=Remove all passwords -loginsSpielAll=Passwords for the following sites are stored on your computer: -loginsSpielFiltered=The following passwords match your search: diff --git a/mobile/xul/locales/en-US/chrome/region.properties b/mobile/xul/locales/en-US/chrome/region.properties deleted file mode 100644 index 2a7a85234bc3..000000000000 --- a/mobile/xul/locales/en-US/chrome/region.properties +++ /dev/null @@ -1,32 +0,0 @@ -# Default search engine -browser.search.defaultenginename=Google - -# Search engine order (order displayed in the search bar dropdown)s -browser.search.order.1=Google - -# This is the default set of web based feed handlers shown in the reader -# selection UI -browser.contentHandlers.types.0.title=My Yahoo -browser.contentHandlers.types.0.uri=http://add.my.yahoo.com/rss?url=%s -browser.contentHandlers.types.1.title=Google -browser.contentHandlers.types.1.uri=http://fusion.google.com/add?feedurl=%s - -# Keyword URL (for location bar searches) -keyword.URL=http://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q= - -# increment this number when anything gets changed in the list below. This will -# cause Firefox to re-read these prefs and inject any new handlers into the -# profile database. Note that "new" is defined as "has a different URL"; this -# means that it's not possible to update the name of existing handler, so -# don't make any spelling errors here. -gecko.handlerService.defaultHandlersVersion=2 - -# The default set of protocol handlers for webcal: -gecko.handlerService.schemes.webcal.0.name=30 Boxes -gecko.handlerService.schemes.webcal.0.uriTemplate=http://30boxes.com/external/widget?refer=ff&url=%s - -# The default set of protocol handlers for mailto: -gecko.handlerService.schemes.mailto.0.name=Yahoo! Mail -gecko.handlerService.schemes.mailto.0.uriTemplate=http://compose.mail.yahoo.com/?To=%s -gecko.handlerService.schemes.mailto.1.name=Gmail -gecko.handlerService.schemes.mailto.1.uriTemplate=https://mail.google.com/mail/?extsrc=mailto&url=%s diff --git a/mobile/xul/locales/en-US/installer/setup.ini b/mobile/xul/locales/en-US/installer/setup.ini deleted file mode 100644 index 1ed7de333fd8..000000000000 --- a/mobile/xul/locales/en-US/installer/setup.ini +++ /dev/null @@ -1,17 +0,0 @@ -; This file is in the UTF-8 encoding -[Strings] -AppShortName=%MOZ_APP_DISPLAYNAME% -AppLongName=Mozilla %MOZ_APP_DISPLAYNAME% -WindowCaption=Mozilla %MOZ_APP_DISPLAYNAME% Setup -InstallTo=Install %MOZ_APP_DISPLAYNAME% to -Install=Install -Cancel=Cancel -InstalledSuccessfully=Mozilla %MOZ_APP_DISPLAYNAME% has been installed successfully. -ExtractionError=Archive extraction error: -ThereWereErrors=There were errors during installation: -CreatingUserProfile=Creating user profile. Please wait... -UninstallCaption=Mozilla %MOZ_APP_DISPLAYNAME% Uninstall -FilesWillBeRemoved=All files will be removed from -AreYouSure=Are you sure? -InstallationNotFound=Mozilla %MOZ_APP_DISPLAYNAME% installation not found. -UninstalledSuccessfully=Mozilla %MOZ_APP_DISPLAYNAME% has been uninstalled successfully. diff --git a/mobile/xul/locales/en-US/profile/bookmarks.inc b/mobile/xul/locales/en-US/profile/bookmarks.inc deleted file mode 100644 index 85091c79c041..000000000000 --- a/mobile/xul/locales/en-US/profile/bookmarks.inc +++ /dev/null @@ -1,30 +0,0 @@ -#filter emptyLines - -# LOCALIZATION NOTE: The 'en-US' strings in the URLs will be replaced with -# your locale code, and link to your translated pages as soon as they're -# live. - -# LOCALIZATION NOTE: Some of these URLs are currently 404s, but should be coming -# online shortly. - -# LOCALIZATION NOTE (bookmarks_title): -# title for the folder that will contains the default bookmarks -#define bookmarks_title Mobile - -# LOCALIZATION NOTE (bookmarks_aboutBrowser): -# link title for about:fennec -#define bookmarks_aboutBrowser Firefox: About your browser - -# LOCALIZATION NOTE (bookmarks_addons): -# link title for https://addons.mozilla.org/en-US/mobile -#define bookmarks_addons Firefox: Customize with add-ons - -# LOCALIZATION NOTE (bookmarks_support): -# link title for https://support.mozilla.com/mobile -#define bookmarks_support Firefox: Support - -# LOCALIZATION NOTE (bookmarks_aboutHome): -# link title for about:home -#define bookmarks_aboutHome Firefox Start - -#unfilter emptyLines diff --git a/mobile/xul/locales/en-US/searchplugins/amazondotcom.xml b/mobile/xul/locales/en-US/searchplugins/amazondotcom.xml deleted file mode 100644 index a58e4c46e1ee..000000000000 --- a/mobile/xul/locales/en-US/searchplugins/amazondotcom.xml +++ /dev/null @@ -1,12 +0,0 @@ - -Amazon.com -ISO-8859-1 -data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZJREFUeNrEV01IFGEYfndXNzq0QdalJOoUbQiBUuopD2k/GFSG/Vxyq0OHbCUjwn7AEooUzUNRkdkhS1o9JBVaUDctKqhoRQi0WjtlxG6XVtfpfb6Z2caZ2ZnZXZdeeJxvx+973+f9+775XJIkkUaWMcoYG5TxfMpvxlvGMGM8+RYEFOxn/JJyLzOMiwwv7LqUCNQwHjpxIxKJ0Gg4LABZUVhIfr+f1jLSlNuMwyCwEHoZS6xmvxoZoc6Oq+JpJoVMpOncWdpcWZkOiRIQ2MKDp1az+kIhOtV40pHG6zdvpEOixc1/VtqFvKX5gmOX0pkL2yDgsZpxt+sORaPROe8Q6ncf3tPAk8eG3Ks14lA8brsZYZ2yukBAwOfzCeMHA3WGNXrCVpLnJKeqRyhAfX5RfNmILQF4urG0VIzxBBkU5aQI9agIeU4JqNLZ0UH9ob6sDWZE4MDefSn7P1txO/FcbxypQG18nhinew/u5zYC3dyG+qLL1qjjCKDg9C21q2a3oe9zRsCsn2PR2JzfKEy9PB96Nj8E0IJm54IaGZwPZsWJOU4jY1kD2OlAQhsJjKu3bSe7yPUzifpgMPsuOBY8brtR1evmyFt0IL0IzH4fJtcCH7kK1hn2/hh71G1yKKEdz/DBBIOTkUkRemzVl1uvCGKzE4OMIaFbiv1LSX51L7mXl4kxvgeO8vMaJk0PHiHvjl4DCTWs2lMOX0L6cwD/Bxlp6hNNv2gUT9MjsLiB8koaMOxJRgCMgPijWsqvaCP3qqqUZ4JVzUASYyFyL/WTqyggPxWH4qGtBlJzagCGIYjEzJv2zHe38vOUt6mNPGv20OyPMMV7yuVwL5IjBlLmRej1UX7VLdmLt+2CMVKTiSDn0wO1NPPyBOtdLL+MyzWkTbGhC5AGsBdKkEtWAjglgjUwCq/FGjhV0ZosdOhXI5FyH0DoIIK9slB0CS8UCgr8AlpvpamwqHpttcO4WtSqA57ioKENE05IqIYSY46uD4Is0qmGG2s9RYeS7adI3Paz3K6lTMmzIdFmXp/d1Gb1YvLF7i4IzxMfu1ITYWOe1VWix7U5tlKpXkzwYycONqfVLcW+cU7lQ0jePf360DqRS4zT/+Ny+ofRxPBoL6fa6zmu5uvtbkwZyE/lev6a8VV9+VeAAQADg36zc4GRNAAAAABJRU5ErkJggg== - - - - - - -http://www.amazon.com/ - diff --git a/mobile/xul/locales/en-US/searchplugins/google.xml b/mobile/xul/locales/en-US/searchplugins/google.xml deleted file mode 100644 index 721939748da3..000000000000 --- a/mobile/xul/locales/en-US/searchplugins/google.xml +++ /dev/null @@ -1,15 +0,0 @@ - -Google -UTF-8 -data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABvxJREFUeNqUV1tsFVUUXWdm7qMPaKEUChJekgBioFEehhJBEkUjQQkGP5QPSSDCD4nhYZAPYiQaQyTyowJRCBoxEhOx+IMi5aFG5VEIELGFUiilFFrae3sf8zjHfc7M3Dtz70VkyOYMc4ez1l57n733MCEE6FJ/yevg+T5s+qETlgOYpg0zbWJpfRV2vP4oCq/uFSsh9uxBmeCIgsOgZ8wzf1Pahn4BbM8sMtP7fZQQTAuCn76RxruH70KPx6HFYmCe7f+rF5/+fLOIQO3nu6DNfwYOy8OyAhLBNWiSyDXGhBbcsPFiEiISAQwyfyWTz94/1IFS1+Dt22gznbxkeU+CXj3gWYjA9Xs2HMFINgYuoDblJJJgOnqzAl+e7CraIDp9GlBfD5tU8Encz1Di3yECtsNh2VyttiPgSCMmihC9+v3puyVVMObPIxU0FeuHMUnCCBGgzDNNh7wWlIQCtu0QCe6SoLePXrxXkgCqq3JJVpiE/0UAhQRGVOowsxlwTVcK2FINIiFXqUZvximJL6W34YZOBkELyMwLToETsKIQzBpTBjNjEQkLFh1BZZbjKsHFfdIIyLRdow2JsDKEzCkALvwtRGDh1CqUMcclIWsAgVsUEhka0FlnojSBRNNx8lKEzrpVcG8VPPctFIKqMh0rG2rw3o+36GxrsMlrLoPPCZw7ePmJoUXgt/d+BbPtKjJ1MVxpGIqeEXG0T6iASbqb9H+NhI1RrUmMupLEpAt9iA7YOSKqPggRdqsvZWPGlrO40mOraMqYSvChMY6f3p6OaWMH5ZP2Xh++WTUfF5dUIjk6RpsKZIl0loBlumTkartrWj0TmHOkC0sOtKP6TpaqJxHoHbBEdXlICDS3J7Fi12Wcuz6gZH+JPN/22kSMHRbPvZOykvjwxAbctVtQVkZKaZzAPVAf3LtX4LYk4ZGh+zlNXXhrdwtY7apjYvfKyVg8oxb/92rrbcXWY+upCiUUeJQKpjy6pkcgGyCQtgP3niJp716dgj6ivWzHeSzb3oxz1xIPBL/S04rNR9YhpSUgDEo8jYCFC2559cIR7ioPjlB/vHvhHc9AOTR0TajS0XjmDg6d6sLYmhjG1USx6MnhWPNicQfc17wPSUGeK3B3Z9vblJRVJKhs5Ej4xj0SXBQUooig1GHugaTmhI6eFDq7+zFvSvV9io573i3h7iY7IfO840I+lySgSNi+InLlyKnjE1EhiPEsYtxEVJiQZKKUyxGyS22l6/7qmWswJFYHqlXI2kwllzQ/8+XRM7kfEo+MKFQkf6/FnDR8i6s1Q4QyOPJ7Ky5eLSZRVzkCnyz8DJMG1yOdoYSi6UKRcFwSMgGl5Um4IbF8RXwluGv68Mde2GI4FgzHVNYwpQYNk4fh+ZmjEY8wTBg9pLgF61E8N34hOvq78DclpSqxKgeE67UXBkkitxY8k8RUNxxXHcHyRdMwa+ooTB4/DA9zbZy9AdNqp2Prnx+A0VGU441qPCLvteWTcERRbpSshKH2nOmGlaZJiELDjEGIVz9e8r2dzXux88IXNDmRV0y4igS8thw3HLJKmo67ilIEuNWPVHcTMj1nkLl3DproA7M7ILI3qUMyDJq4CTVT15ck8crBlbiUuKxqg+yMjggrYAXAg401RyDdfRI951bDEB2IxsqhGzp0MUCsknDooJtZA6lUBJGRyzHyqY+LCBxuO4E3f3lHqeAw7qkALxThQhXs6qoJpG7/iq4Ti1FZYSIep2NIVU4NWHLaJdM0akoGVYAox0D71+iwdTwy96MQgWfHzUWGToQKgeb1fy8MPgleYmpV88Ct39YiTgBRXdB8S4WFE7CjQ5Y6oXYjAlStDNpcvpO6egADnX8UqSBrg+p+Xm3IqCMpVALyoimVKdPM5A3wgesEDAUsCJSTh45FZhveqqtn1PJIHY3IGki0HCyejEJd0K0L0vvQp49qDCxXjw0rQVkudPJUggj1I3eVd5sHsRSSGIEzHiGicVIiCid5u1gBb4DhKJhMEfQ6MCzKHKgYOZs2LicCpLRFkmuO6gn5WMkoUWCEQUYfKYJmAlGGyjELQuDf/3NCtWSBEsAoAc5ZPgcGjX+VwCtJ/gpiUe4aL1PENEFGq7wHryCViCxqUTF2fojAjlPfEleRl9sHksAByd2PxUAI5PvDZm1EJw0ZZuICfZHZ0Gm6Ycx3QHNDRAo4tmxTNRg8Yy2MyroceFP7WRztOAM1YyHvXdGnkAgQ8d7J1QFuJpFsaUSqtRFO32VoJCdjzJOOEtCoQnT00xhc/0YIvLmrBQv2r0Uv71dFSPVmVvg9xvKfQkE1PAJy7ZXfN6GM7jzjJRIdlWglokMnFiVdU9tZLP1uswfOVQhccFbsPWd5QtwNvlh3nP0rwACsnu1i3ddmfAAAAABJRU5ErkJggg== - - - - - - - - - -http://www.google.com/m - diff --git a/mobile/xul/locales/en-US/searchplugins/list.txt b/mobile/xul/locales/en-US/searchplugins/list.txt deleted file mode 100644 index f241bfebff64..000000000000 --- a/mobile/xul/locales/en-US/searchplugins/list.txt +++ /dev/null @@ -1,4 +0,0 @@ -amazondotcom -google -twitter -wikipedia diff --git a/mobile/xul/locales/en-US/searchplugins/twitter.xml b/mobile/xul/locales/en-US/searchplugins/twitter.xml deleted file mode 100644 index 2609568cb3c5..000000000000 --- a/mobile/xul/locales/en-US/searchplugins/twitter.xml +++ /dev/null @@ -1,9 +0,0 @@ - - Twitter - data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAADTppVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDQuMi4yLWMwNjMgNTMuMzUyNjI0LCAyMDA4LzA3LzMwLTE4OjA1OjQxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOklwdGM0eG1wQ29yZT0iaHR0cDovL2lwdGMub3JnL3N0ZC9JcHRjNHhtcENvcmUvMS4wL3htbG5zLyIKICAgeG1wUmlnaHRzOldlYlN0YXRlbWVudD0iIgogICBwaG90b3Nob3A6QXV0aG9yc1Bvc2l0aW9uPSIiPgogICA8ZGM6cmlnaHRzPgogICAgPHJkZjpBbHQ+CiAgICAgPHJkZjpsaSB4bWw6bGFuZz0ieC1kZWZhdWx0Ii8+CiAgICA8L3JkZjpBbHQ+CiAgIDwvZGM6cmlnaHRzPgogICA8ZGM6Y3JlYXRvcj4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkvPgogICAgPC9yZGY6U2VxPgogICA8L2RjOmNyZWF0b3I+CiAgIDxkYzp0aXRsZT4KICAgIDxyZGY6QWx0PgogICAgIDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCI+dHdpdHRlci10LXNvbG88L3JkZjpsaT4KICAgIDwvcmRmOkFsdD4KICAgPC9kYzp0aXRsZT4KICAgPHhtcFJpZ2h0czpVc2FnZVRlcm1zPgogICAgPHJkZjpBbHQ+CiAgICAgPHJkZjpsaSB4bWw6bGFuZz0ieC1kZWZhdWx0Ii8+CiAgICA8L3JkZjpBbHQ+CiAgIDwveG1wUmlnaHRzOlVzYWdlVGVybXM+CiAgIDxJcHRjNHhtcENvcmU6Q3JlYXRvckNvbnRhY3RJbmZvCiAgICBJcHRjNHhtcENvcmU6Q2lBZHJFeHRhZHI9IiIKICAgIElwdGM0eG1wQ29yZTpDaUFkckNpdHk9IiIKICAgIElwdGM0eG1wQ29yZTpDaUFkclJlZ2lvbj0iIgogICAgSXB0YzR4bXBDb3JlOkNpQWRyUGNvZGU9IiIKICAgIElwdGM0eG1wQ29yZTpDaUFkckN0cnk9IiIKICAgIElwdGM0eG1wQ29yZTpDaVRlbFdvcms9IiIKICAgIElwdGM0eG1wQ29yZTpDaUVtYWlsV29yaz0iIgogICAgSXB0YzR4bXBDb3JlOkNpVXJsV29yaz0iIi8+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz6sMuh4AAADGElEQVR42tRXT2jTYBT/0tR0Sek2RIvr/NMq66RTOj3sMD1UKuxg9SQep8gugruoh6nbaRfBo6CgF93NHqUgooIgKpYhLWKZ3aTVTZG6dd26Nm3WNn6vS/QjJjVtuooPHs3H95L36/v/KFEUkRoFnmXs+MeD2YmZIa7imGOh450p1ASi1ABg5QPeDrN/uIcN7LGZ+ttoipPvPmbKr6dm+VB0pfQcgwgbRgAASD7xdHngZiQ3yZcqOVGD4C6ULNwHWeX79bJSuf1aOHu1lnISBADF7+w0AsCsMIgHzC6bfEUQ0Y1IDr1fLVUvD7ab0Vi/FXUwFAKZ03stgRc/hJf4aqFRDygBOMHn8oFUDgTP49Nr6NagbUPYRoOsE8eMQ+XbizhGhHoBMGTAkcplSuTL6Guugrqtpur5iofzz62U3Uq58NL6AgaWwY/JWlljbsRsKf43AJ+DOeNT+f8jiEWFspj/nK1EIGswGNWsMaFNJLBmbyc9OHHYOu7bzpyE9G4pABLIxQPsJagtUoEz5oKJyJouOWXWQIZdns6+AS+2xAIQxJBJMkkZ5mypC8hMkjKMaXkM1KJNBwBx8M8AyEFIdlKpnRvLgvP7WOSy0TVl7KzpV7ECgqIEBQmqomEAoPzQNv2vgvJHyeIDaYZIGQZQDyWz5ci9GT6oNcD8n1kQ+lLULQstG3rBsKvtnFovULpAAH/JLdnF0dX2q6S36XU0+S6HArstuoIQvnfKaTkbTZfmMYgkGQdKAN++5ytxadBAx7oYlPjEqyoAEMDN7gXJD+lSVD74uxlkpamm9QJozfjHrRkD2DTxO7P8k6wgLsEZkI/u55rWC/QGYezxfDEoH452bUGjvVzrsgBbITqVKASlslmloV0MGuuzNuQOshdAgEOg60nDGA6W6yQIsMTdI+0bZZijG+oFMB9KQ6r2ZkQuKZh9M8ulV2ITCBYZWHrgu5qbkRaIh3P87dViZdGIcmmL+mOVo7S2Y8Wy6oWt6UIPO9S31ezdwZnc5P5QqwnJY7lWL9AFgADilmY6h3K00iDhb4vJTwEGANC+26MCs7qQAAAAAElFTkSuQmCC - - - - UTF-8 - http://mobile.twitter.com/searches - diff --git a/mobile/xul/locales/en-US/searchplugins/wikipedia.xml b/mobile/xul/locales/en-US/searchplugins/wikipedia.xml deleted file mode 100644 index 171c0f501aef..000000000000 --- a/mobile/xul/locales/en-US/searchplugins/wikipedia.xml +++ /dev/null @@ -1,14 +0,0 @@ - -Wikipedia -UTF-8 -data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAACXBIWXMAAAsTAAALEwEAmpwYAAADGGlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjaY2BgnuDo4uTKJMDAUFBUUuQe5BgZERmlwH6egY2BmYGBgYGBITG5uMAxIMCHgYGBIS8/L5UBFTAyMHy7xsDIwMDAcFnX0cXJlYE0wJpcUFTCwMBwgIGBwSgltTiZgYHhCwMDQ3p5SUEJAwNjDAMDg0hSdkEJAwNjAQMDg0h2SJAzAwNjCwMDE09JakUJAwMDg3N+QWVRZnpGiYKhpaWlgmNKflKqQnBlcUlqbrGCZ15yflFBflFiSWoKAwMD1A4GBgYGXpf8EgX3xMw8BSMDVQYqg4jIKAUICxE+CDEESC4tKoMHJQODAIMCgwGDA0MAQyJDPcMChqMMbxjFGV0YSxlXMN5jEmMKYprAdIFZmDmSeSHzGxZLlg6WW6x6rK2s99gs2aaxfWMPZ9/NocTRxfGFM5HzApcj1xZuTe4FPFI8U3mFeCfxCfNN45fhXyygI7BD0FXwilCq0A/hXhEVkb2i4aJfxCaJG4lfkaiQlJM8JpUvLS19QqZMVl32llyfvIv8H4WtioVKekpvldeqFKiaqP5UO6jepRGqqaT5QeuA9iSdVF0rPUG9V/pHDBYY1hrFGNuayJsym740u2C+02KJ5QSrOutcmzjbQDtXe2sHY0cdJzVnJRcFV3k3BXdlD3VPXS8Tbxsfd99gvwT//ID6wIlBS4N3hVwMfRnOFCEXaRUVEV0RMzN2T9yDBLZE3aSw5IaUNak30zkyLDIzs+ZmX8xlz7PPryjYVPiuWLskq3RV2ZsK/cqSql01jLVedVPrHzbqNdU0n22VaytsP9op3VXUfbpXta+x/+5Em0mzJ/+dGj/t8AyNmf2zvs9JmHt6vvmCpYtEFrcu+bYsc/m9lSGrTq9xWbtvveWGbZtMNm/ZarJt+w6rnft3u+45uy9s/4ODOYd+Hmk/Jn58xUnrU+fOJJ/9dX7SRe1LR68kXv13fc5Nm1t379TfU75/4mHeY7En+59lvhB5efB1/lv5dxc+NH0y/fzq64Lv4T8Ffp360/rP8f9/AA0ADzT6lvFdAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAHqSURBVHjapJUxb9NQFIW/JAip2xNS1/J+goeqUqeajdGVmFgwI1OcmYEgITFG7EgJQ+b2HzgbElJF+QU2iBl7YAFRnQ5+fnFcB0J8Jp/r53vPu+f6vYEA4JBTTjhkN/zkio9kAAihpyr1v/ijN7ovEHqiffFeDHTAdx6wL46HnPX4HM6HHNEHR0NGvRKMhvRE7wT3ACYssBggpyThIRMCAFaEzHhOiQWumfGFBQGQY0kB9EKSCsVCyKpQxUMhdOEMj4Vi91wIGX2uyNJtwTDHAgbjOUBJ/T50ETDAzCn0CiRpLkSd2VW1rqZR6uOpzHoWl40EkmkIlTIhNJc0bUSlSNNtCaZCKGtpyDZimXB92uhBhTEAHzx/BuQ8Isb62Dti16c7PahrNnbovFhXLGQaau4oqDSULDx/BcCl55cEDTUdCqqagWcXbjpqWD8ZWxRUGq5ZOTYhAXLHV0DU/qCtQLJCkZuM0PHQGThvL152JJh5M61SP2CpMpmmgdsTFDJCiaaubq0haQ7Q3xLUZq4NqzS0DNzWxNq8ksgbFmGgwf95oFgijJuB6v9L3Fx2HSg3XeExZxv1xuSEXQt/o8fqg9cDHfB15zuxjRuOETrfu/5b9bhcf+mlRmLgr/cTgp1vqR9c8YlvALcDAPr5jIx+4LKcAAAAAElFTkSuQmCC - - - - - - - - -http://en.wikipedia.org/wiki/Special:Search - diff --git a/mobile/xul/locales/en-US/searchplugins/yahoo.xml b/mobile/xul/locales/en-US/searchplugins/yahoo.xml deleted file mode 100644 index ed3a4c3085c3..000000000000 --- a/mobile/xul/locales/en-US/searchplugins/yahoo.xml +++ /dev/null @@ -1,13 +0,0 @@ - -Yahoo -UTF-8 -data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAA7xJREFUeNrs10tslFUUB/DfN9NCaUsir2JAQAQKBgQXiFEeC6JdKAQCoiERXKiJrmRhNMZH3LjQjaILIiqKCWp8BgL4IGpQWkASCY+qWHm0CFjKyz5oO9PO56LfDNNhpmXHwp7ky9w5c+49//ud//nfO0EYhq6nxVxnGwAwAOC6Ayh6IVjfX8wEzMBMlCDdt2fwM+rR1s8a03AIR3AAv+EYGov6mLQKq3FPP4s34AN8gt8LxAxBEaZHT9rq85XgiWh3H15DchiPl6JdfRQly7XLBebuyAXwBdaFwhtTQt1SmSclFOpXNVeiCVNy/Mk8sQfweTaAT7GsZxhkEnfp1qVbiFAQffZpZVEpKrJ8+absQU2aAw9hRUpK+YhSyzbOVzFjpNbGNsnWpGRnShjS3NBq85PVOiV6vY1AICZQrEhMAHFsxZwoJJEHwHdoKcJ9eDG9UKIlac+6WsMm3mDJW/My0V89+pOTv5zVrsOtd08wbu5o7Rc7BAFDRpQoLok7uOmoprpLYj3dfQfmYRdO5QGwTcTMx9LMDASSiS4Htx3XptPx7aetOfogqFw81jcb9goQL467ac4otz0wCbQ2tavffcb+9+tyebIiApBr9ehMC9G07F8CgVKDDVeu7thJG6u2g+lLJ1n+zAKdkqp31jq1r6lnG0/XeLniPeuXbHG+vkVcPHu5+QV4sjUjREjll8iYcqV27zhs5FPD3L/2LlWv3qnx4L9mrp5o1sop1s/d7EDNX8qUKjeop/q9bWgBAN9nA8grRgHiYsqV+fbNPW5eONr0JbdY9XWVjpaE1yo2aWg6a6gy8TT1rr3/f80+C+oK9VMgUCQmqcsPz+3P+Lc8vsuRpgalSsQKJ4fdeXx/RxzIAHgHfxR6CyG6hVKJ7oy//VynpJSUVF/J4bM8vkO9DiNswbBIz3tZKFQSG2TS+DEql4/L+CcvGisRT2qua3PheHME9So7nF1rjI5a88/soPgCi9OyOBuVvfUzZczM4R7+ssrY2aOc3n/OxRPNSkeWmHrveJdOtvin9mKhIsxGc9Z4MKpxIYugYfC8t3PPgmW9uiGIKR5SpP1yQjKS9Jj4Fc272lJYiJ1ZvjpMjsYfoxaz8G5uByzHGryeWS1M6bjcGdUrniFnDzuCfOr2CM5n+QZlJYdGvNLXjeiNaOVnsRfJtNbHxcQj3gdXkp/AhkhNF+UkF11ifkRr9L06l4SFbC32YWmk6eOyRCtAC2qiW9HW6A6Rz5qjs+Z2TM3tuGDgn9EAgP89gP8GAF3DLhLnfG5QAAAAAElFTkSuQmCC - - - - - - -http://search.yahoo.com/ - diff --git a/mobile/xul/locales/filter.py b/mobile/xul/locales/filter.py index b6596c8cce14..79598419cba9 100644 --- a/mobile/xul/locales/filter.py +++ b/mobile/xul/locales/filter.py @@ -35,30 +35,43 @@ # # ***** END LICENSE BLOCK ***** +"""This routine controls which localizable files and entries are +reported and l10n-merged. +It's common to all of mobile, mobile/android and mobile/xul, so +those three versions need to stay in sync. +""" def test(mod, path, entity = None): import re # ignore anything but mobile, which is our local repo checkout name if mod not in ("netwerk", "dom", "toolkit", "security/manager", - "services/sync", "embedding/android", + "services/sync", "mobile", + "mobile/android/base", "mobile/android", "mobile/xul"): - return False + return "ignore" - if mod != "mobile/xul": - # we only have exceptions for mobile - return True - if not entity: - return not (re.match(r"searchplugins\/.+\.xml", path) or - re.match(r"mobile-l10n.js", path) or - re.match(r"defines.inc", path)) - if path == "defines.inc": - return entity != "MOZ_LANGPACK_CONTRIBUTORS" + if mod not in ("mobile", "mobile/android", "mobile/xul"): + # we only have exceptions for mobile* + return "error" + if mod in ("mobile/android", "mobile/xul"): + if not entity: + if (re.match(r"mobile-l10n.js", path) or + re.match(r"defines.inc", path)): + return "ignore" + if path == "defines.inc": + if entity == "MOZ_LANGPACK_CONTRIBUTORS": + return "ignore" + return "error" - if path != "chrome/region.properties": - # only region.properties exceptions remain, compare all others - return True - - return not (re.match(r"browser\.search\.order\.[1-9]", entity) or - re.match(r"browser\.contentHandlers\.types\.[0-5]", entity) or - re.match(r"gecko\.handlerService\.schemes\.", entity) or - re.match(r"gecko\.handlerService\.defaultHandlersVersion", entity)) + # we're in mod == "mobile" + if re.match(r"searchplugins\/.+\.xml", path): + return "ignore" + if path == "chrome/region.properties": + # only region.properties exceptions remain + if (re.match(r"browser\.search\.order\.[1-9]", entity) or + re.match(r"browser\.contentHandlers\.types\.[0-5]", entity) or + re.match(r"gecko\.handlerService\.schemes\.", entity) or + re.match(r"gecko\.handlerService\.defaultHandlersVersion", entity)): + return "ignore" + + return "error" diff --git a/mobile/xul/locales/jar.mn b/mobile/xul/locales/jar.mn index 58723593baf6..6e5af8504f36 100644 --- a/mobile/xul/locales/jar.mn +++ b/mobile/xul/locales/jar.mn @@ -9,7 +9,6 @@ locale/@AB_CD@/browser/browser.properties (%chrome/browser.properties) locale/@AB_CD@/browser/config.dtd (%chrome/config.dtd) locale/@AB_CD@/browser/localepicker.properties (%chrome/localepicker.properties) - locale/@AB_CD@/browser/region.properties (%chrome/region.properties) locale/@AB_CD@/browser/preferences.dtd (%chrome/preferences.dtd) locale/@AB_CD@/browser/checkbox.dtd (%chrome/checkbox.dtd) locale/@AB_CD@/browser/notification.dtd (%chrome/notification.dtd) @@ -19,13 +18,3 @@ locale/@AB_CD@/browser/webapps.dtd (%chrome/webapps.dtd) locale/@AB_CD@/browser/feedback.dtd (%chrome/feedback.dtd) locale/@AB_CD@/browser/phishing.dtd (%chrome/phishing.dtd) - locale/@AB_CD@/browser/bookmarks.json (bookmarks.json) - locale/@AB_CD@/browser/searchplugins/list.txt (%searchplugins/list.txt) - -# Fennec-specific overrides of generic strings -* locale/@AB_CD@/browser/netError.dtd (%chrome/overrides/netError.dtd) -% override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd -* locale/@AB_CD@/browser/appstrings.properties (%chrome/overrides/appstrings.properties) -% override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties -* locale/@AB_CD@/browser/passwordmgr.properties (%chrome/overrides/passwordmgr.properties) -% override chrome://passwordmgr/locale/passwordmgr.properties chrome://browser/locale/passwordmgr.properties diff --git a/mobile/xul/locales/l10n.ini b/mobile/xul/locales/l10n.ini index 7922a78eacf0..a717ccf0072e 100644 --- a/mobile/xul/locales/l10n.ini +++ b/mobile/xul/locales/l10n.ini @@ -1,3 +1,7 @@ +# Control which directories and modules are part of mobile/xul. +# Changes here should be reflected in mobile/locales/l10n.ini so +# that the dashboard picks them up. + [general] depth = ../../.. all = mobile/xul/locales/all-locales From de733594f19cea501ddf03aa947b56f604d99847 Mon Sep 17 00:00:00 2001 From: Yury Date: Thu, 22 Dec 2011 11:46:51 +0100 Subject: [PATCH 02/26] Bug 712217 - Update OTS to r77 [r=jfkthame] --- gfx/ots/README.mozilla | 2 +- gfx/ots/src/cff.cc | 2 +- gfx/ots/src/ots.cc | 84 +++++++++++++++++++++--------------------- 3 files changed, 43 insertions(+), 45 deletions(-) diff --git a/gfx/ots/README.mozilla b/gfx/ots/README.mozilla index 2e6fc26a547b..8d439531d525 100644 --- a/gfx/ots/README.mozilla +++ b/gfx/ots/README.mozilla @@ -1,6 +1,6 @@ This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/. -Current revision: r74 +Current revision: r77 Applied local patches: ots-fix-vc10.patch - workaround for VS10 STL wrappers (bug 602558) diff --git a/gfx/ots/src/cff.cc b/gfx/ots/src/cff.cc index e8acd1d88806..73fc23399ca6 100644 --- a/gfx/ots/src/cff.cc +++ b/gfx/ots/src/cff.cc @@ -57,7 +57,7 @@ bool ParseIndex(ots::Buffer *table, ots::CFFIndex *index) { } if (index->count == 0) { // An empty INDEX. - index->offset_to_next = table->offset() + sizeof(index->count); + index->offset_to_next = table->offset(); return true; } diff --git a/gfx/ots/src/ots.cc b/gfx/ots/src/ots.cc index a934bbc7b609..c6fb4b6dfbe2 100644 --- a/gfx/ots/src/ots.cc +++ b/gfx/ots/src/ots.cc @@ -34,12 +34,6 @@ template T Round4(T value) { return (value + 3) & ~3; } -uint32_t Tag(const char *tag_str) { - uint32_t ret; - std::memcpy(&ret, tag_str, 4); - return ret; -} - bool CheckTag(uint32_t tag_value) { for (unsigned i = 0; i < 4; ++i) { const uint32_t check = tag_value & 0xff; @@ -83,6 +77,10 @@ struct Arena { std::vector hunks_; }; +// Use a macro instead of a function because gcc 4.4.3 creates static +// initializers in that case. Note this macro assumes a little-endian system. +#define TAG(a, b, c, d) (a | (b << 8) | (c << 16) | (d << 24)) + const struct { uint32_t tag; bool (*parse)(ots::OpenTypeFile *otf, const uint8_t *data, size_t length); @@ -91,80 +89,80 @@ const struct { void (*free)(ots::OpenTypeFile *file); bool required; } table_parsers[] = { - { Tag("maxp"), ots::ots_maxp_parse, ots::ots_maxp_serialise, + { TAG('m', 'a', 'x', 'p'), ots::ots_maxp_parse, ots::ots_maxp_serialise, ots::ots_maxp_should_serialise, ots::ots_maxp_free, true }, - { Tag("head"), ots::ots_head_parse, ots::ots_head_serialise, + { TAG('h', 'e', 'a', 'd'), ots::ots_head_parse, ots::ots_head_serialise, ots::ots_head_should_serialise, ots::ots_head_free, true }, - { Tag("OS/2"), ots::ots_os2_parse, ots::ots_os2_serialise, + { TAG('O', 'S', '/', '2'), ots::ots_os2_parse, ots::ots_os2_serialise, ots::ots_os2_should_serialise, ots::ots_os2_free, true }, - { Tag("cmap"), ots::ots_cmap_parse, ots::ots_cmap_serialise, + { TAG('c', 'm', 'a', 'p'), ots::ots_cmap_parse, ots::ots_cmap_serialise, ots::ots_cmap_should_serialise, ots::ots_cmap_free, true }, - { Tag("hhea"), ots::ots_hhea_parse, ots::ots_hhea_serialise, + { TAG('h', 'h', 'e', 'a'), ots::ots_hhea_parse, ots::ots_hhea_serialise, ots::ots_hhea_should_serialise, ots::ots_hhea_free, true }, - { Tag("hmtx"), ots::ots_hmtx_parse, ots::ots_hmtx_serialise, + { TAG('h', 'm', 't', 'x'), ots::ots_hmtx_parse, ots::ots_hmtx_serialise, ots::ots_hmtx_should_serialise, ots::ots_hmtx_free, true }, - { Tag("name"), ots::ots_name_parse, ots::ots_name_serialise, + { TAG('n', 'a', 'm', 'e'), ots::ots_name_parse, ots::ots_name_serialise, ots::ots_name_should_serialise, ots::ots_name_free, true }, - { Tag("post"), ots::ots_post_parse, ots::ots_post_serialise, + { TAG('p', 'o', 's', 't'), ots::ots_post_parse, ots::ots_post_serialise, ots::ots_post_should_serialise, ots::ots_post_free, true }, - { Tag("loca"), ots::ots_loca_parse, ots::ots_loca_serialise, + { TAG('l', 'o', 'c', 'a'), ots::ots_loca_parse, ots::ots_loca_serialise, ots::ots_loca_should_serialise, ots::ots_loca_free, false }, - { Tag("glyf"), ots::ots_glyf_parse, ots::ots_glyf_serialise, + { TAG('g', 'l', 'y', 'f'), ots::ots_glyf_parse, ots::ots_glyf_serialise, ots::ots_glyf_should_serialise, ots::ots_glyf_free, false }, - { Tag("CFF "), ots::ots_cff_parse, ots::ots_cff_serialise, + { TAG('C', 'F', 'F', ' '), ots::ots_cff_parse, ots::ots_cff_serialise, ots::ots_cff_should_serialise, ots::ots_cff_free, false }, - { Tag("VDMX"), ots::ots_vdmx_parse, ots::ots_vdmx_serialise, + { TAG('V', 'D', 'M', 'X'), ots::ots_vdmx_parse, ots::ots_vdmx_serialise, ots::ots_vdmx_should_serialise, ots::ots_vdmx_free, false }, - { Tag("hdmx"), ots::ots_hdmx_parse, ots::ots_hdmx_serialise, + { TAG('h', 'd', 'm', 'x'), ots::ots_hdmx_parse, ots::ots_hdmx_serialise, ots::ots_hdmx_should_serialise, ots::ots_hdmx_free, false }, - { Tag("gasp"), ots::ots_gasp_parse, ots::ots_gasp_serialise, + { TAG('g', 'a', 's', 'p'), ots::ots_gasp_parse, ots::ots_gasp_serialise, ots::ots_gasp_should_serialise, ots::ots_gasp_free, false }, - { Tag("cvt "), ots::ots_cvt_parse, ots::ots_cvt_serialise, + { TAG('c', 'v', 't', ' '), ots::ots_cvt_parse, ots::ots_cvt_serialise, ots::ots_cvt_should_serialise, ots::ots_cvt_free, false }, - { Tag("fpgm"), ots::ots_fpgm_parse, ots::ots_fpgm_serialise, + { TAG('f', 'p', 'g', 'm'), ots::ots_fpgm_parse, ots::ots_fpgm_serialise, ots::ots_fpgm_should_serialise, ots::ots_fpgm_free, false }, - { Tag("prep"), ots::ots_prep_parse, ots::ots_prep_serialise, + { TAG('p', 'r', 'e', 'p'), ots::ots_prep_parse, ots::ots_prep_serialise, ots::ots_prep_should_serialise, ots::ots_prep_free, false }, - { Tag("LTSH"), ots::ots_ltsh_parse, ots::ots_ltsh_serialise, + { TAG('L', 'T', 'S', 'H'), ots::ots_ltsh_parse, ots::ots_ltsh_serialise, ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false }, - { Tag("VORG"), ots::ots_vorg_parse, ots::ots_vorg_serialise, + { TAG('V', 'O', 'R', 'G'), ots::ots_vorg_parse, ots::ots_vorg_serialise, ots::ots_vorg_should_serialise, ots::ots_vorg_free, false }, - { Tag("kern"), ots::ots_kern_parse, ots::ots_kern_serialise, + { TAG('k', 'e', 'r', 'n'), ots::ots_kern_parse, ots::ots_kern_serialise, ots::ots_kern_should_serialise, ots::ots_kern_free, false }, // We need to parse GDEF table in advance of parsing GSUB/GPOS tables // because they could refer GDEF table. - { Tag("GDEF"), ots::ots_gdef_parse, ots::ots_gdef_serialise, + { TAG('G', 'D', 'E', 'F'), ots::ots_gdef_parse, ots::ots_gdef_serialise, ots::ots_gdef_should_serialise, ots::ots_gdef_free, false }, - { Tag("GPOS"), ots::ots_gpos_parse, ots::ots_gpos_serialise, + { TAG('G', 'P', 'O', 'S'), ots::ots_gpos_parse, ots::ots_gpos_serialise, ots::ots_gpos_should_serialise, ots::ots_gpos_free, false }, - { Tag("GSUB"), ots::ots_gsub_parse, ots::ots_gsub_serialise, + { TAG('G', 'S', 'U', 'B'), ots::ots_gsub_parse, ots::ots_gsub_serialise, ots::ots_gsub_should_serialise, ots::ots_gsub_free, false }, - { Tag("vhea"), ots::ots_vhea_parse, ots::ots_vhea_serialise, + { TAG('v', 'h', 'e', 'a'), ots::ots_vhea_parse, ots::ots_vhea_serialise, ots::ots_vhea_should_serialise, ots::ots_vhea_free, false }, - { Tag("vmtx"), ots::ots_vmtx_parse, ots::ots_vmtx_serialise, + { TAG('v', 'm', 't', 'x'), ots::ots_vmtx_parse, ots::ots_vmtx_serialise, ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false }, // SILGraphite layout tables - not actually parsed, just copied - { Tag("Silf"), ots::ots_silf_parse, ots::ots_silf_serialise, + { TAG('S', 'i', 'l', 'f'), ots::ots_silf_parse, ots::ots_silf_serialise, ots::ots_silf_should_serialise, ots::ots_silf_free, false }, - { Tag("Sill"), ots::ots_sill_parse, ots::ots_sill_serialise, + { TAG('S', 'i', 'l', 'l'), ots::ots_sill_parse, ots::ots_sill_serialise, ots::ots_sill_should_serialise, ots::ots_sill_free, false }, - { Tag("Gloc"), ots::ots_gloc_parse, ots::ots_gloc_serialise, + { TAG('G', 'l', 'o', 'c'), ots::ots_gloc_parse, ots::ots_gloc_serialise, ots::ots_gloc_should_serialise, ots::ots_gloc_free, false }, - { Tag("Glat"), ots::ots_glat_parse, ots::ots_glat_serialise, + { TAG('G', 'l', 'a', 't'), ots::ots_glat_parse, ots::ots_glat_serialise, ots::ots_glat_should_serialise, ots::ots_glat_free, false }, - { Tag("Feat"), ots::ots_feat_parse, ots::ots_feat_serialise, + { TAG('F', 'e', 'a', 't'), ots::ots_feat_parse, ots::ots_feat_serialise, ots::ots_feat_should_serialise, ots::ots_feat_free, false }, // TODO(bashi): Support mort, base, and jstf tables. { 0, NULL, NULL, NULL, NULL, false }, }; bool IsValidVersionTag(uint32_t tag) { - return tag == Tag("\x00\x01\x00\x00") || + return tag == TAG('\x00', '\x01', '\x00', '\x00') || // OpenType fonts with CFF data have 'OTTO' tag. - tag == Tag("OTTO") || + tag == TAG('O', 'T', 'T', 'O') || // Older Mac fonts might have 'true' or 'typ1' tag. - tag == Tag("true") || - tag == Tag("typ1"); + tag == TAG('t', 'r', 'u', 'e') || + tag == TAG('t', 'y', 'p', '1'); } bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output, @@ -262,7 +260,7 @@ bool ProcessWOFF(ots::OpenTypeFile *header, return OTS_FAILURE(); } - if (woff_tag != Tag("wOFF")) { + if (woff_tag != TAG('w', 'O', 'F', 'F')) { return OTS_FAILURE(); } @@ -458,7 +456,7 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output, if (header->cff) { // font with PostScript glyph - if (header->version != Tag("OTTO")) { + if (header->version != TAG('O', 'T', 'T', 'O')) { return OTS_FAILURE(); } if (header->glyf || header->loca) { @@ -522,7 +520,7 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output, out.offset = output->Tell(); output->ResetChecksum(); - if (table_parsers[i].tag == Tag("head")) { + if (table_parsers[i].tag == TAG('h', 'e', 'a', 'd')) { head_table_offset = out.offset; } if (!table_parsers[i].serialise(output, header)) { From 62e57cac37fa5a2c077109174d240078113cb57b Mon Sep 17 00:00:00 2001 From: Thomas Prip Vestergaard Date: Thu, 22 Dec 2011 11:47:59 +0100 Subject: [PATCH 03/26] Bug 708277 - Replace PR_MAX with NS_MAX. r=roc --- content/media/nsBuiltinDecoderStateMachine.cpp | 2 +- netwerk/cache/nsMemoryCacheDevice.cpp | 2 +- xpcom/glue/pldhash.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/content/media/nsBuiltinDecoderStateMachine.cpp b/content/media/nsBuiltinDecoderStateMachine.cpp index 74180b445d22..a16ae62530cb 100644 --- a/content/media/nsBuiltinDecoderStateMachine.cpp +++ b/content/media/nsBuiltinDecoderStateMachine.cpp @@ -2115,7 +2115,7 @@ nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine(PRInt64 aUsecs) { if (mState == DECODER_STATE_SHUTDOWN) { return NS_ERROR_FAILURE; } - aUsecs = PR_MAX(aUsecs, 0); + aUsecs = NS_MAX(aUsecs, 0); TimeStamp timeout = TimeStamp::Now() + UsecsToDuration(aUsecs); if (!mTimeout.IsNull()) { diff --git a/netwerk/cache/nsMemoryCacheDevice.cpp b/netwerk/cache/nsMemoryCacheDevice.cpp index a53c731b733f..deabb5f506fa 100644 --- a/netwerk/cache/nsMemoryCacheDevice.cpp +++ b/netwerk/cache/nsMemoryCacheDevice.cpp @@ -410,7 +410,7 @@ nsMemoryCacheDevice::EvictEntriesIfNecessary(void) if (entry != &mEvictionList[i]) { entryCost = (PRUint64) (now - entry->LastFetched()) * entry->Size() / - PR_MAX(1, entry->FetchCount()); + NS_MAX(1, entry->FetchCount()); if (!maxEntry || (entryCost > maxCost)) { maxEntry = entry; maxCost = entryCost; diff --git a/xpcom/glue/pldhash.cpp b/xpcom/glue/pldhash.cpp index 874332f39ff7..7e948cb4c619 100644 --- a/xpcom/glue/pldhash.cpp +++ b/xpcom/glue/pldhash.cpp @@ -310,7 +310,7 @@ PL_DHashTableSetAlphaBounds(PLDHashTable *table, "PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) >= 1"); if (PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) < 1) { maxAlpha = (float) - (PL_DHASH_MIN_SIZE - PR_MAX(PL_DHASH_MIN_SIZE / 256, 1)) + (PL_DHASH_MIN_SIZE - NS_MAX(PL_DHASH_MIN_SIZE / 256, 1)) / PL_DHASH_MIN_SIZE; } @@ -323,7 +323,7 @@ PL_DHashTableSetAlphaBounds(PLDHashTable *table, "minAlpha < maxAlpha / 2"); if (minAlpha >= maxAlpha / 2) { size = PL_DHASH_TABLE_SIZE(table); - minAlpha = (size * maxAlpha - PR_MAX(size / 256, 1)) / (2 * size); + minAlpha = (size * maxAlpha - NS_MAX(size / 256, 1)) / (2 * size); } table->maxAlphaFrac = (PRUint8)(maxAlpha * 256); From ab86c791371a5ea2b3a0cd566e704f864f7c5698 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Thu, 22 Dec 2011 11:50:56 +0100 Subject: [PATCH 04/26] Bug 712847 - Change declaration to struct to fix MSVC warning. r=bz --- layout/style/nsCSSRuleProcessor.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/layout/style/nsCSSRuleProcessor.h b/layout/style/nsCSSRuleProcessor.h index f1b1111135b3..859adcc0a3ab 100644 --- a/layout/style/nsCSSRuleProcessor.h +++ b/layout/style/nsCSSRuleProcessor.h @@ -53,12 +53,12 @@ #include "nsRuleWalker.h" #include "nsEventStates.h" -struct RuleCascadeData; -struct nsCSSSelectorList; struct CascadeEnumData; +struct nsCSSSelector; +struct nsCSSSelectorList; +struct RuleCascadeData; struct TreeMatchContext; class nsCSSKeyframesRule; -class nsCSSSelector; /** * The CSS style rule processor provides a mechanism for sibling style From 14d7ce0332cb5071a0a24892efb3e6a0528c56a7 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Thu, 22 Dec 2011 11:07:12 +0000 Subject: [PATCH 05/26] Backout b02402b95e5c (bug 708277) for build failures on all platforms --- content/media/nsBuiltinDecoderStateMachine.cpp | 2 +- netwerk/cache/nsMemoryCacheDevice.cpp | 2 +- xpcom/glue/pldhash.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/content/media/nsBuiltinDecoderStateMachine.cpp b/content/media/nsBuiltinDecoderStateMachine.cpp index a16ae62530cb..74180b445d22 100644 --- a/content/media/nsBuiltinDecoderStateMachine.cpp +++ b/content/media/nsBuiltinDecoderStateMachine.cpp @@ -2115,7 +2115,7 @@ nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine(PRInt64 aUsecs) { if (mState == DECODER_STATE_SHUTDOWN) { return NS_ERROR_FAILURE; } - aUsecs = NS_MAX(aUsecs, 0); + aUsecs = PR_MAX(aUsecs, 0); TimeStamp timeout = TimeStamp::Now() + UsecsToDuration(aUsecs); if (!mTimeout.IsNull()) { diff --git a/netwerk/cache/nsMemoryCacheDevice.cpp b/netwerk/cache/nsMemoryCacheDevice.cpp index deabb5f506fa..a53c731b733f 100644 --- a/netwerk/cache/nsMemoryCacheDevice.cpp +++ b/netwerk/cache/nsMemoryCacheDevice.cpp @@ -410,7 +410,7 @@ nsMemoryCacheDevice::EvictEntriesIfNecessary(void) if (entry != &mEvictionList[i]) { entryCost = (PRUint64) (now - entry->LastFetched()) * entry->Size() / - NS_MAX(1, entry->FetchCount()); + PR_MAX(1, entry->FetchCount()); if (!maxEntry || (entryCost > maxCost)) { maxEntry = entry; maxCost = entryCost; diff --git a/xpcom/glue/pldhash.cpp b/xpcom/glue/pldhash.cpp index 7e948cb4c619..874332f39ff7 100644 --- a/xpcom/glue/pldhash.cpp +++ b/xpcom/glue/pldhash.cpp @@ -310,7 +310,7 @@ PL_DHashTableSetAlphaBounds(PLDHashTable *table, "PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) >= 1"); if (PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) < 1) { maxAlpha = (float) - (PL_DHASH_MIN_SIZE - NS_MAX(PL_DHASH_MIN_SIZE / 256, 1)) + (PL_DHASH_MIN_SIZE - PR_MAX(PL_DHASH_MIN_SIZE / 256, 1)) / PL_DHASH_MIN_SIZE; } @@ -323,7 +323,7 @@ PL_DHashTableSetAlphaBounds(PLDHashTable *table, "minAlpha < maxAlpha / 2"); if (minAlpha >= maxAlpha / 2) { size = PL_DHASH_TABLE_SIZE(table); - minAlpha = (size * maxAlpha - NS_MAX(size / 256, 1)) / (2 * size); + minAlpha = (size * maxAlpha - PR_MAX(size / 256, 1)) / (2 * size); } table->maxAlphaFrac = (PRUint8)(maxAlpha * 256); From 7813f5b5dd80a719d08ff3a4b7f1317bf82e8037 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Thu, 22 Dec 2011 11:13:29 +0000 Subject: [PATCH 06/26] bug 691505 followup - use the proper #define so that our malloc replacements actually take effect. r=roc --- gfx/graphite2/src/Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/graphite2/src/Makefile.in b/gfx/graphite2/src/Makefile.in index eeee12795c70..c4bedd587ac3 100644 --- a/gfx/graphite2/src/Makefile.in +++ b/gfx/graphite2/src/Makefile.in @@ -78,7 +78,7 @@ DEFINES += -DDISABLE_FILE_FACE -DDISABLE_TRACING -DDISABLE_SEGCACHE # provide a custom header that overrides malloc() and friends, # to ensure safe OOM handling -DEFINES += -DGR_CUSTOM_HEADER="\"MozGrMalloc.h\"" +DEFINES += -DGR2_CUSTOM_HEADER="\"MozGrMalloc.h\"" # Filter out -pedantic so that direct_machine.cpp can use computed goto CXXFLAGS := $(filter-out -pedantic,$(CXXFLAGS)) From 53830bd6b873b0628562b41ab3c4543a5bd25eb2 Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Thu, 22 Dec 2011 11:35:17 +0000 Subject: [PATCH 07/26] Bug 709152 - Group multiple texture uploads into a single upload. r=pcwalton Instead of tracking multiple rectangles when we receive endDrawing calls, just union the dirty area into a single rectangle. --- mobile/android/base/gfx/TileLayer.java | 30 +++++++++++++++----------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/mobile/android/base/gfx/TileLayer.java b/mobile/android/base/gfx/TileLayer.java index 7905f49a3927..887f18ea5a99 100644 --- a/mobile/android/base/gfx/TileLayer.java +++ b/mobile/android/base/gfx/TileLayer.java @@ -47,7 +47,6 @@ import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; -import java.util.ArrayList; /** * Base class for tile layers, which encapsulate the logic needed to draw textured tiles in OpenGL @@ -56,7 +55,7 @@ import java.util.ArrayList; public abstract class TileLayer extends Layer { private static final String LOGTAG = "GeckoTileLayer"; - private final ArrayList mDirtyRects; + private final Rect mDirtyRect; private final CairoImage mImage; private final boolean mRepeat; private IntSize mSize; @@ -66,7 +65,9 @@ public abstract class TileLayer extends Layer { mRepeat = repeat; mImage = image; mSize = new IntSize(0, 0); - mDirtyRects = new ArrayList(); + + IntSize bufferSize = mImage.getSize(); + mDirtyRect = new Rect(); } @Override @@ -89,7 +90,7 @@ public abstract class TileLayer extends Layer { public void invalidate(Rect rect) { if (!inTransaction()) throw new RuntimeException("invalidate() is only valid inside a transaction"); - mDirtyRects.add(rect); + mDirtyRect.union(rect); } public void invalidate() { @@ -136,22 +137,24 @@ public abstract class TileLayer extends Layer { if (!mImage.getSize().isPositive()) return; - if (mTextureIDs == null) { + // If we haven't allocated a texture, assume the whole region is dirty + if (mTextureIDs == null) uploadFullTexture(gl); - } else { - for (Rect dirtyRect : mDirtyRects) - uploadDirtyRect(gl, dirtyRect); - } + else + uploadDirtyRect(gl, mDirtyRect); - mDirtyRects.clear(); + mDirtyRect.setEmpty(); } private void uploadFullTexture(GL10 gl) { IntSize bufferSize = mImage.getSize(); uploadDirtyRect(gl, new Rect(0, 0, bufferSize.width, bufferSize.height)); } - + private void uploadDirtyRect(GL10 gl, Rect dirtyRect) { + if (dirtyRect.isEmpty()) + return; + boolean newlyCreated = false; if (mTextureIDs == null) { @@ -168,7 +171,7 @@ public abstract class TileLayer extends Layer { bindAndSetGLParameters(gl); - if (newlyCreated || dirtyRect.equals(bufferRect)) { + if (newlyCreated || dirtyRect.contains(bufferRect)) { if (mSize.equals(bufferSize)) { gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, mSize.height, 0, glInfo.format, glInfo.type, mImage.getBuffer()); @@ -198,7 +201,8 @@ public abstract class TileLayer extends Layer { } viewBuffer.position(position); - gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, dirtyRect.top, bufferSize.width, dirtyRect.height(), + gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, dirtyRect.top, bufferSize.width, + Math.min(bufferSize.height - dirtyRect.top, dirtyRect.height()), glInfo.format, glInfo.type, viewBuffer); } From 36d311dfe0314b3dbada008f827f171a4bb6c145 Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Thu, 22 Dec 2011 11:35:32 +0000 Subject: [PATCH 08/26] Bug 709152 - Squash multiple viewport events in nsAppShell. r=pcwalton We only ever want to respond to the latest viewport sent, separate the viewport change into its own event and remove all but the latest in the queue when processing events. --- mobile/android/base/GeckoEvent.java | 8 ++++++++ .../base/gfx/GeckoSoftwareLayerClient.java | 3 +-- widget/src/android/AndroidJavaWrappers.cpp | 1 + widget/src/android/AndroidJavaWrappers.h | 1 + widget/src/android/nsAppShell.cpp | 17 ++++++++++++++++- widget/src/android/nsAppShell.h | 1 + 6 files changed, 28 insertions(+), 3 deletions(-) diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index 0338527d6cce..a13e0bf81c46 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -37,6 +37,7 @@ package org.mozilla.gecko; +import org.mozilla.gecko.gfx.ViewportMetrics; import android.os.*; import android.app.*; import android.view.*; @@ -76,6 +77,7 @@ public class GeckoEvent { public static final int GECKO_EVENT_SYNC = 15; public static final int ACTIVITY_START = 17; public static final int BROADCAST = 19; + public static final int VIEWPORT = 20; public static final int IME_COMPOSITION_END = 0; public static final int IME_COMPOSITION_BEGIN = 1; @@ -232,6 +234,12 @@ public class GeckoEvent { mCharactersExtra = data; } + public GeckoEvent(ViewportMetrics viewport) { + mType = VIEWPORT; + mCharacters = "Viewport:Change"; + mCharactersExtra = viewport.toJSON(); + } + public GeckoEvent(String uri) { mType = LOAD_URI; mCharacters = uri; diff --git a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java index 9a51e996f511..e14184376406 100644 --- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java +++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java @@ -315,8 +315,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL viewportMetrics.setViewportOffset(viewportOffset); viewportMetrics.setViewport(viewportMetrics.getClampedViewport()); - GeckoEvent event = new GeckoEvent("Viewport:Change", viewportMetrics.toJSON()); - GeckoAppShell.sendEventToGecko(event); + GeckoAppShell.sendEventToGecko(new GeckoEvent(viewportMetrics)); if (mViewportSizeChanged) { mViewportSizeChanged = false; GeckoAppShell.viewSizeChanged(); diff --git a/widget/src/android/AndroidJavaWrappers.cpp b/widget/src/android/AndroidJavaWrappers.cpp index 393536dfad8b..f773cb1218a7 100644 --- a/widget/src/android/AndroidJavaWrappers.cpp +++ b/widget/src/android/AndroidJavaWrappers.cpp @@ -480,6 +480,7 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj) break; } + case VIEWPORT: case BROADCAST: { ReadCharactersField(jenv); ReadCharactersExtraField(jenv); diff --git a/widget/src/android/AndroidJavaWrappers.h b/widget/src/android/AndroidJavaWrappers.h index b77e4b8a5e3c..3e397a0be5bf 100644 --- a/widget/src/android/AndroidJavaWrappers.h +++ b/widget/src/android/AndroidJavaWrappers.h @@ -531,6 +531,7 @@ public: FORCED_RESIZE = 16, ACTIVITY_START = 17, BROADCAST = 19, + VIEWPORT = 20, dummy_java_enum_list_end }; diff --git a/widget/src/android/nsAppShell.cpp b/widget/src/android/nsAppShell.cpp index 506ca3822adf..9918b1837c67 100644 --- a/widget/src/android/nsAppShell.cpp +++ b/widget/src/android/nsAppShell.cpp @@ -84,7 +84,8 @@ nsAppShell::nsAppShell() : mQueueLock("nsAppShell.mQueueLock"), mCondLock("nsAppShell.mCondLock"), mQueueCond(mCondLock, "nsAppShell.mQueueCond"), - mNumDraws(0) + mNumDraws(0), + mNumViewports(0) { gAppShell = this; } @@ -242,6 +243,15 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) int curType = curEvent->Type(); int nextType = nextEvent->Type(); + while (nextType == AndroidGeckoEvent::VIEWPORT && mNumViewports > 1) { + // Skip this viewport change, as there's another one later and + // processing this one will only cause more unnecessary work + PopNextEvent(); + delete nextEvent; + nextEvent = PeekNextEvent(); + nextType = nextEvent->Type(); + } + while (nextType == AndroidGeckoEvent::DRAW && mLastDrawEvent && mNumDraws > 1) { @@ -385,6 +395,7 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) break; } + case AndroidGeckoEvent::VIEWPORT: case AndroidGeckoEvent::BROADCAST: { if (curEvent->Characters().Length() == 0) @@ -461,6 +472,8 @@ nsAppShell::PopNextEvent() if (ae->Type() == AndroidGeckoEvent::DRAW) { if (--mNumDraws == 0) mLastDrawEvent = nsnull; + } else if (ae->Type() == AndroidGeckoEvent::VIEWPORT) { + mNumViewports--; } } @@ -503,6 +516,8 @@ nsAppShell::PostEvent(AndroidGeckoEvent *ae) if (ae->Type() == AndroidGeckoEvent::DRAW) { mNumDraws++; mLastDrawEvent = ae; + } else if (ae->Type() == AndroidGeckoEvent::VIEWPORT) { + mNumViewports++; } } NotifyNativeEvent(); diff --git a/widget/src/android/nsAppShell.h b/widget/src/android/nsAppShell.h index 431e10a467e0..80dfa530a98b 100644 --- a/widget/src/android/nsAppShell.h +++ b/widget/src/android/nsAppShell.h @@ -92,6 +92,7 @@ protected: Mutex mCondLock; CondVar mQueueCond; int mNumDraws; + int mNumViewports; mozilla::AndroidGeckoEvent *mLastDrawEvent; nsTArray mEventQueue; nsInterfaceHashtable mObserversHash; From 157c7f8bcbe8633a554f69e22e9fc8498cc2293d Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Thu, 22 Dec 2011 11:35:41 +0000 Subject: [PATCH 09/26] Bug 709152 - Adjust viewport offset while panning. r=pcwalton This patch adjusts the viewport offset while panning to increase the buffered area in the direction of panning, and thereby reduce checkerboarding. --- .../base/gfx/GeckoSoftwareLayerClient.java | 1 - mobile/android/base/gfx/ViewportMetrics.java | 63 ++++++++++++++----- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java index e14184376406..17c690c1f942 100644 --- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java +++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java @@ -307,7 +307,6 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL } private void adjustViewport() { - Log.i(LOGTAG, "Adjusting viewport"); ViewportMetrics viewportMetrics = new ViewportMetrics(getLayerController().getViewportMetrics()); diff --git a/mobile/android/base/gfx/ViewportMetrics.java b/mobile/android/base/gfx/ViewportMetrics.java index 5fc6408d764a..b49bd5856178 100644 --- a/mobile/android/base/gfx/ViewportMetrics.java +++ b/mobile/android/base/gfx/ViewportMetrics.java @@ -64,11 +64,17 @@ public class ViewportMetrics { private PointF mViewportOffset; private float mZoomFactor; + // A scale from -1,-1 to 1,1 that represents what edge of the displayport + // we want the viewport to be biased towards. + private PointF mViewportBias; + private static final float MAX_BIAS = 0.8f; + public ViewportMetrics() { mPageSize = new FloatSize(1, 1); mViewportRect = new RectF(0, 0, 1, 1); mViewportOffset = new PointF(0, 0); mZoomFactor = 1.0f; + mViewportBias = new PointF(0.0f, 0.0f); } public ViewportMetrics(ViewportMetrics viewport) { @@ -77,6 +83,7 @@ public class ViewportMetrics { PointF offset = viewport.getViewportOffset(); mViewportOffset = new PointF(offset.x, offset.y); mZoomFactor = viewport.getZoomFactor(); + mViewportBias = viewport.mViewportBias; } public ViewportMetrics(JSONObject json) throws JSONException { @@ -94,36 +101,36 @@ public class ViewportMetrics { mViewportRect = new RectF(x, y, x + width, y + height); mViewportOffset = new PointF(offsetX, offsetY); mZoomFactor = zoom; + mViewportBias = new PointF(0.0f, 0.0f); } public PointF getOptimumViewportOffset(IntSize displayportSize) { - // XXX We currently always position the viewport in the centre of the - // displayport, but we might want to optimise this during panning - // to minimise checkerboarding. - Point optimumOffset = - new Point((int)Math.round((displayportSize.width - mViewportRect.width()) / 2), - (int)Math.round((displayportSize.height - mViewportRect.height()) / 2)); - /* XXX Until bug #524925 is fixed, changing the viewport origin will - * probably cause things to be slower than just having a smaller usable - * displayport. + * cause unnecessary relayouts. This may cause rendering time to + * increase and should be considered. */ - Rect viewport = RectUtils.round(getClampedViewport()); + RectF viewport = getClampedViewport(); + + FloatSize bufferSpace = new FloatSize(displayportSize.width - viewport.width(), + displayportSize.height - viewport.height()); + PointF optimumOffset = + new PointF(bufferSpace.width * ((mViewportBias.x + 1.0f) / 2.0f), + bufferSpace.height * ((mViewportBias.y + 1.0f) / 2.0f)); // Make sure this offset won't cause wasted pixels in the displayport // (i.e. make sure the resultant displayport intersects with the page // as much as possible) if (viewport.left - optimumOffset.x < 0) optimumOffset.x = viewport.left; - else if (optimumOffset.x + viewport.right > mPageSize.width) - optimumOffset.x -= (mPageSize.width - (optimumOffset.x + viewport.right)); + else if ((bufferSpace.width - optimumOffset.x) + viewport.right > mPageSize.width) + optimumOffset.x = bufferSpace.width - (mPageSize.width - viewport.right); if (viewport.top - optimumOffset.y < 0) optimumOffset.y = viewport.top; - else if (optimumOffset.y + viewport.bottom > mPageSize.height) - optimumOffset.y -= (mPageSize.height - (optimumOffset.y + viewport.bottom)); + else if ((bufferSpace.height - optimumOffset.y) + viewport.bottom > mPageSize.height) + optimumOffset.y = bufferSpace.height - (mPageSize.height - viewport.bottom); - return new PointF(optimumOffset); + return new PointF(Math.round(optimumOffset.x), Math.round(optimumOffset.y)); } public PointF getOrigin() { @@ -184,6 +191,23 @@ public class ViewportMetrics { } public void setOrigin(PointF origin) { + // When the origin is set, we compare it with the last value set and + // change the viewport bias accordingly, so that any viewport based + // on these metrics will have a larger buffer in the direction of + // movement. + + // XXX Note the comment about bug #524925 in getOptimumViewportOffset. + // Ideally, the viewport bias would be a sliding scale, but we + // don't want to change it too often at the moment. + if (FloatUtils.fuzzyEquals(origin.x, mViewportRect.left)) + mViewportBias.x = 0; + else + mViewportBias.x = ((mViewportRect.left - origin.x) > 0) ? MAX_BIAS : -MAX_BIAS; + if (FloatUtils.fuzzyEquals(origin.y, mViewportRect.top)) + mViewportBias.y = 0; + else + mViewportBias.y = ((mViewportRect.top - origin.y) > 0) ? MAX_BIAS : -MAX_BIAS; + mViewportRect.set(origin.x, origin.y, origin.x + mViewportRect.width(), origin.y + mViewportRect.height()); @@ -218,6 +242,15 @@ public class ViewportMetrics { setOrigin(origin); mZoomFactor = newZoomFactor; + + // Similar to setOrigin, set the viewport bias based on the focal point + // of the zoom so that a viewport based on these metrics will have a + // larger buffer based on the direction of movement when scaling. + // + // This is biased towards scaling outwards, as zooming in doesn't + // really require a viewport bias. + mViewportBias.set(((focus.x / mViewportRect.width()) * (2.0f * MAX_BIAS)) - MAX_BIAS, + ((focus.y / mViewportRect.height()) * (2.0f * MAX_BIAS)) - MAX_BIAS); } /* From 0eb9ac48128d33f1b8a2775da1a307ea5b49a257 Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Thu, 22 Dec 2011 09:09:41 -0500 Subject: [PATCH 10/26] Bug 701076 - core robocop toolchain (1.03) a=tfair; r=gbrown,blassey --- build/mobile/robocop/Actions.java.in | 60 +++ build/mobile/robocop/AndroidManifest.xml.in | 19 + build/mobile/robocop/Driver.java.in | 78 ++++ build/mobile/robocop/Element.java.in | 49 ++ .../robocop/FennecNativeActions.java.in | 197 ++++++++ .../mobile/robocop/FennecNativeDriver.java.in | 425 ++++++++++++++++++ .../robocop/FennecNativeElement.java.in | 149 ++++++ build/mobile/robocop/Makefile.in | 136 ++++++ build/mobile/robocop/RoboCopException.java.in | 59 +++ build/mobile/robocop/parse_ids.py | 60 +++ build/mobile/robocop/res/values/strings.xml | 5 + 11 files changed, 1237 insertions(+) create mode 100644 build/mobile/robocop/Actions.java.in create mode 100644 build/mobile/robocop/AndroidManifest.xml.in create mode 100644 build/mobile/robocop/Driver.java.in create mode 100644 build/mobile/robocop/Element.java.in create mode 100644 build/mobile/robocop/FennecNativeActions.java.in create mode 100644 build/mobile/robocop/FennecNativeDriver.java.in create mode 100644 build/mobile/robocop/FennecNativeElement.java.in create mode 100644 build/mobile/robocop/Makefile.in create mode 100644 build/mobile/robocop/RoboCopException.java.in create mode 100644 build/mobile/robocop/parse_ids.py create mode 100644 build/mobile/robocop/res/values/strings.xml diff --git a/build/mobile/robocop/Actions.java.in b/build/mobile/robocop/Actions.java.in new file mode 100644 index 000000000000..66e81111dda0 --- /dev/null +++ b/build/mobile/robocop/Actions.java.in @@ -0,0 +1,60 @@ +#filter substitution +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Firefox Mobile Test Framework. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Trevor Fairey + * David Burns + * Joel Maher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package @ANDROID_PACKAGE_NAME@; +import java.util.List; + +public interface Actions { + public enum SpecialKey { + DOWN, UP, LEFT, RIGHT, ENTER + } + /** + * Waits for a gecko event to be sent from the Gecko instance. + * + * @param geckoEvent The geckoEvent JSONObject's type + */ + + void waitForGeckoEvent(String geckoEvent); + // Send the string kewsToSend to the application + void sendKeys(String keysToSend); + //Send any of the above keys to the element + void sendSpecialKey(SpecialKey button); + + void drag(int startingX, int endingX, int startingY, int endingY); +} diff --git a/build/mobile/robocop/AndroidManifest.xml.in b/build/mobile/robocop/AndroidManifest.xml.in new file mode 100644 index 000000000000..f3bad0132179 --- /dev/null +++ b/build/mobile/robocop/AndroidManifest.xml.in @@ -0,0 +1,19 @@ +#filter substitution + + + + + + + + + + + + diff --git a/build/mobile/robocop/Driver.java.in b/build/mobile/robocop/Driver.java.in new file mode 100644 index 000000000000..b428f2f93a36 --- /dev/null +++ b/build/mobile/robocop/Driver.java.in @@ -0,0 +1,78 @@ +#filter substitution +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Firefox Mobile Test Framework. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Trevor Fairey + * David Burns + * Joel Maher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package @ANDROID_PACKAGE_NAME@; +import java.util.List; + +public interface Driver { + /** + * Find the first Element using the given method. + * + * @param name The name of the element + * @return The first matching element on the current context + * @throws RoboCopException If no matching elements are found + */ + Element findElement(String name); + + /** + * Sets up scroll handling so that data is received from the extension. + */ + void setupScrollHandling(); + + int getPageHeight(); + int getScrollHeight(); + int getHeight(); + int getGeckoTop(); + int getGeckoLeft(); + int getGeckoWidth(); + int getGeckoHeight(); + + void startFrameRecording(); + int stopFrameRecording(); + void dumpLog(String message); + void setLogFile(String filename); + + void ok(boolean condition, String name, String diag); + void is(Object a, Object b, String name); + void isnot(Object a, Object b, String name); + void todo(boolean condition, String name, String diag); + void todo_is(Object a, Object b, String name); + void todo_isnot(Object a, Object b, String name); + void info(String name, String message); +} diff --git a/build/mobile/robocop/Element.java.in b/build/mobile/robocop/Element.java.in new file mode 100644 index 000000000000..3db4197648d3 --- /dev/null +++ b/build/mobile/robocop/Element.java.in @@ -0,0 +1,49 @@ +#filter substitution +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Firefox Mobile Test Framework. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Trevor Fairey + * David Burns + * Joel Maher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package @ANDROID_PACKAGE_NAME@; + +public interface Element { + //Click on the element + void click(); + //Returns true if the element is currently displayed + boolean isDisplayed(); + //Returns the text currently displayed on the element. + String getText(); +} diff --git a/build/mobile/robocop/FennecNativeActions.java.in b/build/mobile/robocop/FennecNativeActions.java.in new file mode 100644 index 000000000000..1219450ee08f --- /dev/null +++ b/build/mobile/robocop/FennecNativeActions.java.in @@ -0,0 +1,197 @@ +#filter substitution +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Firefox Mobile Test Framework. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Trevor Fairey + * David Burns + * Joel Maher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package @ANDROID_PACKAGE_NAME@; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.HashMap; +import java.util.List; + +import java.lang.Class; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; +import java.lang.Long; + +import android.app.Activity; +import android.app.Instrumentation; +import android.util.Log; +import android.view.View; +import android.view.KeyEvent; + +import java.util.concurrent.SynchronousQueue; + +import org.json.*; + +import com.jayway.android.robotium.solo.Solo; + +public class FennecNativeActions implements Actions { + // Map of IDs to element names. + private Solo solo; + private Instrumentation instr; + + // Objects for reflexive access of fennec classes. + private ClassLoader classLoader; + private Class gel; + private Class ge; + private Class gas; + private Method registerGEL; + private Method unregisterGEL; + private Method sendGE; + + + // If waiting for an event. + private SynchronousQueue waitqueue = new SynchronousQueue(); + + public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation){ + this.solo = robocop; + this.instr = instrumentation; + // Set up reflexive access of java classes and methods. + try { + classLoader = activity.getClassLoader(); + gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener"); + ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent"); + gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell"); + Class [] parameters = new Class[2]; + parameters[0] = String.class; + parameters[1] = gel; + registerGEL = gas.getMethod("registerGeckoEventListener", parameters); + unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters); + parameters = new Class[1]; + parameters[0] = ge; + sendGE = gas.getMethod("sendEventToGecko", parameters); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + } + + class wakeInvocationHandler implements InvocationHandler { + public wakeInvocationHandler(){}; + public Object invoke(Object proxy, Method method, Object[] args) { + String methodName = method.getName(); + //Depending on the method, return a completely different type. + if(methodName.equals("toString")) { + return "wakeInvocationHandler"; + } + if(methodName.equals("equals")) { + return this == args[0]; + } + if(methodName.equals("clone")) { + return this; + } + if(methodName.equals("hashCode")) { + return 314; + } + Log.i("Robocop", "Waking up on "+methodName); + waitqueue.offer(new Boolean(true)); + return null; + } + } + + public void waitForGeckoEvent(String geckoEvent) { + Log.i("Robocop", "waiting for "+geckoEvent); + try { + Class [] interfaces = new Class[1]; + interfaces[0] = gel; + Object[] finalParams = new Object[2]; + finalParams[0] = geckoEvent; + + wakeInvocationHandler wIH = new wakeInvocationHandler(); + Object proxy = Proxy.newProxyInstance(classLoader, interfaces, wIH); + finalParams[1] = proxy; + registerGEL.invoke(null, finalParams); + + waitqueue.take(); + unregisterGEL.invoke(null, finalParams); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Log.i("Robocop", "wait ends for: "+geckoEvent); + } + + public void sendSpecialKey(SpecialKey button) { + switch( button) { + case DOWN: + instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN); + break; + case UP: + instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_UP); + break; + case LEFT: + instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_LEFT); + break; + case RIGHT: + instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_RIGHT); + break; + case ENTER: + instr.sendCharacterSync(KeyEvent.KEYCODE_ENTER); + break; + default: + break; + } + } + + @Override + public void sendKeys(String input) { + instr.sendStringSync(input); + } + + + public void drag(int startingX, int endingX, int startingY, int endingY) { + solo.drag(startingX, endingX, startingY, endingY, 10); + } +} diff --git a/build/mobile/robocop/FennecNativeDriver.java.in b/build/mobile/robocop/FennecNativeDriver.java.in new file mode 100644 index 000000000000..fe67c1fa28fb --- /dev/null +++ b/build/mobile/robocop/FennecNativeDriver.java.in @@ -0,0 +1,425 @@ +#filter substitution +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Firefox Mobile Test Framework. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Trevor Fairey + * David Burns + * Joel Maher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package @ANDROID_PACKAGE_NAME@; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.HashMap; +import java.util.List; + +import java.lang.Class; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; +import java.lang.Long; + +import android.app.Activity; +import android.util.Log; +import android.view.View; + +import org.json.*; + +import com.jayway.android.robotium.solo.Solo; + +public class FennecNativeDriver implements Driver { + // Map of IDs to element names. + private HashMap locators = null; + private Activity activity; + private Solo solo; + private String logFile; + + // Objects for reflexive access of fennec classes. + private ClassLoader classLoader; + private Class gel; + private Class ge; + private Class gas; + private Method registerGEL; + private Method unregisterGEL; + private Method sendGE; + private Method _startFrameRecording; + private Method _stopFrameRecording; + + + private LinkedList testList = new LinkedList(); + + public FennecNativeDriver(Activity activity, Solo robocop){ + this.activity = activity; + this.solo = robocop; + + // Set up table of fennec_ids. + locators = convertTextToTable(getFile("/mnt/sdcard/fennec_ids.txt")); + + // Set up reflexive access of java classes and methods. + try { + classLoader = activity.getClassLoader(); + gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener"); + ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent"); + gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell"); + Class [] parameters = new Class[2]; + parameters[0] = String.class; + parameters[1] = gel; + registerGEL = gas.getMethod("registerGeckoEventListener", parameters); + unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters); + parameters = new Class[1]; + parameters[0] = ge; + sendGE = gas.getMethod("sendEventToGecko", parameters); + + Class gfx = classLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI"); + _startFrameRecording = gfx.getDeclaredMethod("startFrameTimeRecording"); + _stopFrameRecording = gfx.getDeclaredMethod("stopFrameTimeRecording"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + } + + //Information on the location of the Gecko Frame. + private boolean geckoInfo = false; + private int geckoTop = 100; + private int geckoLeft = 0; + private int geckoHeight= 700; + private int geckoWidth = 1024; + + private void getGeckoInfo() { + View geckoLayout = activity.findViewById(Integer.decode((String)locators.get("gecko_layout"))); + if (geckoLayout != null) { + geckoTop = geckoLayout.getTop(); + geckoLeft = geckoLayout.getLeft(); + geckoWidth = geckoLayout.getWidth(); + geckoHeight = geckoLayout.getHeight(); + geckoInfo = true; + } + } + + public int getGeckoTop() { + if(!geckoInfo) { + getGeckoInfo(); + } + return geckoTop; + } + + public int getGeckoLeft() { + if(!geckoInfo) { + getGeckoInfo(); + } + return geckoLeft; + } + + public int getGeckoHeight() { + if(!geckoInfo) { + getGeckoInfo(); + } + return geckoHeight; + } + public int getGeckoWidth() { + if(!geckoInfo) { + getGeckoInfo(); + } + return geckoWidth; + } + + @Override + public Element findElement(String name) { + if (name == null) + throw new IllegalArgumentException("Can not findElements when passed a null"); + if (locators.containsKey(name)){ + return new FennecNativeElement(Integer.decode((String)locators.get(name)), activity, solo); + } + throw new RoboCopException("Element does not exist in the list"); + } + + public void startFrameRecording() { + try { + Object [] params = null; + _startFrameRecording.invoke(null, params); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + + public int stopFrameRecording() { + Class [] parameters = new Class[1]; + parameters[0] = null; + List frames; + + try { + Object [] params = null; + frames = (List)_stopFrameRecording.invoke(null, params); + Object [] framearray = frames.toArray(); + Long last = new Long(0); + Long threshold = new Long(17); + int numDelays = 0; + for (int i=0; i < framearray.length; i++) { + Long val = (Long)framearray[i]; + if ((val - last) > threshold) { + numDelays++; + } + last = val; + } + return numDelays; + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + + return 0; + } + + class scrollHandler implements InvocationHandler { + public scrollHandler(){}; + public Object invoke(Object proxy, Method method, Object[] args) { + try{ + //Disect the JSON object into the appropriate variables + JSONObject jo = ((JSONObject)args[1]); + scrollHeight = jo.getInt("y"); + height = jo.getInt("cheight"); + //We don't want a height of 0. That means it's a bad response. + if( height > 0) { + pageHeight = jo.getInt("height"); + } + + } catch( Throwable e) { + Log.i("Robocop", "WARNING: ScrollReceived, but read wrong!"); + } + return null; + } + } + public int getScrollHeight() { + return scrollHeight; + } + public int getPageHeight() { + return pageHeight; + } + public int getHeight() { + return height; + } + + public int height=0; + public int scrollHeight=0; + public int pageHeight=10; + public void setupScrollHandling() { + //Setup scrollHandler to catch "robocop:scroll" events. + try { + Class [] interfaces = new Class[1]; + interfaces[0] = gel; + Object[] finalParams = new Object[2]; + finalParams[0] = "robocop:scroll"; + finalParams[1] = Proxy.newProxyInstance(classLoader, interfaces, new scrollHandler()); + registerGEL.invoke(null, finalParams); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + + } + + //Takes a filename, loads the file, + // and returns a string version of the entire file. + public static String getFile(String filename) + { + File file = new File(filename); + StringBuilder text = new StringBuilder(); + + try { + BufferedReader br = new BufferedReader(new FileReader(file)); + String line; + + while ((line = br.readLine()) != null) { + text.append(line); + text.append('\n'); + } + } catch(IOException e) { + e.printStackTrace(); + } + return text.toString(); + } + + // Write information to a logfile and logcat + public void dumpLog(String message) + { + File file = new File(logFile); + BufferedWriter bw = null; + + try { + bw = new BufferedWriter(new FileWriter(logFile, true)); + bw.write(message); + bw.newLine(); + } catch(IOException e) { + e.printStackTrace(); + } finally { + try { + if (bw != null) { + bw.flush(); + bw.close(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + Log.i("Robocop", message); + } + + // Set the filename used for dumpLog. + public void setLogFile(String filename) + { + logFile = filename; + } + + // Takes a string of "key=value" pairs split by \n and creates a hash table. + public static HashMap convertTextToTable(String data) + { + HashMap retVal = new HashMap(); + + String[] lines = data.split("\n"); + for (int i = 0; i < lines.length; i++) { + String[] parts = lines[i].split("="); + retVal.put(parts[0].trim(), parts[1].trim()); + } + return retVal; + } + + class testInfo { + public boolean result; + public String name; + public String diag; + public boolean todo; + public testInfo(boolean r, String n, String d, boolean t) { + result = r; + name = n; + diag = d; + todo = t; + } + + } + + + private void _logResult(testInfo test, String passString, String failString) + { + boolean isError = true; + String resultString = failString; + if(test.result || test.todo){ + isError = false; + } + if(test.result) + { + resultString = passString; + } + String diag= test.name; + if(test.diag!=null) diag+= " - " + test.diag; + + String message = resultString + " | " + "ROBOCOP" + " | " + diag; + if(isError) { + dumpLog(message); + } + else { + dumpLog(message); + } + } + + public void ok(boolean condition, String name, String diag) { + testInfo test = new testInfo(condition, name, diag, false); + _logResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL"); + testList.add(test); + } + + public void is(Object a, Object b, String name) { + boolean pass = a.equals(b); + String diag = "got " + a.toString() + ", expected " + b.toString(); + if(pass) { + diag = a.toString() + " should equal " + b.toString(); + } + ok(pass, name, diag); + } + + public void isnot(Object a, Object b, String name) { + boolean pass = !a.equals(b); + String diag = "didn't expect " + a.toString() + ", but got it"; + if(pass) { + diag = a.toString() + " should not equal " + b.toString(); + } + ok(pass, name, diag); + } + + public void todo(boolean condition, String name, String diag) { + testInfo test = new testInfo(condition, name, diag, true); + _logResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL"); + testList.add(test); + } + + public void todo_is(Object a, Object b, String name) { + boolean pass = a.equals(b); + String diag = "got " + a.toString() + ", expected " + b.toString(); + if(pass) { + diag = a.toString() + " should equal " + b.toString(); + } + todo(pass, name, diag); + } + + public void todo_isnot(Object a, Object b, String name) { + boolean pass = !a.equals(b); + String diag = "didn't expect " + a.toString() + ", but got it"; + if(pass) { + diag = a.toString() + " should not equal " + b.toString(); + } + todo(pass, name, diag); + } + + public void info(String name, String message) { + testInfo test = new testInfo(true, name, message, false); + _logResult(test, "TEST-INFO", "INFO FAILED?"); + } +} diff --git a/build/mobile/robocop/FennecNativeElement.java.in b/build/mobile/robocop/FennecNativeElement.java.in new file mode 100644 index 000000000000..76906a9f1d6d --- /dev/null +++ b/build/mobile/robocop/FennecNativeElement.java.in @@ -0,0 +1,149 @@ +#filter substitution +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Firefox Mobile Test Framework. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Trevor Fairey + * David Burns + * Joel Maher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package @ANDROID_PACKAGE_NAME@; + +import java.util.List; + +import android.app.Activity; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.TextSwitcher; +import android.app.Instrumentation; +import android.util.Log; +import com.jayway.android.robotium.solo.Solo; +import java.util.concurrent.SynchronousQueue; + +public class FennecNativeElement implements Element { + private Integer id; + private Activity currentActivity; + private Solo robocop; + + public FennecNativeElement(Integer id, Activity activity, Solo solo){ + this.id = id; + robocop = solo; + currentActivity = activity; + } + + public Integer getId() { + return id; + } + + @Override + public void click() { + final SynchronousQueue syncQueue = new SynchronousQueue(); + currentActivity = robocop.getCurrentActivity(); + currentActivity.runOnUiThread( + new Runnable() { + public void run() { + View view = (View)currentActivity.findViewById(id); + if(view != null) { + view.performClick(); + } else { + throw new RoboCopException("click: unable to find view "+id); + } + syncQueue.offer(new Object()); + } + }); + try { + syncQueue.take(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private Object text; + private Activity elementActivity; + + @Override + public String getText() { + elementActivity = robocop.getCurrentActivity(); + final SynchronousQueue syncQueue = new SynchronousQueue(); + elementActivity.runOnUiThread( + new Runnable() { + public void run() { + View v = elementActivity.findViewById(id); + if(v instanceof EditText) { + EditText et = (EditText)v; + text = et.getEditableText(); + }else if(v instanceof TextSwitcher) { + TextSwitcher ts = (TextSwitcher)v; + ts.getNextView(); + text = ((TextView)ts.getCurrentView()).getText(); + }else if(v instanceof ViewGroup) { + ViewGroup vg = (ViewGroup)v; + for(int i = 0; i < vg.getChildCount(); i++) { + if(vg.getChildAt(i) instanceof TextView) { + text = ((TextView)vg.getChildAt(i)).getText(); + } + } //end of for + } else if(v instanceof TextView) { + text = ((TextView)v).getText(); + } else if(v == null) { + throw new RoboCopException("getText: unable to find view "+id); + } else { + throw new RoboCopException("getText: unhandled type for view "+id); + } + syncQueue.offer(new Object()); + } // end of run() method definition + } // end of anonymous Runnable object instantiation + ); + try { + //Wait for the UiThread code to finish running + syncQueue.take(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if(text == null) { + throw new RoboCopException("getText: Text is null for view "+id); + } + return text.toString(); + } + + @Override + public boolean isDisplayed() { + // TODO Auto-generated method stub + return false; + } +} diff --git a/build/mobile/robocop/Makefile.in b/build/mobile/robocop/Makefile.in new file mode 100644 index 000000000000..8ea8256ef6b2 --- /dev/null +++ b/build/mobile/robocop/Makefile.in @@ -0,0 +1,136 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Android sutagent for testing. +# +# The Initial Developer of the Original Code is +# Mozilla Foundation +# Portions created by the Initial Developer are Copyright (C) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Clint Talbert +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +TESTPATH = $(topsrcdir)/mobile/android/base/tests + +include $(DEPTH)/config/autoconf.mk + +MODULE = robocop + +ROBOTIUM_PATH = $(srcdir)/robotium-solo-3.0.jar + +JAVAFILES = \ + R.java \ + +_JAVA_HARNESS = \ + Driver.java \ + Element.java \ + Actions.java \ + FennecNativeElement.java \ + RoboCopException.java \ + FennecNativeDriver.java \ + FennecNativeActions.java \ + +_JAVA_TESTS = $(patsubst $(TESTPATH)/%.in,%,$(wildcard $(TESTPATH)/*.java.in)) + +_ROBOCOP_TOOLS = \ + $(TESTPATH)/robocop.ini \ + parse_ids.py \ + $(NULL) + +GARBAGE += \ + AndroidManifest.xml \ + _JAVA_TESTS \ + _JAVA_HARNESS \ + classes.dex \ + robocop.apk \ + robocop.ap_ \ + robocop-unsigned-unaligned.apk \ + robocop-unaligned.apk \ + $(NULL) + +DEFINES += \ + -DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \ + $(NULL) + +GARBAGE_DIRS += res + +JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar:$(ROBOTIUM_PATH) + +include $(topsrcdir)/config/rules.mk + +# Override rules.mk java flags with the android specific ones +include $(topsrcdir)/config/android-common.mk + +$(_JAVA_HARNESS): % : %.in + $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@ + +AndroidManifest.xml: % : %.in + $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@ + +$(_JAVA_TESTS): % : $(TESTPATH)/%.in + $(NSINSTALL) -D $(DEPTH)/mobile/android/base/tests + $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $(DEPTH)/mobile/android/base/tests/$@ + +$(_ROBOCOP_TOOLS): + cp $(TESTPATH)/robocop.ini robocop.ini + cp $(srcdir)/parse_ids.txt parse_ids.txt + +tools:: robocop.apk + +classes.dex: robocop.ap_ +classes.dex: $(_ROBOCOP_TOOLS) +classes.dex: $(_JAVA_HARNESS) +classes.dex: $(_JAVA_TESTS) +classes.dex: $(TEST_FILES) + $(NSINSTALL) -D classes + $(JAVAC) $(JAVAC_FLAGS) -d classes $(JAVAFILES) $(_JAVA_HARNESS) $(addprefix $(DEPTH)/mobile/android/base/tests/,$(_JAVA_TESTS)) + $(DX) --dex --output=$@ classes $(ROBOTIUM_PATH) + +robocop.ap_: AndroidManifest.xml + $(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -I . -S res -F $@ -J ./ + +robocop-unsigned-unaligned.apk: robocop.ap_ classes.dex + $(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z robocop.ap_ -f classes.dex + +robocop-unaligned.apk: robocop-unsigned-unaligned.apk + cp robocop-unsigned-unaligned.apk $@ + jarsigner -keystore ~/.android/debug.keystore -storepass android -keypass android $@ androiddebugkey + +robocop.apk: robocop-unaligned.apk + $(ZIPALIGN) -f -v 4 robocop-unaligned.apk $@ + cp $(TESTPATH)/robocop.ini robocop.ini + cp $(srcdir)/parse_ids.py parse_ids.py + +export:: + $(NSINSTALL) -D res + @(cd $(srcdir)/res && tar $(TAR_CREATE_FLAGS) - *) | (cd $(DEPTH)/build/mobile/robocop/res && tar -xf -) + diff --git a/build/mobile/robocop/RoboCopException.java.in b/build/mobile/robocop/RoboCopException.java.in new file mode 100644 index 000000000000..de908cc32137 --- /dev/null +++ b/build/mobile/robocop/RoboCopException.java.in @@ -0,0 +1,59 @@ +#filter substitution +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Firefox Mobile Test Framework. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Trevor Fairey + * David Burns + * Joel Maher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package @ANDROID_PACKAGE_NAME@; + +public class RoboCopException extends RuntimeException { + + public RoboCopException(){ + super(); + } + + public RoboCopException(String message){ + super(message); + } + + public RoboCopException(Throwable cause){ + super(cause); + } + + public RoboCopException(String message, Throwable cause){ + super(message, cause); + } +} diff --git a/build/mobile/robocop/parse_ids.py b/build/mobile/robocop/parse_ids.py new file mode 100644 index 000000000000..6fafe88e2cd8 --- /dev/null +++ b/build/mobile/robocop/parse_ids.py @@ -0,0 +1,60 @@ +import re +import os +import sys +import optparse + +def getFile(filename): + fHandle = open(filename, 'r') + data = fHandle.read() + fHandle.close() + return data + +def findIDs(data): + start_function = False + reID = re.compile('.*public static final class id {.*') + reEnd = re.compile('.*}.*') + idlist = [] + + for line in data.split('\n'): + if reEnd.match(line): + start_function = False + + if start_function: + id_value = line.split(' ')[-1] + idlist.append(id_value.split(';')[0].split('=')) + + if reID.match(line): + start_function = True + + return idlist + + +def printIDs(outputFile, idlist): + fOutput = open(outputFile, 'w') + for item in idlist: + fOutput.write("%s=%s\n" % (item[0], item[1])) + fOutput.close() + +def main(args=sys.argv[1:]): + parser = optparse.OptionParser() + parser.add_option('-o', '--output', dest='outputFile', default='', + help="output file with the id=value pairs") + parser.add_option('-i', '--input', dest='inputFile', default='', + help="filename of the input R.java file") + options, args = parser.parse_args(args) + + if options.inputFile == '': + print "Error: please provide input file: -i " + sys.exit(1) + + if options.outputFile == '': + print "Error: please provide output file: -o " + sys.exit(1) + + data = getFile(os.path.abspath(options.inputFile)); + idlist = findIDs(data) + printIDs(os.path.abspath(options.outputFile), idlist) + +if __name__ == "__main__": + main() + diff --git a/build/mobile/robocop/res/values/strings.xml b/build/mobile/robocop/res/values/strings.xml new file mode 100644 index 000000000000..78f68c7ef4b7 --- /dev/null +++ b/build/mobile/robocop/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Roboexample + + From ed22c6af2df84eaeb1d873e701f8f78897e072de Mon Sep 17 00:00:00 2001 From: Geoff Brown Date: Thu, 22 Dec 2011 09:09:50 -0500 Subject: [PATCH 11/26] Bug 701076: Minor updates to robotium tests to improve reliability of text entry; r=jmaher --- mobile/android/base/tests/testAwesomebar.java.in | 10 ++++++---- mobile/android/base/tests/testBookmark.java.in | 11 +++++++++-- mobile/android/base/tests/testLoad.java.in | 11 ++++++++--- mobile/android/base/tests/testNewTab.java.in | 12 ++++++++---- mobile/android/base/tests/testPan.java.in | 4 +++- 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/mobile/android/base/tests/testAwesomebar.java.in b/mobile/android/base/tests/testAwesomebar.java.in index aa223cea4d64..08ec40cb7080 100644 --- a/mobile/android/base/tests/testAwesomebar.java.in +++ b/mobile/android/base/tests/testAwesomebar.java.in @@ -67,14 +67,15 @@ public class testAwesomebar extends ActivityInstrumentationTestCase2 { public void testAwesomebar() { // TODO: find a better way to not hardcode this url String url = "http://mochi.test:8888/tests/robocop/robocop.html"; + actions.waitForGeckoEvent("Gecko:Ready"); Element awesomebar = driver.findElement("awesome_bar"); awesomebar.click(); Element urlbar = driver.findElement("awesomebar_text"); + getInstrumentation().waitForIdleSync(); actions.sendKeys(url); - driver.is(url, urlbar.getText(), "Awesomebar URL Typed Properly"); - //could also use: urlbar.clickSpecialKey(Element.SpecialKey.ENTER) - actions.sendKeys("\n"); + driver.is(urlbar.getText(), url, "Awesomebar URL Typed Properly"); + actions.sendSpecialKey(Actions.SpecialKey.ENTER); //wait for screen to load actions.waitForGeckoEvent("DOMContentLoaded"); driver.setupScrollHandling(); @@ -92,7 +93,8 @@ public class testAwesomebar extends ActivityInstrumentationTestCase2 { } //Click the awesomebar again awesomebar.click(); - driver.is(url, urlbar.getText(), "Aweosmebar URL stayed the same"); + getInstrumentation().waitForIdleSync(); + driver.is(urlbar.getText(), url, "Aweosmebar URL stayed the same"); } @Override diff --git a/mobile/android/base/tests/testBookmark.java.in b/mobile/android/base/tests/testBookmark.java.in index 011c07163e49..33ee2e48e3f2 100644 --- a/mobile/android/base/tests/testBookmark.java.in +++ b/mobile/android/base/tests/testBookmark.java.in @@ -67,30 +67,37 @@ public class testBookmark extends ActivityInstrumentationTestCase2 { public void testBookmark(){ // TODO: find a better way to not hardcode this url String url = "http://mochi.test:8888/tests/robocop/robocop.html"; + + actions.waitForGeckoEvent("Gecko:Ready"); Element awesomebar = driver.findElement("awesome_bar"); awesomebar.click(); Element urlbar = driver.findElement("awesomebar_text"); + getInstrumentation().waitForIdleSync(); actions.sendKeys(url); - driver.is(url, urlbar.getText(), "Awesomebar url typed properly"); + driver.is(urlbar.getText(), url, "Awesomebar url typed properly"); //Click the top item in the list. actions.sendSpecialKey(Actions.SpecialKey.DOWN); actions.sendSpecialKey(Actions.SpecialKey.ENTER); actions.waitForGeckoEvent("DOMContentLoaded"); + getInstrumentation().waitForIdleSync(); awesomebar.click(); - driver.is(url, urlbar.getText(),"Awesomebar URL still on"); + driver.is(urlbar.getText(), url, "Awesomebar URL still on"); //Click the Top item in the history list. + getInstrumentation().waitForIdleSync(); actions.sendSpecialKey(Actions.SpecialKey.RIGHT); actions.sendSpecialKey(Actions.SpecialKey.RIGHT); + getInstrumentation().waitForIdleSync(); actions.sendSpecialKey(Actions.SpecialKey.DOWN); actions.sendSpecialKey(Actions.SpecialKey.DOWN); actions.sendSpecialKey(Actions.SpecialKey.ENTER); actions.waitForGeckoEvent("DOMContentLoaded"); + getInstrumentation().waitForIdleSync(); awesomebar.click(); //Unfortunately, the item isn't constant so can't be tested. //driver.is(url, urlbar.getText(),"Shouldn't this be the last url in the history?"); diff --git a/mobile/android/base/tests/testLoad.java.in b/mobile/android/base/tests/testLoad.java.in index 64443c4f2ea7..a18cdd663d53 100644 --- a/mobile/android/base/tests/testLoad.java.in +++ b/mobile/android/base/tests/testLoad.java.in @@ -67,20 +67,25 @@ public class testLoad extends ActivityInstrumentationTestCase2 { public void testLoad(){ // TODO: find a better way to not hardcode this url String url = "http://mochi.test:8888/tests/robocop/robocop.html"; + + actions.waitForGeckoEvent("Gecko:Ready"); Element awesomebar = driver.findElement("awesome_bar"); awesomebar.click(); Element urlbar = driver.findElement("awesomebar_text"); + getInstrumentation().waitForIdleSync(); actions.sendKeys(url); - driver.is(url, urlbar.getText(),"Awesomebar URL Correct"); + getInstrumentation().waitForIdleSync(); + driver.is(urlbar.getText(), url, "Awesomebar URL Correct"); //Select top item in the list. actions.sendSpecialKey(Actions.SpecialKey.DOWN); - actions.sendKeys("\n"); + actions.sendSpecialKey(Actions.SpecialKey.ENTER); actions.waitForGeckoEvent("DOMContentLoaded"); awesomebar.click(); - driver.is(url, urlbar.getText(), "Awesomebar URL is still correct"); + getInstrumentation().waitForIdleSync(); + driver.is(urlbar.getText(), url, "Awesomebar URL is still correct"); } @Override diff --git a/mobile/android/base/tests/testNewTab.java.in b/mobile/android/base/tests/testNewTab.java.in index cd38dfd1a56b..dd6cb039f89f 100644 --- a/mobile/android/base/tests/testNewTab.java.in +++ b/mobile/android/base/tests/testNewTab.java.in @@ -67,20 +67,22 @@ public class testNewTab extends ActivityInstrumentationTestCase2 { public void testNewTab(){ // TODO: find a better way to not hardcode this url String url = "http://mochi.test:8888/tests/robocop/robocop.html"; + actions.waitForGeckoEvent("Gecko:Ready"); Element tabs = driver.findElement("tabs"); //Add one tab tabs.click(); Element urlbar = driver.findElement("awesomebar_text"); + getInstrumentation().waitForIdleSync(); actions.sendKeys(url); - driver.is(url, urlbar.getText(),"Awesomebar url is fine"); + driver.is(urlbar.getText(), url, "Awesomebar url is fine"); actions.sendSpecialKey(Actions.SpecialKey.ENTER); actions.waitForGeckoEvent("DOMContentLoaded"); try{Thread.sleep(5000);}catch(Throwable e){}; //See tab count Element tabCount = driver.findElement("tabs_count"); - driver.is("2", tabCount.getText(),"Number of tabs has increased"); + driver.is(tabCount.getText(), "2", "Number of tabs has increased"); //Click tab list tabs.click(); @@ -88,13 +90,15 @@ public class testNewTab extends ActivityInstrumentationTestCase2 { //Add another tab addTab.click(); + getInstrumentation().waitForIdleSync(); actions.sendKeys(url); - driver.is(url, urlbar.getText(),"URL is still fine"); + getInstrumentation().waitForIdleSync(); + driver.is(urlbar.getText(), url, "URL is still fine"); actions.sendSpecialKey(Actions.SpecialKey.ENTER); actions.waitForGeckoEvent("DOMContentLoaded"); //Check tab count another time. - driver.is("3", tabCount.getText(),"Number of tabs has increased"); + driver.is(tabCount.getText(), "3", "Number of tabs has increased"); } diff --git a/mobile/android/base/tests/testPan.java.in b/mobile/android/base/tests/testPan.java.in index 29b6643389ca..a6cec4eaa10e 100644 --- a/mobile/android/base/tests/testPan.java.in +++ b/mobile/android/base/tests/testPan.java.in @@ -68,11 +68,13 @@ public class testPan extends ActivityInstrumentationTestCase2 { public void testPan() { // TODO: find a better way to not hardcode this url - String url = "http://mochi.test:8888/tests/robocop/robocop.html"; + String url = "http://mochi.test:8888/tests/robocop/pantest.html"; + actions.waitForGeckoEvent("Gecko:Ready"); Element awesomebar = driver.findElement("awesome_bar"); awesomebar.click(); Element urlbar = driver.findElement("awesomebar_text"); + getInstrumentation().waitForIdleSync(); actions.sendKeys(url); driver.is(url, urlbar.getText(),"Asserting Awesomebar typing works"); From 40916411f6dc4d5cfd42ea8c549e00fd32ed42ea Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 22 Nov 2011 11:37:57 -0800 Subject: [PATCH 12/26] Bug 692274, part 1 - Hoist SprintNormalFor (r=jorendorff) --- js/src/jsopcode.cpp | 172 ++++++++++++++++++++++++-------------------- 1 file changed, 93 insertions(+), 79 deletions(-) diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 24807e6acb2d..8fbefc818171 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -2175,8 +2175,6 @@ GetBlockNames(JSContext *cx, JSObject *blockObj, AtomVector *atoms) return true; } -#undef LOCAL_ASSERT - static bool PushBlockNames(JSContext *cx, SprintStack *ss, const AtomVector &atoms) { @@ -2246,6 +2244,92 @@ GetTokenForAssignment(JSPrinter *jp, jssrcnote *sn, JSOp lastop, return token; } +static ptrdiff_t +SprintNormalFor(JSContext *cx, JSPrinter *jp, SprintStack *ss, + const char *init, jsbytecode *initpc, + jsbytecode **ppc, ptrdiff_t *plen) +{ + jsbytecode *pc = *ppc; + jssrcnote *sn = js_GetSrcNote(jp->script, pc); + JS_ASSERT(SN_TYPE(sn) == SRC_FOR); + + /* Print the keyword and the possibly empty init-part. */ + js_printf(jp, "\tfor ("); + SprintOpcodePermanent(jp, init, initpc); + js_printf(jp, ";"); + + /* Skip the JSOP_NOP or JSOP_POP bytecode. */ + JS_ASSERT(*pc == JSOP_NOP || *pc == JSOP_POP); + pc += JSOP_NOP_LENGTH; + + /* Get the cond, next, and loop-closing tail offsets. */ + ptrdiff_t cond = js_GetSrcNoteOffset(sn, 0); + ptrdiff_t next = js_GetSrcNoteOffset(sn, 1); + ptrdiff_t tail = js_GetSrcNoteOffset(sn, 2); + + /* + * If this loop has a condition, then pc points at a goto + * targeting the condition. + */ + jsbytecode *pc2 = pc; + if (cond != tail) { + LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); + pc2 += (*pc == JSOP_GOTO) ? JSOP_GOTO_LENGTH : JSOP_GOTOX_LENGTH; + } + LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == pc2 - pc); + + if (cond != tail) { + /* Decompile the loop condition. */ + if (!Decompile(ss, pc + cond, tail - cond)) + return -1; + js_printf(jp, " "); + jsbytecode *condpc; + const char *cond = PopStr(ss, JSOP_NOP, &condpc); + SprintOpcodePermanent(jp, cond, condpc); + } + + /* Need a semicolon whether or not there was a cond. */ + js_puts(jp, ";"); + + if (next != cond) { + /* + * Decompile the loop updater. It may end in a JSOP_POP + * that we skip; or in a JSOP_POPN that we do not skip, + * followed by a JSOP_NOP (skipped as if it's a POP). + * We cope with the difference between these two cases + * by checking for stack imbalance and popping if there + * is an rval. + */ + uintN saveTop = ss->top; + + if (!Decompile(ss, pc + next, cond - next - JSOP_POP_LENGTH)) + return -1; + LOCAL_ASSERT(ss->top - saveTop <= 1U); + jsbytecode *updatepc = NULL; + const char *update = (ss->top == saveTop) + ? ss->sprinter.base + ss->sprinter.offset + : PopStr(ss, JSOP_NOP, &updatepc); + js_printf(jp, " "); + SprintOpcodePermanent(jp, update, updatepc); + } + + /* Do the loop body. */ + js_printf(jp, ") {\n"); + jp->indent += 4; + next -= pc2 - pc; + if (!Decompile(ss, pc2, next)) + return -1; + jp->indent -= 4; + js_printf(jp, "\t}\n"); + + /* Set len so pc skips over the entire loop. */ + *ppc = pc; + *plen = tail + js_CodeSpec[pc[tail]].length; + return -2; +} + +#undef LOCAL_ASSERT + static JSBool InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) { @@ -2627,78 +2711,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case SRC_FOR: - rval = ""; - rvalpc = NULL; - - do_forloop: - JS_ASSERT(SN_TYPE(sn) == SRC_FOR); - - /* Print the keyword and the possibly empty init-part. */ - js_printf(jp, "\tfor ("); - SprintOpcodePermanent(jp, rval, rvalpc); - js_printf(jp, ";"); - - /* Skip the JSOP_NOP or JSOP_POP bytecode. */ - pc += JSOP_NOP_LENGTH; - - /* Get the cond, next, and loop-closing tail offsets. */ - cond = js_GetSrcNoteOffset(sn, 0); - next = js_GetSrcNoteOffset(sn, 1); - tail = js_GetSrcNoteOffset(sn, 2); - - /* - * If this loop has a condition, then pc points at a goto - * targeting the condition. - */ - pc2 = pc; - if (cond != tail) { - LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); - pc2 += (*pc == JSOP_GOTO) ? JSOP_GOTO_LENGTH : JSOP_GOTOX_LENGTH; - } - LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == pc2 - pc); - - if (cond != tail) { - /* Decompile the loop condition. */ - DECOMPILE_CODE(pc + cond, tail - cond); - js_printf(jp, " "); - rval = PopStr(ss, op, &rvalpc); - SprintOpcodePermanent(jp, rval, rvalpc); - } - - /* Need a semicolon whether or not there was a cond. */ - js_puts(jp, ";"); - - if (next != cond) { - /* - * Decompile the loop updater. It may end in a JSOP_POP - * that we skip; or in a JSOP_POPN that we do not skip, - * followed by a JSOP_NOP (skipped as if it's a POP). - * We cope with the difference between these two cases - * by checking for stack imbalance and popping if there - * is an rval. - */ - uintN saveTop = ss->top; - - DECOMPILE_CODE(pc + next, cond - next - JSOP_POP_LENGTH); - LOCAL_ASSERT(ss->top - saveTop <= 1U); - rvalpc = NULL; - rval = (ss->top == saveTop) - ? ss->sprinter.base + ss->sprinter.offset - : PopStr(ss, op, &rvalpc); - js_printf(jp, " "); - SprintOpcodePermanent(jp, rval, rvalpc); - } - - /* Do the loop body. */ - js_printf(jp, ") {\n"); - jp->indent += 4; - next -= pc2 - pc; - DECOMPILE_CODE(pc2, next); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - - /* Set len so pc skips over the entire loop. */ - len = tail + js_CodeSpec[pc[tail]].length; + /* for loop with empty initializer. */ + todo = SprintNormalFor(cx, jp, ss, "", NULL, &pc, &len); break; case SRC_ENDBRACE: @@ -2862,8 +2876,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) /* * Kill newtop before the end_groupassignment: label by * retracting/popping early. Control will either jump - * to do_forloop: or do_letheadbody: or else break from - * our case JSOP_POPN: after the switch (*pc2) below. + * to do_letheadbody: or else break from our case. */ LOCAL_ASSERT(newtop < oldtop); ss->sprinter.offset = GetOff(ss, newtop); @@ -2903,7 +2916,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (SN_TYPE(sn) == SRC_FOR) { op = JSOP_NOP; pc = pc2; - goto do_forloop; + todo = SprintNormalFor(cx, jp, ss, rval, rvalpc, &pc, &len); + break; } if (SN_TYPE(sn) == SRC_DECL) { @@ -2982,8 +2996,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (ss->opcodes[ss->top-1] == JSOP_IN) op = JSOP_LSH; rval = PopStr(ss, op, &rvalpc); - todo = -2; - goto do_forloop; + todo = SprintNormalFor(cx, jp, ss, rval, rvalpc, &pc, &len); + break; case SRC_PCDELTA: /* Comma operator: use JSOP_POP for correct precedence. */ From e8ece4c65ab7deaf8e0bd390a955df75e41cba0c Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 22 Nov 2011 11:37:57 -0800 Subject: [PATCH 13/26] Bug 692274, part 2 - Fix generator expression bug (r=waldo) --- .../jit-test/tests/basic/testThatGenExpsActuallyDecompile.js | 4 ++++ js/src/jsopcode.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/basic/testThatGenExpsActuallyDecompile.js diff --git a/js/src/jit-test/tests/basic/testThatGenExpsActuallyDecompile.js b/js/src/jit-test/tests/basic/testThatGenExpsActuallyDecompile.js new file mode 100644 index 000000000000..1f309b962a48 --- /dev/null +++ b/js/src/jit-test/tests/basic/testThatGenExpsActuallyDecompile.js @@ -0,0 +1,4 @@ +// |jit-test| error: 3 +var str = (function (x) {return (i for (i in x));}).toSource().replace('\n', ''); +assertEq(str, "(function (x) {return (i for (i in x));})"); +throw 3; diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 8fbefc818171..2947d509d07d 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -4362,7 +4362,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (fun->script()->bindings.hasLocalNames()) { innerLocalNames = cx->new_ >(cx); if (!innerLocalNames || - fun->script()->bindings.getLocalNameArray(cx, innerLocalNames)) + !fun->script()->bindings.getLocalNameArray(cx, innerLocalNames)) { return NULL; } From 0b5000dc38250e3c43b93032636e4ff71d8a3eeb Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 7 Oct 2011 12:02:50 -0700 Subject: [PATCH 14/26] Bug 692274, part 3 - Remove JSOP_BLOCKCHAIN and JSOP_NULLBLOCKCHAIN, which produces incorrect let scoping until the next patch (r=jorendorff) --- js/src/frontend/BytecodeEmitter-inl.h | 2 +- js/src/frontend/BytecodeEmitter.cpp | 233 ++---------------- js/src/frontend/BytecodeEmitter.h | 16 +- js/src/frontend/ParseNode.h | 2 - js/src/frontend/Parser.cpp | 23 +- js/src/jit-test/tests/basic/testBug683470.js | 2 +- .../tests/jaeger/bug563000/eif-trap-newvar.js | 2 +- .../jaeger/bug563000/trap-from-add-inline.js | 2 +- .../jaeger/bug563000/trap-from-add-ool.js | 2 +- .../jaeger/bug563000/trap-self-as-parent.js | 2 +- .../jaeger/bug563000/trap-self-from-trap.js | 2 +- js/src/jsfun.cpp | 2 +- js/src/jsfun.h | 3 +- js/src/jsinfer.cpp | 2 - js/src/jsinterp.cpp | 229 ++++++----------- js/src/jsinterp.h | 16 +- js/src/jsobj.cpp | 15 +- js/src/jsobj.h | 2 +- js/src/jsobjinlines.h | 2 +- js/src/jsopcode.cpp | 14 -- js/src/jsopcode.tbl | 19 +- js/src/jsopcodeinlines.h | 14 -- js/src/methodjit/Compiler.cpp | 13 +- js/src/methodjit/InvokeHelpers.cpp | 2 + js/src/methodjit/StubCalls.cpp | 38 +-- js/src/methodjit/StubCalls.h | 2 +- js/src/vm/Stack-inl.h | 4 +- js/src/vm/Stack.cpp | 3 +- js/src/vm/Stack.h | 26 +- js/src/vm/StackSpace.h | 2 +- 30 files changed, 197 insertions(+), 499 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter-inl.h b/js/src/frontend/BytecodeEmitter-inl.h index b393d4fd0fa5..422359d43e61 100644 --- a/js/src/frontend/BytecodeEmitter-inl.h +++ b/js/src/frontend/BytecodeEmitter-inl.h @@ -49,7 +49,7 @@ namespace js { inline TreeContext::TreeContext(Parser *prs) : flags(0), bodyid(0), blockidGen(0), parenDepth(0), yieldCount(0), argumentsCount(0), - topStmt(NULL), topScopeStmt(NULL), blockChainBox(NULL), blockNode(NULL), + topStmt(NULL), topScopeStmt(NULL), blockChain(NULL), blockNode(NULL), decls(prs->context), parser(prs), yieldNode(NULL), argumentsNode(NULL), scopeChain_(NULL), lexdeps(prs->context), parent(prs->tc), staticLevel(0), funbox(NULL), functionList(NULL), innermostWith(NULL), bindings(prs->context), sharpSlotBase(-1) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 85786a427a2d..b7eeb2803f87 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -96,9 +96,6 @@ NewTryNote(JSContext *cx, BytecodeEmitter *bce, JSTryNoteKind kind, uintN stackD static bool EmitIndexOp(JSContext *cx, JSOp op, uintN index, BytecodeEmitter *bce, JSOp *psuffix = NULL); -static JSBool -EmitLeaveBlock(JSContext *cx, BytecodeEmitter *bce, JSOp op, ObjectBox *box); - static JSBool SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, uintN index, uintN which, ptrdiff_t offset); @@ -298,24 +295,6 @@ frontend::Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1, return offset; } -ptrdiff_t -frontend::Emit5(JSContext *cx, BytecodeEmitter *bce, JSOp op, uint16_t op1, uint16_t op2) -{ - ptrdiff_t offset = EmitCheck(cx, bce, 5); - - if (offset >= 0) { - jsbytecode *next = bce->next(); - next[0] = (jsbytecode)op; - next[1] = UINT16_HI(op1); - next[2] = UINT16_LO(op1); - next[3] = UINT16_HI(op2); - next[4] = UINT16_LO(op2); - bce->current->next = next + 5; - UpdateDepth(cx, bce, offset); - } - return offset; -} - ptrdiff_t frontend::EmitN(JSContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra) { @@ -1366,7 +1345,7 @@ frontend::PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_ stmt->blockid = tc->blockid(); SET_STATEMENT_TOP(stmt, top); stmt->label = NULL; - JS_ASSERT(!stmt->blockBox); + JS_ASSERT(!stmt->blockObj); stmt->down = tc->topStmt; tc->topStmt = stmt; if (STMT_LINKS_SCOPE(stmt)) { @@ -1378,16 +1357,15 @@ frontend::PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_ } void -frontend::PushBlockScope(TreeContext *tc, StmtInfo *stmt, ObjectBox *blockBox, ptrdiff_t top) +frontend::PushBlockScope(TreeContext *tc, StmtInfo *stmt, JSObject *blockObj, ptrdiff_t top) { PushStatement(tc, stmt, STMT_BLOCK, top); stmt->flags |= SIF_SCOPE; - blockBox->parent = tc->blockChainBox; - blockBox->object->setStaticBlockScopeChain(tc->blockChain()); + blockObj->setStaticBlockScopeChain(tc->blockChain); stmt->downScope = tc->topScopeStmt; tc->topScopeStmt = stmt; - tc->blockChainBox = blockBox; - stmt->blockBox = blockBox; + tc->blockChain = blockObj; + stmt->blockObj = blockObj; } /* @@ -1581,8 +1559,8 @@ EmitNonLocalJumpFixup(JSContext *cx, BytecodeEmitter *bce, StmtInfo *toStmt) FLUSH_POPS(); if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0) return JS_FALSE; - if (!EmitLeaveBlock(cx, bce, JSOP_LEAVEBLOCK, stmt->blockBox)) - return JS_FALSE; + uintN i = OBJ_BLOCK_COUNT(cx, stmt->blockObj); + EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i); } } @@ -1593,20 +1571,6 @@ EmitNonLocalJumpFixup(JSContext *cx, BytecodeEmitter *bce, StmtInfo *toStmt) #undef FLUSH_POPS } -static JSBool -EmitKnownBlockChain(JSContext *cx, BytecodeEmitter *bce, ObjectBox *box) -{ - if (box) - return EmitIndexOp(cx, JSOP_BLOCKCHAIN, box->index, bce); - return Emit1(cx, bce, JSOP_NULLBLOCKCHAIN) >= 0; -} - -static JSBool -EmitBlockChain(JSContext *cx, BytecodeEmitter *bce) -{ - return EmitKnownBlockChain(cx, bce, bce->blockChainBox); -} - static const jsatomid INVALID_ATOMID = -1; static ptrdiff_t @@ -1627,14 +1591,7 @@ EmitGoto(JSContext *cx, BytecodeEmitter *bce, StmtInfo *toStmt, ptrdiff_t *lastp if (index < 0) return -1; - ptrdiff_t result = EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, lastp); - if (result < 0) - return result; - - if (!EmitBlockChain(cx, bce)) - return -1; - - return result; + return EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, lastp); } static JSBool @@ -1668,9 +1625,8 @@ frontend::PopStatementTC(TreeContext *tc) tc->topStmt = stmt->down; if (STMT_LINKS_SCOPE(stmt)) { tc->topScopeStmt = stmt->downScope; - if (stmt->flags & SIF_SCOPE) { - tc->blockChainBox = stmt->blockBox->parent; - } + if (stmt->flags & SIF_SCOPE) + tc->blockChain = stmt->blockObj->staticBlockScopeChain(); } } @@ -1712,7 +1668,7 @@ frontend::LexicalLookup(TreeContext *tc, JSAtom *atom, jsint *slotp, StmtInfo *s if (!(stmt->flags & SIF_SCOPE)) continue; - JSObject *obj = stmt->blockBox->object; + JSObject *obj = stmt->blockObj; JS_ASSERT(obj->isStaticBlock()); const Shape *shape = obj->nativeLookup(tc->parser->context, ATOM_TO_JSID(atom)); @@ -2031,20 +1987,6 @@ EmitEnterBlock(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce) return true; } -static JSBool -EmitLeaveBlock(JSContext *cx, BytecodeEmitter *bce, JSOp op, ObjectBox *box) -{ - JSOp bigSuffix; - uintN count = OBJ_BLOCK_COUNT(cx, box->object); - - bigSuffix = EmitBigIndexPrefix(cx, bce, box->index); - if (bigSuffix == JSOP_FALSE) - return JS_FALSE; - if (Emit5(cx, bce, op, count, box->index) < 0) - return JS_FALSE; - return bigSuffix == JSOP_NOP || Emit1(cx, bce, bigSuffix) >= 0; -} - /* * Try to convert a *NAME op to a *GNAME op, which optimizes access to * undeclared globals. Return true if a conversion was made. @@ -3223,56 +3165,6 @@ AllocateSwitchConstant(JSContext *cx) return cx->tempLifoAlloc().new_(); } -/* - * Sometimes, let-slots are pushed to the JS stack before we logically enter - * the let scope. For example, - * let (x = EXPR) BODY - * compiles to roughly {enterblock; EXPR; setlocal x; BODY; leaveblock} even - * though EXPR is evaluated in the enclosing scope; it does not see x. - * - * In those cases we use TempPopScope around the code to emit EXPR. It - * temporarily removes the let-scope from the BytecodeEmitter's scope stack and - * emits extra bytecode to ensure that js::GetBlockChain also finds the correct - * scope at run time. - */ -class TempPopScope { - StmtInfo *savedStmt; - StmtInfo *savedScopeStmt; - ObjectBox *savedBlockBox; - - public: - TempPopScope() : savedStmt(NULL), savedScopeStmt(NULL), savedBlockBox(NULL) {} - - bool popBlock(JSContext *cx, BytecodeEmitter *bce) { - savedStmt = bce->topStmt; - savedScopeStmt = bce->topScopeStmt; - savedBlockBox = bce->blockChainBox; - - if (bce->topStmt->type == STMT_FOR_LOOP || bce->topStmt->type == STMT_FOR_IN_LOOP) - PopStatementTC(bce); - JS_ASSERT(STMT_LINKS_SCOPE(bce->topStmt)); - JS_ASSERT(bce->topStmt->flags & SIF_SCOPE); - PopStatementTC(bce); - - /* - * Since we have changed the block chain, emit an instruction marking - * the change for the benefit of dynamic GetScopeChain callers such as - * the debugger. - * - * FIXME bug 671360 - The JSOP_NOP instruction should not be necessary. - */ - return Emit1(cx, bce, JSOP_NOP) >= 0 && EmitBlockChain(cx, bce); - } - - bool repushBlock(JSContext *cx, BytecodeEmitter *bce) { - JS_ASSERT(savedStmt); - bce->topStmt = savedStmt; - bce->topScopeStmt = savedScopeStmt; - bce->blockChainBox = savedBlockBox; - return Emit1(cx, bce, JSOP_NOP) >= 0 && EmitBlockChain(cx, bce); - } -}; - static JSBool EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { @@ -3287,7 +3179,7 @@ EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) size_t switchSize, tableSize; jsbytecode *pc, *savepc; #if JS_HAS_BLOCK_SCOPE - ObjectBox *box; + int count; #endif StmtInfo stmtInfo; @@ -3305,7 +3197,6 @@ EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) */ pn2 = pn->pn_right; #if JS_HAS_BLOCK_SCOPE - TempPopScope tps; if (pn2->isKind(PNK_LEXICALSCOPE)) { /* * Push the body's block scope before discriminant code-gen to reflect @@ -3313,24 +3204,17 @@ EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) * the discriminant on the stack so that case-dispatch bytecodes can * find the discriminant on top of stack. */ - box = pn2->pn_objbox; - PushBlockScope(bce, &stmtInfo, box, -1); + count = OBJ_BLOCK_COUNT(cx, pn2->pn_objbox->object); + PushBlockScope(bce, &stmtInfo, pn2->pn_objbox->object, -1); stmtInfo.type = STMT_SWITCH; /* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */ if (!EmitEnterBlock(cx, pn2, bce)) return JS_FALSE; - - /* - * Pop the switch's statement info around discriminant code-gen, which - * belongs in the enclosing scope. - */ - if (!tps.popBlock(cx, bce)) - return JS_FALSE; } #ifdef __GNUC__ else { - box = NULL; + count = 0; } #endif #endif @@ -3350,10 +3234,6 @@ EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (pn2->isKind(PNK_STATEMENTLIST)) { PushStatement(bce, &stmtInfo, STMT_SWITCH, top); } else { - /* Re-push the switch's statement info record. */ - if (!tps.repushBlock(cx, bce)) - return JS_FALSE; - /* * Set the statement info record's idea of top. Reset top too, since * repushBlock emits code. @@ -3806,7 +3686,7 @@ out: #if JS_HAS_BLOCK_SCOPE if (ok && pn->pn_right->isKind(PNK_LEXICALSCOPE)) - ok = EmitLeaveBlock(cx, bce, JSOP_LEAVEBLOCK, box); + EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count); #endif } return ok; @@ -4956,7 +4836,6 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) ptrdiff_t tryEnd = bce->offset(); - ObjectBox *prevBox = NULL; /* If this try has a catch block, emit it. */ ParseNode *lastCatch = NULL; if (ParseNode *pn2 = pn->pn_kid2) { @@ -4965,7 +4844,6 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) /* * The emitted code for a catch block looks like: * - * blockchain * [throwing] only if 2nd+ catch block * [leaveblock] only if 2nd+ catch block * enterblock with SRC_CATCH @@ -4991,9 +4869,6 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) JS_ASSERT(bce->stackDepth == depth); guardJump = GUARDJUMP(stmtInfo); if (guardJump != -1) { - if (EmitKnownBlockChain(cx, bce, prevBox) < 0) - return false; - /* Fix up and clean up previous catch block. */ CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, guardJump); @@ -5016,8 +4891,7 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) } if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0) return false; - if (!EmitLeaveBlock(cx, bce, JSOP_LEAVEBLOCK, prevBox)) - return false; + EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count); JS_ASSERT(bce->stackDepth == depth); } @@ -5040,7 +4914,6 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) */ JS_ASSERT(pn3->isKind(PNK_LEXICALSCOPE)); count = OBJ_BLOCK_COUNT(cx, pn3->pn_objbox->object); - prevBox = pn3->pn_objbox; if (!EmitTree(cx, bce, pn3)) return false; @@ -5075,9 +4948,6 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) * stack unbalanced. */ if (lastCatch && lastCatch->pn_kid2) { - if (EmitKnownBlockChain(cx, bce, prevBox) < 0) - return false; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, GUARDJUMP(stmtInfo)); /* Sync the stack to take into account pushed exception. */ @@ -5090,9 +4960,6 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) */ if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0 || Emit1(cx, bce, JSOP_THROW) < 0) return false; - - if (EmitBlockChain(cx, bce) < 0) - return false; } JS_ASSERT(bce->stackDepth == depth); @@ -5261,26 +5128,12 @@ EmitLet(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) pn2 = NULL; } - /* - * Non-null pn2 means that pn is the variable list from a let head. - * - * Use TempPopScope to evaluate the expressions in the enclosing scope. - * This also causes the initializing assignments to be emitted in the - * enclosing scope, but the assignment opcodes emitted here - * (essentially just setlocal, though destructuring assignment uses - * other additional opcodes) do not care about the block chain. - */ + /* Non-null pn2 means that pn is the variable list from a let head. */ JS_ASSERT(pn->isArity(PN_LIST)); - TempPopScope tps; - bool popScope = pn2 || (bce->flags & TCF_IN_FOR_INIT); - if (popScope && !tps.popBlock(cx, bce)) - return false; ptrdiff_t noteIndex; if (!EmitVariables(cx, bce, pn, pn2 != NULL, ¬eIndex)) return false; ptrdiff_t tmp = bce->offset(); - if (popScope && !tps.repushBlock(cx, bce)) - return false; /* Thus non-null pn2 is the body of the let block or expression. */ if (pn2 && !EmitTree(cx, bce, pn2)) @@ -5374,7 +5227,7 @@ EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) StmtInfo stmtInfo; StmtInfo *stmt; ObjectBox *objbox = pn->pn_objbox; - PushBlockScope(bce, &stmtInfo, objbox, bce->offset()); + PushBlockScope(bce, &stmtInfo, objbox->object, bce->offset()); /* * If this lexical scope is not for a catch block, let block or let @@ -5419,8 +5272,8 @@ EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) } /* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */ - if (!EmitLeaveBlock(cx, bce, op, objbox)) - return false; + uintN count = OBJ_BLOCK_COUNT(cx, objbox->object); + EMIT_UINT16_IMM_OP(op, count); return PopStatementBCE(cx, bce); } @@ -5435,9 +5288,6 @@ EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (Emit1(cx, bce, JSOP_ENTERWITH) < 0) return false; - /* Make blockChain determination quicker. */ - if (EmitBlockChain(cx, bce) < 0) - return false; if (!EmitTree(cx, bce, pn->pn_right)) return false; if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0) @@ -5495,10 +5345,8 @@ EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) * see Parser::forStatement. 'for (let x = i in o)' is mercifully * banned. */ - bool forLet = false; if (ParseNode *decl = forHead->pn_kid1) { JS_ASSERT(decl->isKind(PNK_VAR) || decl->isKind(PNK_LET)); - forLet = decl->isKind(PNK_LET); bce->flags |= TCF_IN_FOR_INIT; if (!EmitTree(cx, bce, decl)) return false; @@ -5506,15 +5354,8 @@ EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) } /* Compile the object expression to the right of 'in'. */ - { - TempPopScope tps; - if (forLet && !tps.popBlock(cx, bce)) - return false; - if (!EmitTree(cx, bce, forHead->pn_kid3)) - return false; - if (forLet && !tps.repushBlock(cx, bce)) - return false; - } + if (!EmitTree(cx, bce, forHead->pn_kid3)) + return JS_FALSE; /* * Emit a bytecode to convert top of stack value to the iterator @@ -5825,9 +5666,7 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) return false; } EMIT_INDEX_OP(pn->getOp(), index); - - /* Make blockChain determination quicker. */ - return EmitBlockChain(cx, bce) >= 0; + return true; } /* @@ -5847,10 +5686,6 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) bce->switchToProlog(); JSOp op = fun->isFlatClosure() ? JSOP_DEFFUN_FC : JSOP_DEFFUN; EMIT_INDEX_OP(op, index); - - /* Make blockChain determination quicker. */ - if (EmitBlockChain(cx, bce) < 0) - return false; bce->switchToMain(); } @@ -5870,11 +5705,7 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { return false; } - if (!EmitSlotIndexOp(cx, op, slot, index, bce)) - return false; - - /* Make blockChain determination quicker. */ - return EmitBlockChain(cx, bce) >= 0; + return EmitSlotIndexOp(cx, op, slot, index, bce); } return true; @@ -6068,8 +5899,6 @@ EmitReturn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) bce->base()[top] = JSOP_SETRVAL; if (Emit1(cx, bce, JSOP_RETRVAL) < 0) return false; - if (EmitBlockChain(cx, bce) < 0) - return false; } return true; @@ -6381,11 +6210,8 @@ EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) if (Emit3(cx, bce, pn->getOp(), ARGC_HI(argc), ARGC_LO(argc)) < 0) return false; CheckTypeSet(cx, bce, pn->getOp()); - if (pn->isOp(JSOP_EVAL)) { + if (pn->isOp(JSOP_EVAL)) EMIT_UINT16_IMM_OP(JSOP_LINENO, pn->pn_pos.begin.lineno); - if (EmitBlockChain(cx, bce) < 0) - return false; - } if (pn->pn_xflags & PNX_SETCALL) { if (Emit1(cx, bce, JSOP_SETCALL) < 0) return false; @@ -7212,10 +7038,6 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, jmp); if (EmitJump(cx, bce, JSOP_ENDFILTER, top - bce->offset()) < 0) return JS_FALSE; - - /* Make blockChain determination quicker. */ - if (EmitBlockChain(cx, bce) < 0) - return JS_FALSE; break; #endif @@ -7852,8 +7674,7 @@ CGObjectList::index(ObjectBox *objbox) JS_ASSERT(!objbox->emitLink); objbox->emitLink = lastbox; lastbox = objbox; - objbox->index = length++; - return objbox->index; + return length++; } void diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index cb731f5b9247..e337ce17c8f4 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -138,7 +138,7 @@ struct StmtInfo { ptrdiff_t continues; /* offset of last continue in loop */ union { JSAtom *label; /* name of LABEL */ - ObjectBox *blockBox; /* block scope object */ + JSObject *blockObj; /* block scope object */ }; StmtInfo *down; /* info for enclosing statement */ StmtInfo *downScope; /* next enclosing lexical scope */ @@ -299,7 +299,7 @@ struct TreeContext { /* tree context for semantic checks */ at non-zero depth in current paren tree */ StmtInfo *topStmt; /* top of statement info stack */ StmtInfo *topScopeStmt; /* top lexical scope statement */ - ObjectBox *blockChainBox; /* compile time block scope chain (NB: one + JSObject *blockChain; /* compile time block scope chain (NB: one deeper than the topScopeStmt/downScope chain when in head of let block/expr) */ ParseNode *blockNode; /* parse node for a block with let declarations @@ -376,10 +376,6 @@ struct TreeContext { /* tree context for semantic checks */ uintN blockid() { return topStmt ? topStmt->blockid : bodyid; } - JSObject *blockChain() { - return blockChainBox ? blockChainBox->object : NULL; - } - /* * True if we are at the topmost level of a entire script or function body. * For example, while parsing this code we would encounter f1 and f2 at @@ -796,12 +792,6 @@ Emit2(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1); ptrdiff_t Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1, jsbytecode op2); -/* - * Emit five bytecodes, an opcode with two 16-bit immediates. - */ -ptrdiff_t -Emit5(JSContext *cx, BytecodeEmitter *bce, JSOp op, uint16_t op1, uint16_t op2); - /* * Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand. */ @@ -843,7 +833,7 @@ PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top); * (if generating code), PopStatementBCE. */ void -PushBlockScope(TreeContext *tc, StmtInfo *stmt, ObjectBox *blockBox, ptrdiff_t top); +PushBlockScope(TreeContext *tc, StmtInfo *stmt, JSObject *blockObj, ptrdiff_t top); /* * Pop tc->topStmt. If the top StmtInfo struct is not stack-allocated, it diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index d76307f9dc0d..425ef9e394f1 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -1293,8 +1293,6 @@ struct ObjectBox { ObjectBox *traceLink; ObjectBox *emitLink; JSObject *object; - ObjectBox *parent; - uintN index; bool isFunctionBox; }; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 9f82a4bc6a58..65a305838738 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -1882,7 +1882,7 @@ MatchLabel(JSContext *cx, TokenStream *ts, PropertyName **label) * must already be in such a scope. * * Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a - * property for the new variable on the block object, tc->blockChain(); + * property for the new variable on the block object, tc->blockChain; * populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to * data->pn in a slot of the block object. */ @@ -1903,7 +1903,7 @@ BindLet(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc) if (!CheckStrictBinding(cx, tc, atom->asPropertyName(), pn)) return false; - blockObj = tc->blockChain(); + blockObj = tc->blockChain; Definition *dn = tc->decls.lookupFirst(atom); if (dn && dn->pn_blockid == tc->blockid()) { JSAutoByteString name; @@ -1966,7 +1966,7 @@ PopStatement(TreeContext *tc) StmtInfo *stmt = tc->topStmt; if (stmt->flags & SIF_SCOPE) { - JSObject *obj = stmt->blockBox->object; + JSObject *obj = stmt->blockObj; JS_ASSERT(!obj->isClonedBlock()); for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) { @@ -2613,8 +2613,8 @@ CheckDestructuring(JSContext *cx, BindData *data, ParseNode *left, TreeContext * */ if (data && data->binder == BindLet && - OBJ_BLOCK_COUNT(cx, tc->blockChain()) == 0 && - !DefineNativeProperty(cx, tc->blockChain(), + OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0 && + !DefineNativeProperty(cx, tc->blockChain, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), UndefinedValue(), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT, @@ -2778,7 +2778,7 @@ PushLexicalScope(JSContext *cx, TokenStream *ts, TreeContext *tc, StmtInfo *stmt if (!blockbox) return NULL; - PushBlockScope(tc, stmt, blockbox, -1); + PushBlockScope(tc, stmt, obj, -1); pn->setOp(JSOP_LEAVEBLOCK); pn->pn_objbox = blockbox; pn->pn_cookie.makeFree(); @@ -3639,7 +3639,7 @@ Parser::letStatement() } if (stmt && (stmt->flags & SIF_SCOPE)) { - JS_ASSERT(tc->blockChainBox == stmt->blockBox); + JS_ASSERT(tc->blockChain == stmt->blockObj); } else { if (!stmt || (stmt->flags & SIF_BODY_BLOCK)) { /* @@ -3685,10 +3685,9 @@ Parser::letStatement() stmt->downScope = tc->topScopeStmt; tc->topScopeStmt = stmt; - obj->setStaticBlockScopeChain(tc->blockChain()); - blockbox->parent = tc->blockChainBox; - tc->blockChainBox = blockbox; - stmt->blockBox = blockbox; + obj->setStaticBlockScopeChain(tc->blockChain); + tc->blockChain = obj; + stmt->blockObj = obj; #ifdef DEBUG ParseNode *tmp = tc->blockNode; @@ -4196,7 +4195,7 @@ Parser::variables(ParseNodeKind kind, bool inLetHead) * this code will change soon. */ if (let) { - JS_ASSERT(tc->blockChainBox == scopeStmt->blockBox); + JS_ASSERT(tc->blockChain == scopeStmt->blockObj); data.binder = BindLet; data.let.overflow = JSMSG_TOO_MANY_LOCALS; } else { diff --git a/js/src/jit-test/tests/basic/testBug683470.js b/js/src/jit-test/tests/basic/testBug683470.js index 7219c3140922..83b0945a1b60 100644 --- a/js/src/jit-test/tests/basic/testBug683470.js +++ b/js/src/jit-test/tests/basic/testBug683470.js @@ -11,5 +11,5 @@ f = (function() { } catch (e) {} } }) -trap(f, 52, undefined); +trap(f, 39, undefined); f() diff --git a/js/src/jit-test/tests/jaeger/bug563000/eif-trap-newvar.js b/js/src/jit-test/tests/jaeger/bug563000/eif-trap-newvar.js index 85cd185f6090..b9d62794303d 100644 --- a/js/src/jit-test/tests/jaeger/bug563000/eif-trap-newvar.js +++ b/js/src/jit-test/tests/jaeger/bug563000/eif-trap-newvar.js @@ -6,5 +6,5 @@ function caller(code, obj) { eval(code); // Make the compiler give up on binding analysis. return x; } -trap(caller, 14, "var x = 'success'; nop()"); +trap(caller, 12, "var x = 'success'; nop()"); assertEq(caller("var y = 'ignominy'", this), "success"); diff --git a/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-inline.js b/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-inline.js index 5f8cb55bb980..59da2a43ea37 100644 --- a/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-inline.js +++ b/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-inline.js @@ -3,7 +3,7 @@ setDebug(true); x = "notset"; function main() { /* The JSOP_STOP in main. */ - a = { valueOf: function () { trap(main, 36, "success()"); } }; + a = { valueOf: function () { trap(main, 34, "success()"); } }; a + ""; x = "failure"; } diff --git a/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js b/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js index 3b2417649720..5d38997d979f 100644 --- a/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js +++ b/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js @@ -3,7 +3,7 @@ setDebug(true); x = "notset"; function main() { /* The JSOP_STOP in main. */ - a = { valueOf: function () { trap(main, 57, "success()"); } }; + a = { valueOf: function () { trap(main, 55, "success()"); } }; b = ""; eval(); a + b; diff --git a/js/src/jit-test/tests/jaeger/bug563000/trap-self-as-parent.js b/js/src/jit-test/tests/jaeger/bug563000/trap-self-as-parent.js index 16c9bd0a9403..151ab536d568 100644 --- a/js/src/jit-test/tests/jaeger/bug563000/trap-self-as-parent.js +++ b/js/src/jit-test/tests/jaeger/bug563000/trap-self-as-parent.js @@ -5,7 +5,7 @@ x = "notset"; function myparent(nested) { if (nested) { /* noop call in myparent */ - trap(myparent, 50, "success()"); + trap(myparent, 49, "success()"); } else { myparent(true); x = "failure"; diff --git a/js/src/jit-test/tests/jaeger/bug563000/trap-self-from-trap.js b/js/src/jit-test/tests/jaeger/bug563000/trap-self-from-trap.js index ef5b3f53a97b..b5c45f5add65 100644 --- a/js/src/jit-test/tests/jaeger/bug563000/trap-self-from-trap.js +++ b/js/src/jit-test/tests/jaeger/bug563000/trap-self-from-trap.js @@ -14,7 +14,7 @@ function myparent(nested) { } } /* JSOP_CALL to doNothing in myparent with nested = false. */ -trap(myparent, 35, "myparent(true)"); +trap(myparent, 34, "myparent(true)"); function success() { x = "success"; diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index a0706e7d9c51..b6c77382af24 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2281,7 +2281,7 @@ js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain) } JSFunction * -js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen) +js_NewFlatClosure(JSContext *cx, JSFunction *fun) { /* * Flat closures cannot yet be partial, that is, all upvars must be copied, diff --git a/js/src/jsfun.h b/js/src/jsfun.h index b41ef7be979b..0b442cc1472a 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -48,7 +48,6 @@ #include "jsatom.h" #include "jsscript.h" #include "jsstr.h" -#include "jsopcode.h" #include "gc/Barrier.h" @@ -322,7 +321,7 @@ extern JSFunction * JS_FASTCALL js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain); extern JSFunction * -js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen); +js_NewFlatClosure(JSContext *cx, JSFunction *fun); extern JSFunction * js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, JSNative native, diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 979836b4b508..06ce15384420 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -3405,8 +3405,6 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, case JSOP_INDEXBASE3: case JSOP_RESETBASE: case JSOP_RESETBASE0: - case JSOP_BLOCKCHAIN: - case JSOP_NULLBLOCKCHAIN: case JSOP_POPV: case JSOP_DEBUGGER: case JSOP_SETCALL: diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 09f18ad92143..a1565be9c067 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -83,7 +83,6 @@ #include "jsinferinlines.h" #include "jsinterpinlines.h" #include "jsobjinlines.h" -#include "jsopcodeinlines.h" #include "jsprobes.h" #include "jspropertycacheinlines.h" #include "jsscopeinlines.h" @@ -107,124 +106,6 @@ using namespace js; using namespace js::gc; using namespace js::types; -JSObject * -js::GetScopeChain(JSContext *cx) -{ - /* - * Note: we don't need to expand inline frames here, because frames are - * only inlined when the caller and callee share the same scope chain. - */ - StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE); - if (!fp) { - /* - * There is no code active on this context. In place of an actual - * scope chain, use the context's global object, which is set in - * js_InitFunctionAndObjectClasses, and which represents the default - * scope chain for the embedding. See also js_FindClassObject. - * - * For embeddings that use the inner and outer object hooks, the inner - * object represents the ultimate global object, with the outer object - * acting as a stand-in. - */ - JSObject *obj = cx->globalObject; - if (!obj) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); - return NULL; - } - - OBJ_TO_INNER_OBJECT(cx, obj); - return obj; - } - return GetScopeChain(cx, fp); -} - -/* - * This computes the blockChain by iterating through the bytecode - * of the current script until it reaches the PC. Each time it sees - * an ENTERBLOCK or LEAVEBLOCK instruction, it records the new - * blockChain. A faster variant of this function that doesn't - * require bytecode scanning appears below. - */ -JSObject * -js::GetBlockChain(JSContext *cx, StackFrame *fp) -{ - if (!fp->isScriptFrame()) - return NULL; - - jsbytecode *target = fp->pcQuadratic(cx->stack); - - JSScript *script = fp->script(); - jsbytecode *start = script->code; - - /* - * If the debugger asks for the scope chain at a pc where we are about to - * fix it up, advance target past the fixup. See bug 672804. - */ - JSOp op = JSOp(*target); - while (op == JSOP_NOP || op == JSOP_INDEXBASE || op == JSOP_INDEXBASE1 || - op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3 || - op == JSOP_BLOCKCHAIN || op == JSOP_NULLBLOCKCHAIN) - { - target += js_CodeSpec[op].length; - op = JSOp(*target); - } - JS_ASSERT(target >= start && target < start + script->length); - - JSObject *blockChain = NULL; - uintN indexBase = 0; - ptrdiff_t oplen; - for (jsbytecode *pc = start; pc < target; pc += oplen) { - JSOp op = JSOp(*pc); - const JSCodeSpec *cs = &js_CodeSpec[op]; - oplen = cs->length; - if (oplen < 0) - oplen = js_GetVariableBytecodeLength(pc); - - if (op == JSOP_INDEXBASE) - indexBase = GET_INDEXBASE(pc); - else if (op == JSOP_INDEXBASE1 || op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3) - indexBase = (op - JSOP_INDEXBASE1 + 1) << 16; - else if (op == JSOP_RESETBASE || op == JSOP_RESETBASE0) - indexBase = 0; - else if (op == JSOP_ENTERBLOCK) - blockChain = script->getObject(indexBase + GET_INDEX(pc)); - else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR) - blockChain = blockChain->getStaticBlockScopeChain(); - else if (op == JSOP_BLOCKCHAIN) - blockChain = script->getObject(indexBase + GET_INDEX(pc)); - else if (op == JSOP_NULLBLOCKCHAIN) - blockChain = NULL; - } - - return blockChain; -} - -/* - * This function computes the current blockChain, but only in - * the special case where a BLOCKCHAIN or NULLBLOCKCHAIN - * instruction appears immediately after the current PC. - * We ensure this happens for a few important ops like DEFFUN. - * |oplen| is the length of opcode at the current PC. - */ -JSObject * -js::GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen) -{ - /* Assume that we're in a script frame. */ - jsbytecode *pc = fp->pcQuadratic(cx->stack); - JS_ASSERT(JSOp(*pc) == op); - - pc += oplen; - op = JSOp(*pc); - - /* The fast paths assume no JSOP_RESETBASE/INDEXBASE noise. */ - if (op == JSOP_NULLBLOCKCHAIN) - return NULL; - if (op == JSOP_BLOCKCHAIN) - return fp->script()->getObject(GET_INDEX(pc)); - - return GetBlockChain(cx, fp); -} - /* * We can't determine in advance which local variables can live on the stack and * be freed when their dynamic scope ends, and which will be closed over and @@ -255,10 +136,10 @@ js::GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen) * This lazy cloning is implemented in GetScopeChain, which is also used in * some other cases --- entering 'with' blocks, for example. */ -static JSObject * -GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain) +JSObject * +js::GetScopeChain(JSContext *cx, StackFrame *fp) { - JSObject *sharedBlock = blockChain; + JSObject *sharedBlock = fp->maybeBlockChain(); if (!sharedBlock) { /* @@ -341,7 +222,7 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain) JSObject *newChild = innermostNewChild; for (;;) { JS_ASSERT(newChild->getProto() == sharedBlock); - sharedBlock = sharedBlock->getStaticBlockScopeChain(); + sharedBlock = sharedBlock->staticBlockScopeChain(); /* Sometimes limitBlock will be NULL, so check that first. */ if (sharedBlock == limitBlock || !sharedBlock) @@ -375,15 +256,34 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain) } JSObject * -js::GetScopeChain(JSContext *cx, StackFrame *fp) +js::GetScopeChain(JSContext *cx) { - return GetScopeChainFull(cx, fp, GetBlockChain(cx, fp)); -} + /* + * Note: we don't need to expand inline frames here, because frames are + * only inlined when the caller and callee share the same scope chain. + */ + StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE); + if (!fp) { + /* + * There is no code active on this context. In place of an actual + * scope chain, use the context's global object, which is set in + * js_InitFunctionAndObjectClasses, and which represents the default + * scope chain for the embedding. See also js_FindClassObject. + * + * For embeddings that use the inner and outer object hooks, the inner + * object represents the ultimate global object, with the outer object + * acting as a stand-in. + */ + JSObject *obj = cx->globalObject; + if (!obj) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); + return NULL; + } -JSObject * -js::GetScopeChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen) -{ - return GetScopeChainFull(cx, fp, GetBlockChainFast(cx, fp, op, oplen)); + OBJ_TO_INNER_OBJECT(cx, obj); + return obj; + } + return GetScopeChain(cx, fp); } /* Some objects (e.g., With) delegate 'this' to another object. */ @@ -1162,7 +1062,7 @@ js::ValueToId(JSContext *cx, const Value &v, jsid *idp) * of the with block with sp + stackIndex. */ static bool -EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen) +EnterWith(JSContext *cx, jsint stackIndex) { StackFrame *fp = cx->fp(); Value *sp = cx->regs().sp; @@ -1179,7 +1079,7 @@ EnterWith(JSContext *cx, jsint stackIndex, JSOp op, size_t oplen) sp[-1].setObject(*obj); } - JSObject *parent = GetScopeChainFast(cx, fp, op, oplen); + JSObject *parent = GetScopeChain(cx, fp); if (!parent) return JS_FALSE; @@ -1228,6 +1128,15 @@ js::UnwindScope(JSContext *cx, jsint stackDepth, JSBool normalUnwind) JS_ASSERT(cx->fp()->base() + stackDepth <= cx->regs().sp); StackFrame *fp = cx->fp(); + JSObject *obj = fp->maybeBlockChain(); + while (obj) { + JS_ASSERT(obj->isStaticBlock()); + if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth) + break; + obj = obj->staticBlockScopeChain(); + } + fp->setBlockChain(obj); + for (;;) { JSObject &scopeChain = fp->scopeChain(); if (!IsActiveWithOrBlock(cx, scopeChain, stackDepth)) @@ -1949,13 +1858,14 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) ADD_EMPTY_CASE(JSOP_NOP) ADD_EMPTY_CASE(JSOP_UNUSED0) ADD_EMPTY_CASE(JSOP_UNUSED1) +ADD_EMPTY_CASE(JSOP_UNUSED2) +ADD_EMPTY_CASE(JSOP_UNUSED3) ADD_EMPTY_CASE(JSOP_CONDSWITCH) ADD_EMPTY_CASE(JSOP_TRY) #if JS_HAS_XML_SUPPORT ADD_EMPTY_CASE(JSOP_STARTXML) ADD_EMPTY_CASE(JSOP_STARTXMLEXPR) #endif -ADD_EMPTY_CASE(JSOP_NULLBLOCKCHAIN) ADD_EMPTY_CASE(JSOP_LOOPHEAD) END_EMPTY_CASES @@ -2001,9 +1911,6 @@ check_backedge: BEGIN_CASE(JSOP_LINENO) END_CASE(JSOP_LINENO) -BEGIN_CASE(JSOP_BLOCKCHAIN) -END_CASE(JSOP_BLOCKCHAIN) - BEGIN_CASE(JSOP_UNDEFINED) PUSH_UNDEFINED(); END_CASE(JSOP_UNDEFINED) @@ -2017,7 +1924,7 @@ BEGIN_CASE(JSOP_POPN) regs.sp -= GET_UINT16(regs.pc); #ifdef DEBUG JS_ASSERT(regs.fp()->base() <= regs.sp); - JSObject *obj = GetBlockChain(cx, regs.fp()); + JSObject *obj = regs.fp()->maybeBlockChain(); JS_ASSERT_IF(obj, OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj) <= (size_t) (regs.sp - regs.fp()->base())); @@ -2040,7 +1947,7 @@ BEGIN_CASE(JSOP_POPV) END_CASE(JSOP_POPV) BEGIN_CASE(JSOP_ENTERWITH) - if (!EnterWith(cx, -1, JSOP_ENTERWITH, JSOP_ENTERWITH_LENGTH)) + if (!EnterWith(cx, -1)) goto error; /* @@ -2078,6 +1985,7 @@ BEGIN_CASE(JSOP_STOP) if (entryFrame != regs.fp()) inline_return: { + JS_ASSERT(!regs.fp()->hasBlockChain()); JS_ASSERT(!IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0)); if (cx->compartment->debugMode()) @@ -4064,7 +3972,7 @@ BEGIN_CASE(JSOP_DEFFUN) } else { JS_ASSERT(!fun->isFlatClosure()); - obj2 = GetScopeChainFast(cx, regs.fp(), JSOP_DEFFUN, JSOP_DEFFUN_LENGTH); + obj2 = GetScopeChain(cx, regs.fp()); if (!obj2) goto error; } @@ -4163,7 +4071,7 @@ BEGIN_CASE(JSOP_DEFFUN_FC) JSFunction *fun; LOAD_FUNCTION(0); - JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_DEFFUN_FC, JSOP_DEFFUN_FC_LENGTH); + JSObject *obj = js_NewFlatClosure(cx, fun); if (!obj) goto error; @@ -4207,8 +4115,7 @@ BEGIN_CASE(JSOP_DEFLOCALFUN) if (fun->isNullClosure()) { parent = ®s.fp()->scopeChain(); } else { - parent = GetScopeChainFast(cx, regs.fp(), JSOP_DEFLOCALFUN, - JSOP_DEFLOCALFUN_LENGTH); + parent = GetScopeChain(cx, regs.fp()); if (!parent) goto error; } @@ -4228,7 +4135,7 @@ BEGIN_CASE(JSOP_DEFLOCALFUN_FC) JSFunction *fun; LOAD_FUNCTION(SLOTNO_LEN); - JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH); + JSObject *obj = js_NewFlatClosure(cx, fun); if (!obj) goto error; @@ -4251,7 +4158,7 @@ BEGIN_CASE(JSOP_LAMBDA) parent = ®s.fp()->scopeChain(); if (fun->joinable()) { - jsbytecode *pc2 = AdvanceOverBlockchainOp(regs.pc + JSOP_LAMBDA_LENGTH); + jsbytecode *pc2 = regs.pc + JSOP_LAMBDA_LENGTH; JSOp op2 = JSOp(*pc2); /* @@ -4266,7 +4173,7 @@ BEGIN_CASE(JSOP_LAMBDA) JSObject *obj2 = &lref.toObject(); JS_ASSERT(obj2->isObject()); #endif - JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(pc2 - regs.pc))); + JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(JSOP_LAMBDA_LENGTH))); break; } @@ -4277,7 +4184,7 @@ BEGIN_CASE(JSOP_LAMBDA) #endif const Value &lref = regs.sp[-1]; if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) { - JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(pc2 - regs.pc))); + JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(JSOP_LAMBDA_LENGTH))); break; } } else if (op2 == JSOP_CALL) { @@ -4315,7 +4222,7 @@ BEGIN_CASE(JSOP_LAMBDA) } } } else { - parent = GetScopeChainFast(cx, regs.fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH); + parent = GetScopeChain(cx, regs.fp()); if (!parent) goto error; } @@ -4337,7 +4244,7 @@ BEGIN_CASE(JSOP_LAMBDA_FC) JSFunction *fun; LOAD_FUNCTION(0); - JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH); + JSObject *obj = js_NewFlatClosure(cx, fun); if (!obj) goto error; JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto()); @@ -5079,7 +4986,7 @@ BEGIN_CASE(JSOP_ENDFILTER) * temporaries. */ JS_ASSERT(IsXML(regs.sp[-1])); - if (!EnterWith(cx, -2, JSOP_ENDFILTER, JSOP_ENDFILTER_LENGTH)) + if (!EnterWith(cx, -2)) goto error; regs.sp--; len = GET_JUMP_OFFSET(regs.pc); @@ -5214,6 +5121,8 @@ BEGIN_CASE(JSOP_ENTERBLOCK) regs.sp = vp; #ifdef DEBUG + JS_ASSERT(regs.fp()->maybeBlockChain() == obj->staticBlockScopeChain()); + /* * The young end of fp->scopeChain may omit blocks if we haven't closed * over them, but if there are any closure blocks on fp->scopeChain, they'd @@ -5233,17 +5142,17 @@ BEGIN_CASE(JSOP_ENTERBLOCK) JS_ASSERT(parent); } #endif + + regs.fp()->setBlockChain(obj); } END_CASE(JSOP_ENTERBLOCK) BEGIN_CASE(JSOP_LEAVEBLOCKEXPR) BEGIN_CASE(JSOP_LEAVEBLOCK) { - JSObject *blockChain; - LOAD_OBJECT(UINT16_LEN, blockChain); #ifdef DEBUG - JS_ASSERT(blockChain->isStaticBlock()); - uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain); + JS_ASSERT(regs.fp()->blockChain().isStaticBlock()); + uintN blockDepth = OBJ_BLOCK_DEPTH(cx, ®s.fp()->blockChain()); JS_ASSERT(blockDepth <= StackDepth(script)); #endif /* @@ -5252,12 +5161,15 @@ BEGIN_CASE(JSOP_LEAVEBLOCK) * the stack into the clone, and pop it off the chain. */ JSObject &obj = regs.fp()->scopeChain(); - if (obj.getProto() == blockChain) { + if (obj.getProto() == ®s.fp()->blockChain()) { JS_ASSERT(obj.isClonedBlock()); if (!js_PutBlockObject(cx, JS_TRUE)) goto error; } + /* Pop the block chain, too. */ + regs.fp()->setBlockChain(regs.fp()->blockChain().staticBlockScopeChain()); + /* Move the result of the expression to the new topmost stack slot. */ Value *vp = NULL; /* silence GCC warnings */ if (op == JSOP_LEAVEBLOCKEXPR) @@ -5478,6 +5390,8 @@ END_CASE(JSOP_ARRAYPUSH) switch (tn->kind) { case JSTRY_CATCH: + JS_ASSERT(*regs.pc == JSOP_ENTERBLOCK); + #if JS_HAS_GENERATORS /* Catch cannot intercept the closing of a generator. */ if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING))) @@ -5565,9 +5479,10 @@ END_CASE(JSOP_ARRAYPUSH) * frame pc. */ JS_ASSERT(entryFrame == regs.fp()); - - JS_ASSERT_IF(!regs.fp()->isGeneratorFrame(), - !IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0)); + if (!regs.fp()->isGeneratorFrame()) { + JS_ASSERT(!IsActiveWithOrBlock(cx, regs.fp()->scopeChain(), 0)); + JS_ASSERT(!regs.fp()->hasBlockChain()); + } #ifdef JS_METHODJIT /* diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 270525702482..fe5fb2245c7f 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -51,15 +51,6 @@ namespace js { -extern JSObject * -GetBlockChain(JSContext *cx, StackFrame *fp); - -extern JSObject * -GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen); - -extern JSObject * -GetScopeChain(JSContext *cx); - /* * Refresh and return fp->scopeChain. It may be stale if block scopes are * active but not yet reflected by objects in the scope chain. If a block @@ -67,11 +58,12 @@ GetScopeChain(JSContext *cx); * dynamically scoped construct, then compile-time block scope at fp->blocks * must reflect at runtime. */ -extern JSObject * -GetScopeChain(JSContext *cx, StackFrame *fp); extern JSObject * -GetScopeChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen); +GetScopeChain(JSContext *cx); + +extern JSObject * +GetScopeChain(JSContext *cx, StackFrame *fp); /* * ScriptPrologue/ScriptEpilogue must be called in pairs. ScriptPrologue diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 5ec91c632a4f..11837e87669a 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1318,12 +1318,14 @@ DirectEval(JSContext *cx, const CallArgs &args) AutoFunctionCallProbe callProbe(cx, args.callee().toFunction(), caller->script()); - JSObject *scopeChain = - GetScopeChainFast(cx, caller, JSOP_EVAL, JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH); + JSObject *scopeChain = GetScopeChain(cx, caller); + if (!scopeChain) + return false; - return scopeChain && - WarnOnTooManyArgs(cx, args) && - EvalKernel(cx, args, DIRECT_EVAL, caller, *scopeChain); + if (!WarnOnTooManyArgs(cx, args)) + return false; + + return EvalKernel(cx, args, DIRECT_EVAL, caller, *scopeChain); } bool @@ -4134,7 +4136,7 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp) if (xdr->mode == JSXDR_ENCODE) { obj = *objp; - parent = obj->getStaticBlockScopeChain(); + parent = obj->staticBlockScopeChain(); parentId = JSScript::isValidOffset(xdr->script->objectsOffset) ? FindObjectIndex(xdr->script->objects(), parent) : NO_PARENT_INDEX; @@ -7458,6 +7460,7 @@ js_DumpStackFrame(JSContext *cx, StackFrame *start) fprintf(stderr, "\n"); } MaybeDumpObject("argsobj", fp->maybeArgsObj()); + MaybeDumpObject("blockChain", fp->maybeBlockChain()); if (!fp->isDummyFrame()) { MaybeDumpValue("this", fp->thisValue()); fprintf(stderr, " rval: "); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index fad800f4e6fd..55ab602c490b 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -908,7 +908,7 @@ struct JSObject : js::gc::Cell * on scope chains but mirror their structure, and can have a NULL * scope chain. */ - inline JSObject *getStaticBlockScopeChain() const; + inline JSObject *staticBlockScopeChain() const; inline void setStaticBlockScopeChain(JSObject *obj); /* Common fixed slot for the scope chain of internal scope objects. */ diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index e7250921674b..9781a92f67f9 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -324,7 +324,7 @@ JSObject::scopeChain() const } inline JSObject * -JSObject::getStaticBlockScopeChain() const +JSObject::staticBlockScopeChain() const { JS_ASSERT(isStaticBlock()); return getFixedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull(); diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 2947d509d07d..03ec9628ed0b 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -2902,14 +2902,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) todo = -2; pc2 = pc + oplen; - /* Skip a block chain annotation if one appears here. */ - if (*pc2 == JSOP_NOP) { - if (pc2[JSOP_NOP_LENGTH] == JSOP_NULLBLOCKCHAIN) - pc2 += JSOP_NOP_LENGTH + JSOP_NULLBLOCKCHAIN_LENGTH; - else if (pc2[JSOP_NOP_LENGTH] == JSOP_BLOCKCHAIN) - pc2 += JSOP_NOP_LENGTH + JSOP_BLOCKCHAIN_LENGTH; - } - if (*pc2 == JSOP_NOP) { sn = js_GetSrcNote(jp->script, pc2); if (sn) { @@ -4409,12 +4401,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) * arrange to advance over the call to this lambda. */ pc += len; - if (*pc == JSOP_BLOCKCHAIN) { - pc += JSOP_BLOCKCHAIN_LENGTH; - } else { - LOCAL_ASSERT(*pc == JSOP_NULLBLOCKCHAIN); - pc += JSOP_NULLBLOCKCHAIN_LENGTH; - } LOCAL_ASSERT(*pc == JSOP_UNDEFINED); pc += JSOP_UNDEFINED_LENGTH; LOCAL_ASSERT(*pc == JSOP_CALL); diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 7e0cfbe1363d..01248a20cec1 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -232,7 +232,7 @@ OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 2, JOF_BYTE) /* Call a function as a constructor; operand is argc. */ OPDEF(JSOP_NEW, 82, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) -OPDEF(JSOP_UNUSED1, 83, "unused1", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED0, 83, "unused1", NULL, 1, 0, 0, 0, JOF_BYTE) /* Fast get/set ops for function arguments and local variables. */ OPDEF(JSOP_GETARG, 84, "getarg", NULL, 3, 0, 1, 19, JOF_QARG |JOF_NAME) @@ -271,7 +271,7 @@ OPDEF(JSOP_DECLOCAL, 102,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL| OPDEF(JSOP_LOCALINC, 103,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3) OPDEF(JSOP_LOCALDEC, 104,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3) -OPDEF(JSOP_UNUSED0, 105,"unused0", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED1, 105,"unused0", NULL, 1, 0, 0, 0, JOF_BYTE) /* The argument is the offset to the next statement and is used by IonMonkey. */ OPDEF(JSOP_LABEL, 106,"label", NULL, 3, 0, 0, 0, JOF_JUMP) @@ -440,15 +440,8 @@ OPDEF(JSOP_DELDESC, 183,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|J OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_CALLOP|JOF_TMPSLOT3) -/* - * These opcodes contain a reference to the current blockChain object. - * They are emitted directly after instructions, such as DEFFUN, that need fast access to - * the blockChain. The special NULLBLOCKCHAIN is needed because the JOF_OBJECT - * does not permit NULL object references, since it stores an index into a table of - * objects. - */ -OPDEF(JSOP_BLOCKCHAIN, 185,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT) -OPDEF(JSOP_NULLBLOCKCHAIN,186,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED2, 185,"unused1", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_UNUSED3, 186,"unused2", NULL, 1, 0, 0, 0, JOF_BYTE) /* * Opcode to hold 24-bit immediate integer operands. @@ -496,7 +489,7 @@ OPDEF(JSOP_TYPEOFEXPR, 197,"typeofexpr", NULL, 1, 1, 1, 15, JOF_BYTE|J * Block-local scope support. */ OPDEF(JSOP_ENTERBLOCK, 198,"enterblock", NULL, 3, 0, -1, 0, JOF_OBJECT) -OPDEF(JSOP_LEAVEBLOCK, 199,"leaveblock", NULL, 5, -1, 0, 0, JOF_UINT16) +OPDEF(JSOP_LEAVEBLOCK, 199,"leaveblock", NULL, 3, -1, 0, 0, JOF_UINT16) /* Jump to target if top of stack value isn't callable. */ OPDEF(JSOP_IFCANTCALLTOP, 200,"ifcantcalltop",NULL, 3, 1, 1, 0, JOF_JUMP|JOF_DETECTING) @@ -525,7 +518,7 @@ OPDEF(JSOP_ENUMCONSTELEM, 206,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|J * Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals, * which must be moved down when the block pops. */ -OPDEF(JSOP_LEAVEBLOCKEXPR,207,"leaveblockexpr",NULL, 5, -1, 1, 3, JOF_UINT16) +OPDEF(JSOP_LEAVEBLOCKEXPR,207,"leaveblockexpr",NULL, 3, -1, 1, 3, JOF_UINT16) \ /* * Optimize atom segments 1-3. These must be followed by JSOP_RESETBASE0 after diff --git a/js/src/jsopcodeinlines.h b/js/src/jsopcodeinlines.h index aeb03506648f..6a17d924db85 100644 --- a/js/src/jsopcodeinlines.h +++ b/js/src/jsopcodeinlines.h @@ -57,20 +57,6 @@ class BytecodeRange { jsbytecode *pc, *end; }; -/* - * Warning: this does not skip JSOP_RESETBASE* or JSOP_INDEXBASE* ops, so it is - * useful only when checking for optimization opportunities. - */ -JS_ALWAYS_INLINE jsbytecode * -AdvanceOverBlockchainOp(jsbytecode *pc) -{ - if (*pc == JSOP_NULLBLOCKCHAIN) - return pc + JSOP_NULLBLOCKCHAIN_LENGTH; - if (*pc == JSOP_BLOCKCHAIN) - return pc + JSOP_BLOCKCHAIN_LENGTH; - return pc; -} - class SrcNoteLineScanner { /* offset of the current JSOp in the bytecode */ diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 28a293edb5c6..0d5d94896c73 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -2470,12 +2470,6 @@ mjit::Compiler::generateMethod() frame.popn(2); END_CASE(JSOP_ENUMELEM) - BEGIN_CASE(JSOP_BLOCKCHAIN) - END_CASE(JSOP_BLOCKCHAIN) - - BEGIN_CASE(JSOP_NULLBLOCKCHAIN) - END_CASE(JSOP_NULLBLOCKCHAIN) - BEGIN_CASE(JSOP_CONDSWITCH) /* No-op for the decompiler. */ END_CASE(JSOP_CONDSWITCH) @@ -2544,7 +2538,7 @@ mjit::Compiler::generateMethod() jsbytecode *pc2 = NULL; if (fun->joinable()) { - pc2 = AdvanceOverBlockchainOp(PC + JSOP_LAMBDA_LENGTH); + pc2 = PC + JSOP_LAMBDA_LENGTH; JSOp next = JSOp(*pc2); if (next == JSOP_INITMETHOD) { @@ -2568,9 +2562,6 @@ mjit::Compiler::generateMethod() prepareStubCall(Uses(uses)); masm.move(ImmPtr(fun), Registers::ArgReg1); - if (stub != stubs::Lambda) - masm.storePtr(ImmPtr(pc2), FrameAddress(offsetof(VMFrame, scratch))); - INLINE_STUBCALL(stub, REJOIN_PUSH_OBJECT); frame.takeReg(Registers::ReturnReg); @@ -7156,9 +7147,7 @@ mjit::Compiler::leaveBlock() * PutBlockObject, and do away with the muckiness in PutBlockObject. */ uint32_t n = js_GetVariableStackUses(JSOP_LEAVEBLOCK, PC); - JSObject *obj = script->getObject(fullAtomIndex(PC + UINT16_LEN)); prepareStubCall(Uses(n)); - masm.move(ImmPtr(obj), Registers::ArgReg1); INLINE_STUBCALL(stubs::LeaveBlock, REJOIN_NONE); frame.leaveBlock(n); } diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index 920b4b0d05bd..2ef490701329 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -177,6 +177,7 @@ InlineReturn(VMFrame &f) { JS_ASSERT(f.fp() != f.entryfp); JS_ASSERT(!IsActiveWithOrBlock(f.cx, f.fp()->scopeChain(), 0)); + JS_ASSERT(!f.fp()->hasBlockChain()); f.cx->stack.popInlineFrame(f.regs); DebugOnly op = JSOp(*f.regs.pc); @@ -638,6 +639,7 @@ js_InternalThrow(VMFrame &f) cx->clearPendingException(); cx->regs().sp++; cx->regs().pc = pc + JSOP_ENTERBLOCK_LENGTH + JSOP_EXCEPTION_LENGTH; + cx->regs().fp()->setBlockChain(obj); } *f.oldregs = f.regs; diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 086d41214737..fd448bb90785 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -673,7 +673,7 @@ stubs::DefFun(VMFrame &f, JSFunction *fun) } else { JS_ASSERT(!fun->isFlatClosure()); - obj2 = GetScopeChainFast(cx, fp, JSOP_DEFFUN, JSOP_DEFFUN_LENGTH); + obj2 = GetScopeChain(cx, fp); if (!obj2) THROW(); } @@ -1333,8 +1333,7 @@ stubs::DefLocalFun(VMFrame &f, JSFunction *fun) if (fun->isNullClosure()) { parent = &f.fp()->scopeChain(); } else { - parent = GetScopeChainFast(f.cx, f.fp(), JSOP_DEFLOCALFUN, - JSOP_DEFLOCALFUN_LENGTH); + parent = GetScopeChain(f.cx, f.fp()); if (!parent) THROWV(NULL); } @@ -1350,7 +1349,7 @@ stubs::DefLocalFun(VMFrame &f, JSFunction *fun) JSObject * JS_FASTCALL stubs::DefLocalFun_FC(VMFrame &f, JSFunction *fun) { - JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH); + JSObject *obj = js_NewFlatClosure(f.cx, fun); if (!obj) THROWV(NULL); return obj; @@ -1376,8 +1375,8 @@ stubs::RegExp(VMFrame &f, JSObject *regex) JSObject * JS_FASTCALL stubs::LambdaJoinableForInit(VMFrame &f, JSFunction *fun) { - DebugOnly nextpc = (jsbytecode *) f.scratch; JS_ASSERT(fun->joinable()); + DebugOnly nextpc = f.regs.pc + JSOP_LAMBDA_LENGTH; JS_ASSERT(fun->methodAtom() == f.script()->getAtom(GET_SLOTNO(nextpc))); return fun; } @@ -1386,9 +1385,9 @@ JSObject * JS_FASTCALL stubs::LambdaJoinableForSet(VMFrame &f, JSFunction *fun) { JS_ASSERT(fun->joinable()); - DebugOnly nextpc = (jsbytecode *) f.scratch; const Value &lref = f.regs.sp[-1]; if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) { + DebugOnly nextpc = f.regs.pc + JSOP_LAMBDA_LENGTH; JS_ASSERT(fun->methodAtom() == f.script()->getAtom(GET_SLOTNO(nextpc))); return fun; } @@ -1399,7 +1398,6 @@ JSObject * JS_FASTCALL stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun) { JS_ASSERT(fun->joinable()); - jsbytecode *nextpc = (jsbytecode *) f.scratch; /* * Array.prototype.sort and String.prototype.replace are optimized as if @@ -1407,7 +1405,7 @@ stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun) * object fun, therefore we don't need to clone that compiler-created * function object for identity/mutation reasons. */ - int iargc = GET_ARGC(nextpc); + int iargc = GET_ARGC(f.regs.pc + JSOP_LAMBDA_LENGTH); /* * Note that we have not yet pushed fun as the final argument, so @@ -1444,7 +1442,7 @@ stubs::Lambda(VMFrame &f, JSFunction *fun) if (fun->isNullClosure()) { parent = &f.fp()->scopeChain(); } else { - parent = GetScopeChainFast(f.cx, f.fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH); + parent = GetScopeChain(f.cx, f.fp()); if (!parent) THROWV(NULL); } @@ -1763,7 +1761,7 @@ stubs::Throw(VMFrame &f) JSObject * JS_FASTCALL stubs::FlatLambda(VMFrame &f, JSFunction *fun) { - JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH); + JSObject *obj = js_NewFlatClosure(f.cx, fun); if (!obj) THROWV(NULL); return obj; @@ -1819,9 +1817,7 @@ void JS_FASTCALL stubs::EnterBlock(VMFrame &f, JSObject *obj) { FrameRegs ®s = f.regs; -#ifdef DEBUG StackFrame *fp = f.fp(); -#endif JS_ASSERT(!f.regs.inlined()); JS_ASSERT(obj->isStaticBlock()); @@ -1834,6 +1830,7 @@ stubs::EnterBlock(VMFrame &f, JSObject *obj) #ifdef DEBUG JSContext *cx = f.cx; + JS_ASSERT(fp->maybeBlockChain() == obj->staticBlockScopeChain()); /* * The young end of fp->scopeChain() may omit blocks if we haven't closed @@ -1854,17 +1851,19 @@ stubs::EnterBlock(VMFrame &f, JSObject *obj) JS_ASSERT(parent); } #endif + + fp->setBlockChain(obj); } void JS_FASTCALL -stubs::LeaveBlock(VMFrame &f, JSObject *blockChain) +stubs::LeaveBlock(VMFrame &f) { JSContext *cx = f.cx; StackFrame *fp = f.fp(); #ifdef DEBUG - JS_ASSERT(blockChain->isStaticBlock()); - uintN blockDepth = OBJ_BLOCK_DEPTH(cx, blockChain); + JS_ASSERT(fp->blockChain().isBlock()); + uintN blockDepth = OBJ_BLOCK_DEPTH(cx, &fp->blockChain()); JS_ASSERT(blockDepth <= StackDepth(fp->script())); #endif @@ -1873,12 +1872,15 @@ stubs::LeaveBlock(VMFrame &f, JSObject *blockChain) * cloned onto fp->scopeChain(), clear its private data, move its locals from * the stack into the clone, and pop it off the chain. */ - JSObject *obj = &fp->scopeChain(); - if (obj->getProto() == blockChain) { - JS_ASSERT(obj->isBlock()); + JSObject &obj = fp->scopeChain(); + JSObject &blockChain = fp->blockChain(); + if (obj.getProto() == &blockChain) { + JS_ASSERT(obj.isBlock()); if (!js_PutBlockObject(cx, JS_TRUE)) THROW(); } + + fp->setBlockChain(blockChain.staticBlockScopeChain()); } void * JS_FASTCALL diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index ba3f632ab720..49def17b906e 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -154,7 +154,7 @@ JSObject * JS_FASTCALL LambdaJoinableForNull(VMFrame &f, JSFunction *fun); JSObject * JS_FASTCALL FlatLambda(VMFrame &f, JSFunction *fun); void JS_FASTCALL Arguments(VMFrame &f); void JS_FASTCALL EnterBlock(VMFrame &f, JSObject *obj); -void JS_FASTCALL LeaveBlock(VMFrame &f, JSObject *blockChain); +void JS_FASTCALL LeaveBlock(VMFrame &f); JSBool JS_FASTCALL LessThan(VMFrame &f); JSBool JS_FASTCALL LessEqual(VMFrame &f); diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 3e6fc2355cd2..f15c42769504 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -154,12 +154,14 @@ StackFrame::initCallFrame(JSContext *cx, JSFunction &callee, JS_ASSERT(script == callee.toFunction()->script()); /* Initialize stack frame members. */ - flags_ = FUNCTION | HAS_PREVPC | HAS_SCOPECHAIN | flagsArg; + flags_ = FUNCTION | HAS_PREVPC | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | flagsArg; exec.fun = &callee; args.nactual = nactual; scopeChain_ = callee.toFunction()->environment(); ncode_ = NULL; initPrev(cx); + blockChain_= NULL; + JS_ASSERT(!hasBlockChain()); JS_ASSERT(!hasHookData()); JS_ASSERT(annotation() == NULL); JS_ASSERT(!hasCallObj()); diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 79ac53b0ba75..7ae495f48c3b 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -78,7 +78,7 @@ StackFrame::initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs * script in the context of another frame and the frame type is determined * by the context. */ - flags_ = type | HAS_SCOPECHAIN | HAS_PREVPC; + flags_ = type | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | HAS_PREVPC; if (!(flags_ & GLOBAL)) flags_ |= (prev->flags_ & (FUNCTION | GLOBAL)); @@ -102,6 +102,7 @@ StackFrame::initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs prev_ = prev; prevpc_ = regs ? regs->pc : (jsbytecode *)0xbad; prevInline_ = regs ? regs->inlined() : NULL; + blockChain_ = NULL; #ifdef DEBUG ncode_ = (void *)0xbad; diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index dc655b8ee78b..407e81be821e 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -345,10 +345,11 @@ class StackFrame HAS_RVAL = 0x20000, /* frame has rval_ set */ HAS_SCOPECHAIN = 0x40000, /* frame has scopeChain_ set */ HAS_PREVPC = 0x80000, /* frame has prevpc_ and prevInline_ set */ + HAS_BLOCKCHAIN = 0x100000, /* frame has blockChain_ set */ /* Method JIT state */ - DOWN_FRAMES_EXPANDED = 0x100000, /* inlining in down frames has been expanded */ - LOWERED_CALL_APPLY = 0x200000 /* Pushed by a lowered call/apply */ + DOWN_FRAMES_EXPANDED = 0x400000, /* inlining in down frames has been expanded */ + LOWERED_CALL_APPLY = 0x800000 /* Pushed by a lowered call/apply */ }; private: @@ -368,6 +369,7 @@ class StackFrame /* Lazily initialized */ Value rval_; /* return value of the frame */ + JSObject *blockChain_; /* innermost let block */ jsbytecode *prevpc_; /* pc of previous frame*/ JSInlinedSite *prevInline_; /* inlined site in previous frame */ void *hookData_; /* closure returned by call hook */ @@ -840,6 +842,26 @@ class StackFrame inline void setScopeChainNoCallObj(JSObject &obj); inline void setScopeChainWithOwnCallObj(CallObject &obj); + /* Block chain */ + + bool hasBlockChain() const { + return (flags_ & HAS_BLOCKCHAIN) && blockChain_; + } + + JSObject *maybeBlockChain() { + return (flags_ & HAS_BLOCKCHAIN) ? blockChain_ : NULL; + } + + JSObject &blockChain() const { + JS_ASSERT(hasBlockChain()); + return *blockChain_; + } + + void setBlockChain(JSObject *obj) { + flags_ |= HAS_BLOCKCHAIN; + blockChain_ = obj; + } + /* * Prologue for function frames: make a call object for heavyweight * functions, and maintain type nesting invariants. diff --git a/js/src/vm/StackSpace.h b/js/src/vm/StackSpace.h index 386cbdb62b0f..1a55cf68e95c 100644 --- a/js/src/vm/StackSpace.h +++ b/js/src/vm/StackSpace.h @@ -55,7 +55,7 @@ class GeneratorFrameGuard; enum InitialFrameFlags { INITIAL_NONE = 0, INITIAL_CONSTRUCT = 0x80, /* == StackFrame::CONSTRUCTING, asserted in Stack.h */ - INITIAL_LOWERED = 0x200000 /* == StackFrame::LOWERED_CALL_APPLY, asserted in Stack.h */ + INITIAL_LOWERED = 0x800000 /* == StackFrame::LOWERED_CALL_APPLY, asserted in Stack.h */ }; enum ExecuteType { From 7862c914b893aaee3b93c913b64878c09624b203 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 7 Oct 2011 12:02:50 -0700 Subject: [PATCH 15/26] Bug Bug 692274, part 4 - Rewrite parsing, emitting and decompiling of let to fix scoping properly (r=jorendorff) --- js/public/Vector.h | 17 + js/src/frontend/BytecodeEmitter.cpp | 915 ++++++++++++------ js/src/frontend/BytecodeEmitter.h | 33 +- js/src/frontend/ParseNode.h | 5 + js/src/frontend/Parser.cpp | 720 +++++++------- js/src/frontend/Parser.h | 6 +- js/src/jit-test/tests/basic/bug657975.js | 2 +- js/src/jit-test/tests/basic/testBug666292.js | 2 +- js/src/jit-test/tests/basic/testBug683470.js | 2 +- .../jit-test/tests/basic/testBug692274-1.js | 6 + .../jit-test/tests/basic/testBug692274-2.js | 7 + .../jit-test/tests/basic/testBug692274-3.js | 16 + .../jit-test/tests/basic/testBug692274-4.js | 4 + js/src/jit-test/tests/basic/testBug703857.js | 12 + js/src/jit-test/tests/basic/testBug709633.js | 9 + js/src/jit-test/tests/basic/testLet.js | 342 +++++++ js/src/jsanalyze.cpp | 6 +- js/src/jsanalyze.h | 7 +- js/src/jsinfer.cpp | 24 +- js/src/jsinterp.cpp | 57 +- js/src/jsobj.cpp | 29 +- js/src/jsobj.h | 2 +- js/src/jsopcode.cpp | 569 ++++++++--- js/src/jsopcode.h | 53 +- js/src/jsopcode.tbl | 10 +- js/src/jsreflect.cpp | 58 +- js/src/jsxdrapi.h | 2 +- js/src/methodjit/Compiler.cpp | 8 +- js/src/methodjit/LoopState.cpp | 4 +- js/src/methodjit/StubCalls.cpp | 15 +- .../tests/js1_8/regress/regress-465567-01.js | 2 +- .../tests/js1_8_5/extensions/reflect-parse.js | 2 +- .../js1_8_5/extensions/regress-672804-1.js | 2 +- .../js1_8_5/extensions/regress-672804-3.js | 2 +- 34 files changed, 2048 insertions(+), 902 deletions(-) create mode 100644 js/src/jit-test/tests/basic/testBug692274-1.js create mode 100644 js/src/jit-test/tests/basic/testBug692274-2.js create mode 100644 js/src/jit-test/tests/basic/testBug692274-3.js create mode 100644 js/src/jit-test/tests/basic/testBug692274-4.js create mode 100644 js/src/jit-test/tests/basic/testBug703857.js create mode 100644 js/src/jit-test/tests/basic/testBug709633.js create mode 100644 js/src/jit-test/tests/basic/testLet.js diff --git a/js/public/Vector.h b/js/public/Vector.h index 96e455e93101..cbfedab7223d 100644 --- a/js/public/Vector.h +++ b/js/public/Vector.h @@ -380,6 +380,23 @@ class Vector : private AllocPolicy return *(end() - 1); } + class Range { + friend class Vector; + T *cur, *end; + Range(T *cur, T *end) : cur(cur), end(end) {} + public: + Range() {} + bool empty() const { return cur == end; } + size_t remain() const { return end - cur; } + T &front() const { return *cur; } + void popFront() { JS_ASSERT(!empty()); ++cur; } + T popCopyFront() { JS_ASSERT(!empty()); return *cur++; } + }; + + Range all() { + return Range(begin(), end()); + } + /* mutators */ /* If reserve(length() + N) succeeds, the N next appends are guaranteed to succeed. */ diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index b7eeb2803f87..ad2dfd5c0b81 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -190,54 +190,53 @@ EmitCheck(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t delta) return offset; } +static JSObject * +CurrentBlock(BytecodeEmitter *bce) +{ + JS_ASSERT(bce->topStmt->type == STMT_BLOCK || bce->topStmt->type == STMT_SWITCH); + JS_ASSERT(bce->topStmt->blockObj->isStaticBlock()); + return bce->topStmt->blockObj; +} + static void UpdateDepth(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t target) { - jsbytecode *pc; - JSOp op; - const JSCodeSpec *cs; - uintN nuses; - intN ndefs; + jsbytecode *pc = bce->code(target); + JSOp op = (JSOp) *pc; + const JSCodeSpec *cs = &js_CodeSpec[op]; - pc = bce->code(target); - op = (JSOp) *pc; - cs = &js_CodeSpec[op]; - if ((cs->format & JOF_TMPSLOT_MASK)) { + + if (cs->format & JOF_TMPSLOT_MASK) { + /* + * An opcode may temporarily consume stack space during execution. + * Account for this in maxStackDepth separately from uses/defs here. + */ uintN depth = (uintN) bce->stackDepth + ((cs->format & JOF_TMPSLOT_MASK) >> JOF_TMPSLOT_SHIFT); if (depth > bce->maxStackDepth) bce->maxStackDepth = depth; } - nuses = js_GetStackUses(cs, op, pc); + /* + * Specially handle any case that would call js_GetIndexFromBytecode since + * it requires a well-formed script. This allows us to safely pass NULL as + * the 'script' parameter. + */ + intN nuses, ndefs; + if (op == JSOP_ENTERBLOCK) { + nuses = 0; + ndefs = OBJ_BLOCK_COUNT(cx, CurrentBlock(bce)); + } else if (op == JSOP_ENTERLET0) { + nuses = ndefs = OBJ_BLOCK_COUNT(cx, CurrentBlock(bce)); + } else if (op == JSOP_ENTERLET1) { + nuses = ndefs = OBJ_BLOCK_COUNT(cx, CurrentBlock(bce)) + 1; + } else { + nuses = StackUses(NULL, pc); + ndefs = StackDefs(NULL, pc); + } + bce->stackDepth -= nuses; JS_ASSERT(bce->stackDepth >= 0); - if (bce->stackDepth < 0) { - char numBuf[12]; - TokenStream *ts; - - JS_snprintf(numBuf, sizeof numBuf, "%d", target); - ts = &bce->parser->tokenStream; - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, - js_GetErrorMessage, NULL, - JSMSG_STACK_UNDERFLOW, - ts->getFilename() ? ts->getFilename() : "stdin", - numBuf); - } - ndefs = cs->ndefs; - if (ndefs < 0) { - JSObject *blockObj; - - /* We just executed IndexParsedObject */ - JS_ASSERT(op == JSOP_ENTERBLOCK); - JS_ASSERT(nuses == 0); - blockObj = bce->objectList.lastbox->object; - JS_ASSERT(blockObj->isStaticBlock()); - JS_ASSERT(blockObj->getSlot(JSSLOT_BLOCK_DEPTH).isUndefined()); - - OBJ_SET_BLOCK_DEPTH(cx, blockObj, bce->stackDepth); - ndefs = OBJ_BLOCK_COUNT(cx, blockObj); - } bce->stackDepth += ndefs; if ((uintN)bce->stackDepth > bce->maxStackDepth) bce->maxStackDepth = bce->stackDepth; @@ -1496,6 +1495,16 @@ FlushPops(JSContext *cx, BytecodeEmitter *bce, intN *npops) return JS_TRUE; } +static bool +PopIterator(JSContext *cx, BytecodeEmitter *bce) +{ + if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0) + return false; + if (Emit1(cx, bce, JSOP_ENDITER) < 0) + return false; + return true; +} + /* * Emit additional bytecode(s) for non-local jumps. */ @@ -1533,13 +1542,8 @@ EmitNonLocalJumpFixup(JSContext *cx, BytecodeEmitter *bce, StmtInfo *toStmt) break; case STMT_FOR_IN_LOOP: - /* - * The iterator and the object being iterated need to be popped. - */ FLUSH_POPS(); - if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0) - return JS_FALSE; - if (Emit1(cx, bce, JSOP_ENDITER) < 0) + if (!PopIterator(cx, bce)) return JS_FALSE; break; @@ -1555,12 +1559,31 @@ EmitNonLocalJumpFixup(JSContext *cx, BytecodeEmitter *bce, StmtInfo *toStmt) } if (stmt->flags & SIF_SCOPE) { - /* There is a Block object with locals on the stack to pop. */ FLUSH_POPS(); - if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0) - return JS_FALSE; - uintN i = OBJ_BLOCK_COUNT(cx, stmt->blockObj); - EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i); + uintN blockObjCount = OBJ_BLOCK_COUNT(cx, stmt->blockObj); + if (stmt->flags & SIF_FOR_BLOCK) { + /* + * For a for-let-in statement, pushing/popping the block is + * interleaved with JSOP_(END)ITER. Just handle both together + * here and skip over the enclosing STMT_FOR_IN_LOOP. + */ + JS_ASSERT(stmt->down->type == STMT_FOR_IN_LOOP); + stmt = stmt->down; + if (stmt == toStmt) + break; + if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0) + return JS_FALSE; + if (!PopIterator(cx, bce)) + return JS_FALSE; + if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0) + return JS_FALSE; + EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount); + } else { + /* There is a Block object with locals on the stack to pop. */ + if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0) + return JS_FALSE; + EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObjCount); + } } } @@ -1928,15 +1951,22 @@ AdjustBlockSlot(JSContext *cx, BytecodeEmitter *bce, jsint slot) } static bool -EmitEnterBlock(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce) +EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op) { JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE)); - if (!EmitObjectOp(cx, pn->pn_objbox, JSOP_ENTERBLOCK, bce)) + if (!EmitObjectOp(cx, pn->pn_objbox, op, bce)) return false; JSObject *blockObj = pn->pn_objbox->object; - jsint depth = AdjustBlockSlot(cx, bce, OBJ_BLOCK_DEPTH(cx, blockObj)); - if (depth < 0) + JS_ASSERT(blockObj->isStaticBlock()); + JS_ASSERT(blockObj->getSlot(JSSLOT_BLOCK_DEPTH).isUndefined()); + + int depth = bce->stackDepth - + (OBJ_BLOCK_COUNT(cx, blockObj) + ((op == JSOP_ENTERLET1) ? 1 : 0)); + JS_ASSERT(depth >= 0); + OBJ_SET_BLOCK_DEPTH(cx, blockObj, depth); + int depthPlusFixed = AdjustBlockSlot(cx, bce, depth); + if (depthPlusFixed < 0) return false; uintN base = JSSLOT_FREE(&BlockClass); @@ -1951,8 +1981,8 @@ EmitEnterBlock(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce) Definition *dn = (Definition *) v.toPrivate(); JS_ASSERT(dn->isDefn()); - JS_ASSERT(uintN(dn->frameSlot() + depth) < JS_BIT(16)); - dn->pn_cookie.set(dn->pn_cookie.level(), uint16_t(dn->frameSlot() + depth)); + JS_ASSERT(uintN(dn->frameSlot() + depthPlusFixed) < JS_BIT(16)); + dn->pn_cookie.set(dn->pn_cookie.level(), uint16_t(dn->frameSlot() + depthPlusFixed)); #ifdef DEBUG for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) { JS_ASSERT(pnu->pn_lexdef == dn); @@ -3178,9 +3208,6 @@ EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) intN noteIndex; size_t switchSize, tableSize; jsbytecode *pc, *savepc; -#if JS_HAS_BLOCK_SCOPE - int count; -#endif StmtInfo stmtInfo; /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */ @@ -3189,43 +3216,35 @@ EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) hasDefault = constPropagated = JS_FALSE; defaultOffset = -1; - /* - * If the switch contains let variables scoped by its body, model the - * resulting block on the stack first, before emitting the discriminant's - * bytecode (in case the discriminant contains a stack-model dependency - * such as a let expression). - */ pn2 = pn->pn_right; #if JS_HAS_BLOCK_SCOPE - if (pn2->isKind(PNK_LEXICALSCOPE)) { - /* - * Push the body's block scope before discriminant code-gen to reflect - * the order of slots on the stack. The block's locals must lie under - * the discriminant on the stack so that case-dispatch bytecodes can - * find the discriminant on top of stack. - */ - count = OBJ_BLOCK_COUNT(cx, pn2->pn_objbox->object); - PushBlockScope(bce, &stmtInfo, pn2->pn_objbox->object, -1); - stmtInfo.type = STMT_SWITCH; - - /* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */ - if (!EmitEnterBlock(cx, pn2, bce)) - return JS_FALSE; - } -#ifdef __GNUC__ - else { - count = 0; - } -#endif -#endif - /* - * Emit code for the discriminant first (or nearly first, in the case of a - * switch whose body is a block scope). + * If there are hoisted let declarations, their stack slots go under the + * discriminant's value so push their slots now and enter the block later. */ + uint32_t blockCount = 0; + if (pn2->isKind(PNK_LEXICALSCOPE)) { + blockCount = OBJ_BLOCK_COUNT(cx, pn2->pn_objbox->object); + for (uint32_t i = 0; i < blockCount; ++i) { + if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) + return JS_FALSE; + } + } +#endif + + /* Push the discriminant. */ if (!EmitTree(cx, bce, pn->pn_left)) return JS_FALSE; +#if JS_HAS_BLOCK_SCOPE + if (pn2->isKind(PNK_LEXICALSCOPE)) { + PushBlockScope(bce, &stmtInfo, pn2->pn_objbox->object, -1); + stmtInfo.type = STMT_SWITCH; + if (!EmitEnterBlock(cx, bce, pn2, JSOP_ENTERLET1)) + return JS_FALSE; + } +#endif + /* Switch bytecodes run from here till end of final case. */ top = bce->offset(); #if !JS_HAS_BLOCK_SCOPE @@ -3686,7 +3705,7 @@ out: #if JS_HAS_BLOCK_SCOPE if (ok && pn->pn_right->isKind(PNK_LEXICALSCOPE)) - EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count); + EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockCount); #endif } return ok; @@ -3772,6 +3791,21 @@ MaybeEmitVarDecl(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode * return true; } +/* + * This enum tells EmitVariables and the destructuring functions how emit the + * given Parser::variables parse tree. In the base case, DefineVars, the caller + * only wants variables to be defined in the prologue (if necessary). For + * PushInitialValues, variable initializer expressions are evaluated and left + * on the stack. For InitializeVars, the initializer expressions values are + * assigned (to local variables) and popped. + */ +enum VarEmitOption +{ + DefineVars = 0, + PushInitialValues = 1, + InitializeVars = 2 +}; + #if JS_HAS_DESTRUCTURING typedef JSBool @@ -3817,11 +3851,26 @@ EmitDestructuringDecls(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, Parse } static JSBool -EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn); +EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, + VarEmitOption emitOption); +/* + * EmitDestructuringLHS assumes the to-be-destructured value has been pushed on + * the stack and emits code to destructure a single lhs expression (either a + * name or a compound []/{} expression). + * + * If emitOption is InitializeVars, the to-be-destructured value is assigned to + * locals and ultimately the initial slot is popped (-1 total depth change). + * + * If emitOption is PushInitialValues, the to-be-destructured value is replaced + * with the initial values of the N (where 0 <= N) variables assigned in the + * lhs expression. (Same post-condition as EmitDestructuringOpsHelper) + */ static JSBool -EmitDestructuringLHS(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) +EmitDestructuringLHS(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption) { + JS_ASSERT(emitOption != DefineVars); + /* * Now emit the lvalue opcode sequence. If the lvalue is a nested * destructuring initialiser-form, call ourselves to handle it, then @@ -3829,11 +3878,29 @@ EmitDestructuringLHS(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) * ending with a JSOP_ENUMELEM or equivalent op. */ if (pn->isKind(PNK_RB) || pn->isKind(PNK_RC)) { - if (!EmitDestructuringOpsHelper(cx, bce, pn)) - return JS_FALSE; - if (Emit1(cx, bce, JSOP_POP) < 0) + if (!EmitDestructuringOpsHelper(cx, bce, pn, emitOption)) return JS_FALSE; + if (emitOption == InitializeVars) { + /* + * Per its post-condition, EmitDestructuringOpsHelper has left the + * to-be-destructured value on top of the stack. + */ + if (Emit1(cx, bce, JSOP_POP) < 0) + return JS_FALSE; + } } else { + if (emitOption == PushInitialValues) { + /* + * The lhs is a simple name so the to-be-destructured value is + * its initial value and there is nothing to do. + */ + JS_ASSERT(pn->getOp() == JSOP_SETLOCAL); + JS_ASSERT(pn->pn_dflags & PND_BOUND); + return JS_TRUE; + } + + /* All paths below must pop after assigning to the lhs. */ + if (pn->isKind(PNK_NAME)) { if (!BindNameToSlot(cx, bce, pn)) return JS_FALSE; @@ -3898,14 +3965,23 @@ EmitDestructuringLHS(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) /* * Recursive helper for EmitDestructuringOps. + * EmitDestructuringOpsHelper assumes the to-be-destructured value has been + * pushed on the stack and emits code to destructure each part of a [] or {} + * lhs expression. * - * Given a value to destructure on the stack, walk over an object or array - * initialiser at pn, emitting bytecodes to match property values and store - * them in the lvalues identified by the matched property names. + * If emitOption is InitializeVars, the initial to-be-destructured value is + * left untouched on the stack and the overall depth is not changed. + * + * If emitOption is PushInitialValues, the to-be-destructured value is replaced + * with the initial values of the N (where 0 <= N) variables assigned in the + * lhs expression. (Same post-condition as EmitDestructuringLHS) */ static JSBool -EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) +EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, + VarEmitOption emitOption) { + JS_ASSERT(emitOption != DefineVars); + jsuint index; ParseNode *pn2, *pn3; JSBool doElemOp; @@ -3919,8 +3995,8 @@ EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (pn->pn_count == 0) { /* Emit a DUP;POP sequence for the decompiler. */ - return Emit1(cx, bce, JSOP_DUP) >= 0 && - Emit1(cx, bce, JSOP_POP) >= 0; + if (Emit1(cx, bce, JSOP_DUP) < 0 || Emit1(cx, bce, JSOP_POP) < 0) + return JS_FALSE; } index = 0; @@ -3976,7 +4052,7 @@ EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) */ if (!EmitElemOpBase(cx, bce, JSOP_GETELEM)) return JS_FALSE; - JS_ASSERT(bce->stackDepth == stackDepth + 1); + JS_ASSERT(bce->stackDepth >= stackDepth + 1); } /* Nullary comma node makes a hole in the array destructurer. */ @@ -3986,14 +4062,47 @@ EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (Emit1(cx, bce, JSOP_POP) < 0) return JS_FALSE; } else { - if (!EmitDestructuringLHS(cx, bce, pn3)) + intN depthBefore = bce->stackDepth; + if (!EmitDestructuringLHS(cx, bce, pn3, emitOption)) return JS_FALSE; + + if (emitOption == PushInitialValues) { + /* + * After '[x,y]' in 'let ([[x,y], z] = o)', the stack is + * | to-be-decompiled-value | x | y | + * The goal is: + * | x | y | z | + * so emit a pick to produce the intermediate state + * | x | y | to-be-decompiled-value | + * before destructuring z. This gives the loop invariant that + * the to-be-compiled-value is always on top of the stack. + */ + JS_ASSERT((bce->stackDepth - bce->stackDepth) >= -1); + uintN pickDistance = (uintN)((bce->stackDepth + 1) - depthBefore); + if (pickDistance > 0) { + if (pickDistance > jsbytecode(-1)) { + ReportCompileErrorNumber(cx, bce->tokenStream(), pn3, JSREPORT_ERROR, + JSMSG_TOO_MANY_LOCALS); + return JS_FALSE; + } + if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)pickDistance) < 0) + return false; + } + } } - JS_ASSERT(bce->stackDepth == stackDepth); ++index; } + if (emitOption == PushInitialValues) { + /* + * Per the above loop invariant, to-be-decompiled-value is at the top + * of the stack. To achieve the post-condition, pop it. + */ + if (Emit1(cx, bce, JSOP_POP) < 0) + return JS_FALSE; + } + return JS_TRUE; } @@ -4012,8 +4121,74 @@ OpToDeclType(JSOp op) } } +/* + * This utility accumulates a set of SRC_DESTRUCTLET notes which need to be + * backpatched with the offset from JSOP_DUP to JSOP_LET0. + * + * Also record whether the let head was a group assignment ([x,y] = [a,b]) + * (which implies no SRC_DESTRUCTLET notes). + */ +class LetNotes +{ + struct Pair { + ptrdiff_t dup; + uintN index; + Pair(ptrdiff_t dup, uintN index) : dup(dup), index(index) {} + }; + Vector notes; + bool groupAssign; + DebugOnly updateCalled; + + public: + LetNotes(JSContext *cx) : notes(cx), groupAssign(false), updateCalled(false) {} + + ~LetNotes() { + JS_ASSERT_IF(!notes.allocPolicy().context()->isExceptionPending(), updateCalled); + } + + void setGroupAssign() { + JS_ASSERT(notes.empty()); + groupAssign = true; + } + + bool isGroupAssign() const { + return groupAssign; + } + + bool append(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t dup, uintN index) { + JS_ASSERT(!groupAssign); + JS_ASSERT(SN_TYPE(bce->notes() + index) == SRC_DESTRUCTLET); + if (!notes.append(Pair(dup, index))) + return false; + + /* + * Pessimistically inflate each srcnote. That way, there is no danger + * of inflation during update() (which would invalidate all indices). + */ + if (!SetSrcNoteOffset(cx, bce, index, 0, SN_MAX_OFFSET)) + return false; + JS_ASSERT(bce->notes()[index + 1] & SN_3BYTE_OFFSET_FLAG); + return true; + } + + /* This should be called exactly once, right before JSOP_ENTERLET0. */ + bool update(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t offset) { + JS_ASSERT(!updateCalled); + for (size_t i = 0; i < notes.length(); ++i) { + JS_ASSERT(offset > notes[i].dup); + JS_ASSERT(*bce->code(notes[i].dup) == JSOP_DUP); + JS_ASSERT(bce->notes()[notes[i].index + 1] & SN_3BYTE_OFFSET_FLAG); + if (!SetSrcNoteOffset(cx, bce, notes[i].index, 0, offset - notes[i].dup)) + return false; + } + updateCalled = true; + return true; + } +}; + static JSBool -EmitDestructuringOps(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn) +EmitDestructuringOps(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t declType, ParseNode *pn, + LetNotes *letNotes = NULL) { /* * If we're called from a variable declaration, help the decompiler by @@ -4021,14 +4196,21 @@ EmitDestructuringOps(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNo * If the destructuring initialiser is empty, our helper will emit a * JSOP_DUP followed by a JSOP_POP for the decompiler. */ - if (NewSrcNote2(cx, bce, SRC_DESTRUCT, OpToDeclType(prologOp)) < 0) - return JS_FALSE; + if (letNotes) { + ptrdiff_t index = NewSrcNote2(cx, bce, SRC_DESTRUCTLET, 0); + if (index < 0 || !letNotes->append(cx, bce, bce->offset(), (uintN)index)) + return JS_FALSE; + } else { + if (NewSrcNote2(cx, bce, SRC_DESTRUCT, declType) < 0) + return JS_FALSE; + } /* * Call our recursive helper to emit the destructuring assignments and * related stack manipulations. */ - return EmitDestructuringOpsHelper(cx, bce, pn); + VarEmitOption emitOption = letNotes ? PushInitialValues : InitializeVars; + return EmitDestructuringOpsHelper(cx, bce, pn, emitOption); } static JSBool @@ -4069,7 +4251,7 @@ EmitGroupAssignment(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, if (Emit1(cx, bce, JSOP_POP) < 0) return JS_FALSE; } else { - if (!EmitDestructuringLHS(cx, bce, pn)) + if (!EmitDestructuringLHS(cx, bce, pn, InitializeVars)) return JS_FALSE; } } @@ -4105,42 +4287,63 @@ MaybeEmitGroupAssignment(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, Par return JS_TRUE; } +/* + * Like MaybeEmitGroupAssignment, but for 'let ([x,y] = [a,b]) ...'. + * + * Instead of issuing a sequence |dup|eval-rhs|set-lhs|pop| (which doesn't work + * since the bound vars don't yet have slots), just eval/push each rhs element + * just like what EmitLet would do for 'let (x = a, y = b) ...'. While shorter, + * simpler and more efficient than MaybeEmitGroupAssignment, it is harder to + * decompile so we restrict the ourselves to cases where the lhs and rhs are in + * 1:1 correspondence and lhs elements are simple names. + */ +static bool +MaybeEmitLetGroupDecl(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, + LetNotes *letNotes, JSOp *pop) +{ + JS_ASSERT(pn->isKind(PNK_ASSIGN)); + JS_ASSERT(pn->isOp(JSOP_NOP)); + JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV); + + ParseNode *lhs = pn->pn_left; + ParseNode *rhs = pn->pn_right; + if (lhs->isKind(PNK_RB) && rhs->isKind(PNK_RB) && + !(rhs->pn_xflags & PNX_HOLEY) && + !(lhs->pn_xflags & PNX_HOLEY) && + lhs->pn_count == rhs->pn_count) + { + for (ParseNode *l = lhs->pn_head; l; l = l->pn_next) { + if (l->getOp() != JSOP_SETLOCAL) + return true; + } + + for (ParseNode *r = rhs->pn_head; r; r = r->pn_next) { + if (!EmitTree(cx, bce, r)) + return false; + } + + letNotes->setGroupAssign(); + *pop = JSOP_NOP; + } + return true; +} + #endif /* JS_HAS_DESTRUCTURING */ static JSBool -EmitVariables(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool inLetHead, - ptrdiff_t *headNoteIndex) +EmitVariables(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption, + LetNotes *letNotes = NULL) { - bool forInVar, first; - ptrdiff_t off, noteIndex, tmp; - ParseNode *pn2, *pn3, *next; - JSOp op; - jsatomid atomIndex; - uintN oldflags; + JS_ASSERT(pn->isArity(PN_LIST)); + JS_ASSERT(!!letNotes == (emitOption == PushInitialValues)); - /* Default in case of JS_HAS_BLOCK_SCOPE early return, below. */ - *headNoteIndex = -1; - - /* - * Let blocks and expressions have a parenthesized head in which the new - * scope is not yet open. Initializer evaluation uses the parent node's - * lexical scope. If popScope is true below, then we hide the top lexical - * block from any calls to BindNameToSlot hiding in pn2->pn_expr so that - * it won't find any names in the new let block. - * - * The same goes for let declarations in the head of any kind of for loop. - * Unlike a let declaration 'let x = i' within a block, where x is hoisted - * to the start of the block, a 'for (let x = i...) ...' loop evaluates i - * in the containing scope, and puts x in the loop body's scope. - */ - DebugOnly let = (pn->isOp(JSOP_NOP)); - forInVar = (pn->pn_xflags & PNX_FORINVAR) != 0; - - off = noteIndex = -1; - for (pn2 = pn->pn_head; ; pn2 = next) { - first = pn2 == pn->pn_head; + ptrdiff_t off = -1, noteIndex = -1; + ParseNode *next; + for (ParseNode *pn2 = pn->pn_head; ; pn2 = next) { + bool first = pn2 == pn->pn_head; next = pn2->pn_next; + ParseNode *pn3; if (!pn2->isKind(PNK_NAME)) { #if JS_HAS_DESTRUCTURING if (pn2->isKind(PNK_RB) || pn2->isKind(PNK_RC)) { @@ -4152,7 +4355,7 @@ EmitVariables(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool inLetHe * enumerating opcode and a branch that tests whether the * enumeration ended. */ - JS_ASSERT(forInVar); + JS_ASSERT(emitOption == DefineVars); JS_ASSERT(pn->pn_count == 1); if (!EmitDestructuringDecls(cx, bce, pn->getOp(), pn2)) return JS_FALSE; @@ -4168,7 +4371,7 @@ EmitVariables(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool inLetHe */ JS_ASSERT(pn2->isKind(PNK_ASSIGN)); JS_ASSERT(pn2->isOp(JSOP_NOP)); - JS_ASSERT(!forInVar); + JS_ASSERT(emitOption != DefineVars); /* * To allow the front end to rewrite var f = x; as f = x; when a @@ -4187,6 +4390,8 @@ EmitVariables(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool inLetHe } #if JS_HAS_DESTRUCTURING + ptrdiff_t stackDepthBefore = bce->stackDepth; + JSOp op = JSOP_POP; if (pn->pn_count == 1) { /* * If this is the only destructuring assignment in the list, @@ -4195,34 +4400,46 @@ EmitVariables(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool inLetHe * in pn->pn_op, to suppress a second (and misplaced) 'let'. */ JS_ASSERT(noteIndex < 0 && !pn2->pn_next); - op = JSOP_POP; - if (!MaybeEmitGroupAssignment(cx, bce, - inLetHead ? JSOP_POP : pn->getOp(), - pn2, &op)) { - return JS_FALSE; - } - if (op == JSOP_NOP) { - pn->pn_xflags = (pn->pn_xflags & ~PNX_POPVAR) | PNX_GROUPINIT; - break; + if (letNotes) { + if (!MaybeEmitLetGroupDecl(cx, bce, pn2, letNotes, &op)) + return JS_FALSE; + } else { + if (!MaybeEmitGroupAssignment(cx, bce, pn->getOp(), pn2, &op)) + return JS_FALSE; } } + if (op == JSOP_NOP) { + pn->pn_xflags = (pn->pn_xflags & ~PNX_POPVAR) | PNX_GROUPINIT; + } else { + pn3 = pn2->pn_left; + if (!EmitDestructuringDecls(cx, bce, pn->getOp(), pn3)) + return JS_FALSE; - pn3 = pn2->pn_left; - if (!EmitDestructuringDecls(cx, bce, pn->getOp(), pn3)) - return JS_FALSE; + if (!EmitTree(cx, bce, pn2->pn_right)) + return JS_FALSE; - if (!EmitTree(cx, bce, pn2->pn_right)) - return JS_FALSE; + /* Only the first list element should print 'let' or 'var'. */ + ptrdiff_t declType = pn2 == pn->pn_head + ? OpToDeclType(pn->getOp()) + : SRC_DECL_NONE; - /* - * Veto pn->pn_op if inLetHead to avoid emitting a SRC_DESTRUCT - * that's redundant with respect to the SRC_DECL/SRC_DECL_LET that - * we will emit at the bottom of this function. - */ - if (!EmitDestructuringOps(cx, bce, - inLetHead ? JSOP_POP : pn->getOp(), - pn3)) { - return JS_FALSE; + if (!EmitDestructuringOps(cx, bce, declType, pn3, letNotes)) + return JS_FALSE; + } + ptrdiff_t stackDepthAfter = bce->stackDepth; + + /* Give let ([] = x) a slot (see CheckDestructuring). */ + JS_ASSERT(stackDepthBefore <= stackDepthAfter); + if (letNotes && stackDepthBefore == stackDepthAfter) { + if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) + return JS_FALSE; + } + + /* If we are not initializing, nothing to pop. */ + if (emitOption != InitializeVars) { + if (next) + continue; + break; } goto emit_note_pop; #endif @@ -4240,67 +4457,60 @@ EmitVariables(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool inLetHe if (!BindNameToSlot(cx, bce, pn2)) return JS_FALSE; + JSOp op; + jsatomid atomIndex; + op = pn2->getOp(); if (op == JSOP_ARGUMENTS) { /* JSOP_ARGUMENTS => no initializer */ - JS_ASSERT(!pn3 && !let); + JS_ASSERT(!pn3 && !letNotes); pn3 = NULL; -#ifdef __GNUC__ - atomIndex = 0; /* quell GCC overwarning */ -#endif + atomIndex = 0; } else { JS_ASSERT(op != JSOP_CALLEE); - JS_ASSERT(!pn2->pn_cookie.isFree() || !let); + JS_ASSERT(!pn2->pn_cookie.isFree() || !pn->isOp(JSOP_NOP)); if (!MaybeEmitVarDecl(cx, bce, pn->getOp(), pn2, &atomIndex)) return JS_FALSE; if (pn3) { - JS_ASSERT(!forInVar); - if (op == JSOP_SETNAME) { - JS_ASSERT(!let); + JS_ASSERT(emitOption != DefineVars); + JS_ASSERT_IF(emitOption == PushInitialValues, op == JSOP_SETLOCAL); + if (op == JSOP_SETNAME) EMIT_INDEX_OP(JSOP_BINDNAME, atomIndex); - } else if (op == JSOP_SETGNAME) { - JS_ASSERT(!let); + else if (op == JSOP_SETGNAME) EMIT_INDEX_OP(JSOP_BINDGNAME, atomIndex); - } if (pn->isOp(JSOP_DEFCONST) && !DefineCompileTimeConstant(cx, bce, pn2->pn_atom, pn3)) { return JS_FALSE; } - oldflags = bce->flags; + uintN oldflags = bce->flags; bce->flags &= ~TCF_IN_FOR_INIT; if (!EmitTree(cx, bce, pn3)) return JS_FALSE; bce->flags |= oldflags & TCF_IN_FOR_INIT; + } else if (letNotes) { + /* JSOP_ENTERLETx expects at least 1 slot to have been pushed. */ + if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) + return JS_FALSE; } } - /* - * The parser rewrites 'for (var x = i in o)' to hoist 'var x = i' -- - * likewise 'for (let x = i in o)' becomes 'i; for (let x in o)' using - * a PNK_SEQ node to make the two statements appear as one. Therefore - * if this declaration is part of a for-in loop head, we do not need to - * emit op or any source note. Our caller, the PNK_FOR/PNK_IN case in - * EmitTree, will annotate appropriately. - */ - JS_ASSERT_IF(pn2->isDefn(), pn3 == pn2->pn_expr); - if (forInVar) { - JS_ASSERT(pn->pn_count == 1); - JS_ASSERT(!pn3); + /* If we are not initializing, nothing to pop. */ + if (emitOption != InitializeVars) { + if (next) + continue; break; } - if (first && - !inLetHead && - NewSrcNote2(cx, bce, SRC_DECL, - (pn->isOp(JSOP_DEFCONST)) - ? SRC_DECL_CONST - : (pn->isOp(JSOP_DEFVAR)) - ? SRC_DECL_VAR - : SRC_DECL_LET) < 0) - { + JS_ASSERT_IF(pn2->isDefn(), pn3 == pn2->pn_expr); + if (first && NewSrcNote2(cx, bce, SRC_DECL, + (pn->isOp(JSOP_DEFCONST)) + ? SRC_DECL_CONST + : (pn->isOp(JSOP_DEFVAR)) + ? SRC_DECL_VAR + : SRC_DECL_LET) < 0) { return JS_FALSE; } if (op == JSOP_ARGUMENTS) { @@ -4315,7 +4525,7 @@ EmitVariables(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool inLetHe #if JS_HAS_DESTRUCTURING emit_note_pop: #endif - tmp = bce->offset(); + ptrdiff_t tmp = bce->offset(); if (noteIndex >= 0) { if (!SetSrcNoteOffset(cx, bce, (uintN)noteIndex, 0, tmp-off)) return JS_FALSE; @@ -4328,16 +4538,12 @@ EmitVariables(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool inLetHe return JS_FALSE; } - /* If this is a let head, emit and return a srcnote on the pop. */ - if (inLetHead) { - *headNoteIndex = NewSrcNote(cx, bce, SRC_DECL); - if (*headNoteIndex < 0) + if (pn->pn_xflags & PNX_POPVAR) { + if (Emit1(cx, bce, JSOP_POP) < 0) return JS_FALSE; - if (!(pn->pn_xflags & PNX_POPVAR)) - return Emit1(cx, bce, JSOP_NOP) >= 0; } - return !(pn->pn_xflags & PNX_POPVAR) || Emit1(cx, bce, JSOP_POP) >= 0; + return JS_TRUE; } static bool @@ -4518,7 +4724,7 @@ EmitAssignment(JSContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, Par #if JS_HAS_DESTRUCTURING case PNK_RB: case PNK_RC: - if (!EmitDestructuringOps(cx, bce, JSOP_SETNAME, lhs)) + if (!EmitDestructuringOps(cx, bce, SRC_DECL_NONE, lhs)) return false; break; #endif @@ -4535,7 +4741,7 @@ EmitAssignment(JSContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, Par return true; } -#if defined DEBUG_brendan || defined DEBUG_mrbkap +#ifdef DEBUG static JSBool GettableNoteForNextOp(BytecodeEmitter *bce) { @@ -4736,7 +4942,7 @@ EmitCatch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) #if JS_HAS_DESTRUCTURING case PNK_RB: case PNK_RC: - if (!EmitDestructuringOps(cx, bce, JSOP_NOP, pn2)) + if (!EmitDestructuringOps(cx, bce, SRC_DECL_NONE, pn2)) return false; if (Emit1(cx, bce, JSOP_POP) < 0) return false; @@ -5106,43 +5312,109 @@ EmitIf(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) } #if JS_HAS_BLOCK_SCOPE +/* + * pnLet represents one of: + * + * let-expression: (let (x = y) EXPR) + * let-statement: let (x = y) { ... } + * + * For a let-expression 'let (x = a, [y,z] = b) e', EmitLet produces: + * + * bytecode stackDepth srcnotes + * evaluate a +1 + * evaluate b +1 + * dup +1 SRC_DESTRUCTLET + offset to enterlet0 + * destructure y + * pick 1 + * dup +1 SRC_DESTRUCTLET + offset to enterlet0 + * pick + * destructure z + * pick 1 + * pop -1 + * enterlet0 SRC_DECL + offset to leaveblockexpr + * evaluate e +1 + * leaveblockexpr -3 SRC_PCBASE + offset to evaluate a + * + * Note that, since enterlet0 simply changes fp->blockChain and does not + * otherwise touch the stack, evaluation of the let-var initializers must leave + * the initial value in the let-var's future slot. + * + * The SRC_DESTRUCTLET distinguish JSOP_DUP as the beginning of a destructuring + * let initialization and the offset allows the decompiler to find the block + * object from which to find let var names. These forward offsets require + * backpatching, which is handled by LetNotes. + * + * The SRC_DECL offset allows recursive decompilation of 'e'. + * + * The SRC_PCBASE allows js_DecompileValueGenerator to walk backwards from + * JSOP_LEAVEBLOCKEXPR to the beginning of the let and is only needed for + * let-expressions. + */ static bool -EmitLet(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) +EmitLet(JSContext *cx, BytecodeEmitter *bce, ParseNode *pnLet) { - /* - * pn represents one of these syntactic constructs: - * let-expression: (let (x = y) EXPR) - * let-statement: let (x = y) { ... } - * let-declaration in statement context: let x = y; - * let-declaration in for-loop head: for (let ...) ... - * - * Let-expressions and let-statements are represented as binary nodes - * with their variable declarations on the left and the body on the - * right. - */ - ParseNode *pn2; - if (pn->isArity(PN_BINARY)) { - pn2 = pn->pn_right; - pn = pn->pn_left; - } else { - pn2 = NULL; + JS_ASSERT(pnLet->isArity(PN_BINARY)); + ParseNode *varList = pnLet->pn_left; + JS_ASSERT(varList->isArity(PN_LIST)); + ParseNode *letBody = pnLet->pn_right; + JS_ASSERT(letBody->isLet() && letBody->isKind(PNK_LEXICALSCOPE)); + JSObject *blockObj = letBody->pn_objbox->object; + JS_ASSERT(blockObj->isStaticBlock()); + + ptrdiff_t letHeadOffset = bce->offset(); + intN letHeadDepth = bce->stackDepth; + + LetNotes letNotes(cx); + if (!EmitVariables(cx, bce, varList, PushInitialValues, &letNotes)) + return false; + + /* Push storage for hoisted let decls (e.g. 'let (x) { let y }'). */ + uint32_t alreadyPushed = uintN(bce->stackDepth - letHeadDepth); + uint32_t blockObjCount = OBJ_BLOCK_COUNT(cx, blockObj); + for (uint32_t i = alreadyPushed; i < blockObjCount; ++i) { + /* Tell the decompiler not to print the decl in the let head. */ + if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0) + return false; + if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) + return false; } - /* Non-null pn2 means that pn is the variable list from a let head. */ - JS_ASSERT(pn->isArity(PN_LIST)); - ptrdiff_t noteIndex; - if (!EmitVariables(cx, bce, pn, pn2 != NULL, ¬eIndex)) - return false; - ptrdiff_t tmp = bce->offset(); + StmtInfo stmtInfo; + PushBlockScope(bce, &stmtInfo, blockObj, bce->offset()); - /* Thus non-null pn2 is the body of the let block or expression. */ - if (pn2 && !EmitTree(cx, bce, pn2)) + if (!letNotes.update(cx, bce, bce->offset())) return false; - if (noteIndex >= 0 && !SetSrcNoteOffset(cx, bce, (uintN)noteIndex, 0, bce->offset() - tmp)) + ptrdiff_t declNote = NewSrcNote(cx, bce, SRC_DECL); + if (declNote < 0) return false; - return true; + ptrdiff_t bodyBegin = bce->offset(); + if (!EmitEnterBlock(cx, bce, letBody, JSOP_ENTERLET0)) + return false; + + if (!EmitTree(cx, bce, letBody->pn_expr)) + return false; + + JSOp leaveOp = letBody->getOp(); + if (leaveOp == JSOP_LEAVEBLOCKEXPR) { + if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - letHeadOffset) < 0) + return false; + } + + JS_ASSERT(leaveOp == JSOP_LEAVEBLOCK || leaveOp == JSOP_LEAVEBLOCKEXPR); + EMIT_UINT16_IMM_OP(leaveOp, OBJ_BLOCK_COUNT(cx, blockObj)); + + ptrdiff_t bodyEnd = bce->offset(); + JS_ASSERT(bodyEnd > bodyBegin); + + if (!PopStatementBCE(cx, bce)) + return false; + + ptrdiff_t o = PackLetData((bodyEnd - bodyBegin) - + (JSOP_ENTERLET0_LENGTH + JSOP_LEAVEBLOCK_LENGTH), + letNotes.isGroupAssign()); + return SetSrcNoteOffset(cx, bce, declNote, 0, o); } #endif @@ -5224,56 +5496,52 @@ EmitXMLProcessingInstruction(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) static bool EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { + JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE)); + JS_ASSERT(pn->getOp() == JSOP_LEAVEBLOCK); + StmtInfo stmtInfo; - StmtInfo *stmt; ObjectBox *objbox = pn->pn_objbox; - PushBlockScope(bce, &stmtInfo, objbox->object, bce->offset()); + JSObject *blockObj = objbox->object; + JS_ASSERT(blockObj->isStaticBlock()); + PushBlockScope(bce, &stmtInfo, blockObj, bce->offset()); /* - * If this lexical scope is not for a catch block, let block or let - * expression, or any kind of for loop (where the scope starts in the - * head after the first part if for (;;), else in the body if for-in); - * and if our container is top-level but not a function body, or else - * a block statement; then emit a SRC_BRACE note. All other container - * statements get braces by default from the decompiler. + * For compound statements (i.e. { stmt-list }), the decompiler does not + * emit curlies by default. However, if this stmt-list contains a let + * declaration, this is semantically invalid so we need to add a srcnote to + * enterblock to tell the decompiler to add curlies. This condition + * shouldn't be so complicated; try to find a simpler condition. */ ptrdiff_t noteIndex = -1; - ParseNodeKind kind = pn->expr()->getKind(); - if (kind != PNK_CATCH && kind != PNK_LET && kind != PNK_FOR && - (!(stmt = stmtInfo.down) - ? !bce->inFunction() - : stmt->type == STMT_BLOCK)) + if (pn->expr()->getKind() != PNK_FOR && + pn->expr()->getKind() != PNK_CATCH && + (stmtInfo.down + ? stmtInfo.down->type == STMT_BLOCK && + (!stmtInfo.down->down || stmtInfo.down->down->type != STMT_FOR_IN_LOOP) + : !bce->inFunction())) { -#if defined DEBUG_brendan || defined DEBUG_mrbkap /* There must be no source note already output for the next op. */ JS_ASSERT(bce->noteCount() == 0 || bce->lastNoteOffset() != bce->offset() || !GettableNoteForNextOp(bce)); -#endif noteIndex = NewSrcNote2(cx, bce, SRC_BRACE, 0); if (noteIndex < 0) return false; } - ptrdiff_t top = bce->offset(); - if (!EmitEnterBlock(cx, pn, bce)) + ptrdiff_t bodyBegin = bce->offset(); + if (!EmitEnterBlock(cx, bce, pn, JSOP_ENTERBLOCK)) return false; if (!EmitTree(cx, bce, pn->pn_expr)) return false; - JSOp op = pn->getOp(); - if (op == JSOP_LEAVEBLOCKEXPR) { - if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - top) < 0) - return false; - } else { - if (noteIndex >= 0 && !SetSrcNoteOffset(cx, bce, (uintN)noteIndex, 0, bce->offset() - top)) + if (noteIndex >= 0) { + if (!SetSrcNoteOffset(cx, bce, (uintN)noteIndex, 0, bce->offset() - bodyBegin)) return false; } - /* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */ - uintN count = OBJ_BLOCK_COUNT(cx, objbox->object); - EMIT_UINT16_IMM_OP(op, count); + EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, OBJ_BLOCK_COUNT(cx, blockObj)); return PopStatementBCE(cx, bce); } @@ -5338,17 +5606,48 @@ EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) ParseNode *forHead = pn->pn_left; ParseNode *forBody = pn->pn_right; + ParseNode *pn1 = forHead->pn_kid1; + bool letDecl = pn1 && pn1->isKind(PNK_LEXICALSCOPE); + JS_ASSERT_IF(letDecl, pn1->isLet()); + + JSObject *blockObj = letDecl ? pn1->pn_objbox->object : NULL; + uint32_t blockObjCount = blockObj ? OBJ_BLOCK_COUNT(cx, blockObj) : 0; + + if (letDecl) { + /* + * The let's slot(s) will be under the iterator, but the block must not + * be entered (i.e. fp->blockChain set) until after evaluating the rhs. + * Thus, push to reserve space and enterblock after. The same argument + * applies when leaving the loop. Thus, a for-let-in loop looks like: + * + * push x N + * eval rhs + * iter + * enterlet1 + * goto + * ... loop body + * ifne + * leaveforinlet + * enditer + * popn(N) + */ + for (uint32_t i = 0; i < blockObjCount; ++i) { + if (Emit1(cx, bce, JSOP_UNDEFINED) < 0) + return false; + } + } + /* * If the left part is 'var x', emit code to define x if necessary - * using a prolog opcode, but do not emit a pop. If the left part - * was originally 'var x = i', the parser will have rewritten it; - * see Parser::forStatement. 'for (let x = i in o)' is mercifully - * banned. + * using a prolog opcode, but do not emit a pop. If the left part was + * originally 'var x = i', the parser will have rewritten it; see + * Parser::forStatement. 'for (let x = i in o)' is mercifully banned. */ - if (ParseNode *decl = forHead->pn_kid1) { + if (pn1) { + ParseNode *decl = letDecl ? pn1->pn_expr : pn1; JS_ASSERT(decl->isKind(PNK_VAR) || decl->isKind(PNK_LET)); bce->flags |= TCF_IN_FOR_INIT; - if (!EmitTree(cx, bce, decl)) + if (!EmitVariables(cx, bce, decl, DefineVars)) return false; bce->flags &= ~TCF_IN_FOR_INIT; } @@ -5366,6 +5665,15 @@ EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) if (Emit2(cx, bce, JSOP_ITER, (uint8_t) pn->pn_iflags) < 0) return false; + /* Enter the block before the loop body, after evaluating the obj. */ + StmtInfo letStmt; + if (letDecl) { + PushBlockScope(bce, &letStmt, blockObj, bce->offset()); + letStmt.flags |= SIF_FOR_BLOCK; + if (!EmitEnterBlock(cx, bce, pn1, JSOP_ENTERLET1)) + return false; + } + /* Annotate so the decompiler can find the loop-closing jump. */ intN noteIndex = NewSrcNote(cx, bce, SRC_FOR_IN); if (noteIndex < 0) @@ -5396,6 +5704,7 @@ EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) */ if (!EmitAssignment(cx, bce, forHead->pn_kid2, JSOP_NOP, NULL)) return false; + ptrdiff_t tmp2 = bce->offset(); if (forHead->pn_kid1 && NewSrcNote2(cx, bce, SRC_DECL, (forHead->pn_kid1->isOp(JSOP_DEFVAR)) @@ -5436,14 +5745,30 @@ EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) if (!SetSrcNoteOffset(cx, bce, (uintN)noteIndex, 1, beq - jmp)) return false; - /* Now fixup all breaks and continues (before the JSOP_ENDITER). */ + /* Fixup breaks and continues before JSOP_ITER (and JSOP_LEAVEFORINLET). */ if (!PopStatementBCE(cx, bce)) return false; + if (letDecl) { + if (!PopStatementBCE(cx, bce)) + return false; + if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0) + return false; + } + if (!NewTryNote(cx, bce, JSTRY_ITER, bce->stackDepth, top, bce->offset())) return false; + if (Emit1(cx, bce, JSOP_ENDITER) < 0) + return false; - return Emit1(cx, bce, JSOP_ENDITER) >= 0; + if (letDecl) { + /* Tell the decompiler to pop but not to print. */ + if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0) + return false; + EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount); + } + + return true; } static bool @@ -5480,7 +5805,7 @@ EmitNormalFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) * not need to emit a pop below, so switch to a nop, * just for the decompiler. */ - JS_ASSERT(pn3->isArity(PN_LIST)); + JS_ASSERT(pn3->isArity(PN_LIST) || pn3->isArity(PN_BINARY)); if (pn3->pn_xflags & PNX_GROUPINIT) op = JSOP_NOP; } @@ -6716,18 +7041,13 @@ EmitUnary(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) JSBool frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { - ptrdiff_t top, off, tmp, jmp; - ParseNode *pn2; - JSAtom *atom; - ptrdiff_t noteIndex; - JSOp op; - EmitLevelManager elm(bce); - jsint sharpnum = -1; - JS_CHECK_RECURSION(cx, return JS_FALSE); + EmitLevelManager elm(bce); + JSBool ok = true; - pn->pn_offset = top = bce->offset(); + ptrdiff_t top = bce->offset(); + pn->pn_offset = top; /* Emit notes to tell the current bytecode's source line number. */ UPDATE_LINE_NUMBER_NOTES(cx, bce, pn->pn_pos.begin.lineno); @@ -6806,7 +7126,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) case PNK_VAR: case PNK_CONST: - if (!EmitVariables(cx, bce, pn, JS_FALSE, ¬eIndex)) + if (!EmitVariables(cx, bce, pn, InitializeVars)) return JS_FALSE; break; @@ -6858,16 +7178,17 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) break; case PNK_COMMA: + { /* * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands. * These notes help the decompiler bracket the bytecodes generated * from each sub-expression that follows a comma. */ - off = noteIndex = -1; - for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { + ptrdiff_t off = -1, noteIndex = -1; + for (ParseNode *pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { if (!EmitTree(cx, bce, pn2)) return JS_FALSE; - tmp = bce->offset(); + ptrdiff_t tmp = bce->offset(); if (noteIndex >= 0) { if (!SetSrcNoteOffset(cx, bce, (uintN)noteIndex, 0, tmp-off)) return JS_FALSE; @@ -6882,6 +7203,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) } } break; + } case PNK_ASSIGN: case PNK_ADDASSIGN: @@ -6931,10 +7253,10 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) case PNK_MOD: if (pn->isArity(PN_LIST)) { /* Left-associative operator chain: avoid too much recursion. */ - pn2 = pn->pn_head; + ParseNode *pn2 = pn->pn_head; if (!EmitTree(cx, bce, pn2)) return JS_FALSE; - op = pn->getOp(); + JSOp op = pn->getOp(); while ((pn2 = pn2->pn_next) != NULL) { if (!EmitTree(cx, bce, pn2)) return JS_FALSE; @@ -7023,11 +7345,12 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) #if JS_HAS_XML_SUPPORT case PNK_FILTER: + { JS_ASSERT(!bce->inStrictMode()); if (!EmitTree(cx, bce, pn->pn_left)) return JS_FALSE; - jmp = EmitJump(cx, bce, JSOP_FILTER, 0); + ptrdiff_t jmp = EmitJump(cx, bce, JSOP_FILTER, 0); if (jmp < 0) return JS_FALSE; top = EmitTraceOp(cx, bce, pn->pn_right); @@ -7039,6 +7362,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (EmitJump(cx, bce, JSOP_ENDFILTER, top - bce->offset()) < 0) return JS_FALSE; break; + } #endif case PNK_DOT: @@ -7076,8 +7400,9 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) #if JS_HAS_BLOCK_SCOPE case PNK_LET: - if (!EmitLet(cx, bce, pn)) - return false; + ok = pn->isArity(PN_BINARY) + ? EmitLet(cx, bce, pn) + : EmitVariables(cx, bce, pn, InitializeVars); break; #endif /* JS_HAS_BLOCK_SCOPE */ #if JS_HAS_GENERATORS @@ -7112,8 +7437,9 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) #if JS_HAS_SHARP_VARS case PNK_DEFSHARP: + { JS_ASSERT(bce->hasSharps()); - sharpnum = pn->pn_num; + int sharpnum = pn->pn_num; pn = pn->pn_kid; if (pn->isKind(PNK_RB)) { ok = EmitArray(cx, bce, pn, sharpnum); @@ -7134,6 +7460,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) return JS_FALSE; EMIT_UINT16PAIR_IMM_OP(JSOP_DEFSHARP, bce->sharpSlotBase, (jsatomid) sharpnum); break; + } case PNK_USESHARP: JS_ASSERT(bce->hasSharps()); @@ -7209,7 +7536,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) return JS_FALSE; } - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (pn2->isKind(PNK_XMLCURLYEXPR) && Emit1(cx, bce, JSOP_STARTXMLEXPR) < 0) return JS_FALSE; if (!EmitTree(cx, bce, pn2)) @@ -7221,7 +7548,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (pn->pn_xflags & PNX_XMLROOT) { if (pn->pn_count == 0) { JS_ASSERT(pn->isKind(PNK_XMLLIST)); - atom = cx->runtime->atomState.emptyAtom; + JSAtom *atom = cx->runtime->atomState.emptyAtom; jsatomid index; if (!bce->makeAtomIndex(atom, &index)) return JS_FALSE; @@ -7248,7 +7575,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (pn->isArity(PN_LIST)) { JS_ASSERT(pn->pn_count != 0); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (pn2->isKind(PNK_XMLCURLYEXPR) && Emit1(cx, bce, JSOP_STARTXMLEXPR) < 0) return JS_FALSE; if (!EmitTree(cx, bce, pn2)) @@ -7447,7 +7774,7 @@ SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, uintN index, uintN which, jssrcnote *sn; ptrdiff_t diff; - if ((jsuword)offset >= (jsuword)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16)) { + if (size_t(offset) > SN_MAX_OFFSET) { ReportStatementTooLarge(cx, bce); return JS_FALSE; } @@ -7461,8 +7788,12 @@ SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, uintN index, uintN which, sn += 2; } - /* See if the new offset requires three bytes. */ - if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) { + /* + * See if the new offset requires three bytes either by being too big or if + * the offset has already been inflated (in which case, we need to stay big + * to not break the srcnote encoding if this isn't the last srcnote). + */ + if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK || (*sn & SN_3BYTE_OFFSET_FLAG)) { /* Maybe this offset was already set to a three-byte value. */ if (!(*sn & SN_3BYTE_OFFSET_FLAG)) { /* Losing, need to insert another two bytes for this offset. */ diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index e337ce17c8f4..7b6433a2618f 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -942,9 +942,10 @@ enum SrcNoteType { from before loop, else JSOP_NOP at top of do-while loop */ SRC_CONTINUE = 5, /* JSOP_GOTO is a continue, not a break; - also used on JSOP_ENDINIT if extra comma - at end of array literal: [1,2,,]; - JSOP_DUP continuing destructuring pattern */ + JSOP_ENDINIT needs extra comma at end of + array literal: [1,2,,]; + JSOP_DUP continuing destructuring pattern; + JSOP_POP at end of for-in */ SRC_DECL = 6, /* type of a declaration (var, const, let*) */ SRC_DESTRUCT = 6, /* JSOP_DUP starting a destructuring assignment operation, with SRC_DECL_* offset operand */ @@ -952,6 +953,8 @@ enum SrcNoteType { next POP, or from CONDSWITCH to first CASE opcode, etc. -- always a forward delta */ SRC_GROUPASSIGN = 7, /* SRC_DESTRUCT variant for [a, b] = [c, d] */ + SRC_DESTRUCTLET = 7, /* JSOP_DUP starting a destructuring let + operation, with offset to JSOP_ENTERLET0 */ SRC_ASSIGNOP = 8, /* += or another assign-op follows */ SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */ SRC_BRACE = 10, /* mandatory brace, for scope or to avoid @@ -1030,6 +1033,8 @@ enum SrcNoteType { #define SN_3BYTE_OFFSET_FLAG 0x80 #define SN_3BYTE_OFFSET_MASK 0x7f +#define SN_MAX_OFFSET ((size_t)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16) - 1) + #define SN_LENGTH(sn) ((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 \ : js_SrcNoteLength(sn)) #define SN_NEXT(sn) ((sn) + SN_LENGTH(sn)) @@ -1102,6 +1107,28 @@ BytecodeEmitter::countFinalSourceNotes() return cnt; } +/* + * To avoid offending js_SrcNoteSpec[SRC_DECL].arity, pack the two data needed + * to decompile let into one ptrdiff_t: + * offset: offset to the LEAVEBLOCK(EXPR) op (not including ENTER/LEAVE) + * groupAssign: whether this was an optimized group assign ([x,y] = [a,b]) + */ +inline ptrdiff_t PackLetData(size_t offset, bool groupAssign) +{ + JS_ASSERT(offset <= (size_t(-1) >> 1)); + return ptrdiff_t(offset << 1) | ptrdiff_t(groupAssign); +} + +inline size_t LetDataToOffset(ptrdiff_t w) +{ + return size_t(w) >> 1; +} + +inline bool LetDataToGroupAssign(ptrdiff_t w) +{ + return size_t(w) & 1; +} + } /* namespace js */ struct JSSrcNoteSpec { diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 425ef9e394f1..c633c1dc50f5 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -845,6 +845,11 @@ struct ParseNode { /* Return true if this node appears in a Directive Prologue. */ bool isDirectivePrologueMember() const { return pn_prologue; } +#ifdef JS_HAS_DESTRUCTURING + /* Return true if this represents a hole in an array literal. */ + bool isArrayHole() const { return isKind(PNK_COMMA) && isArity(PN_NULLARY); } +#endif + #ifdef JS_HAS_GENERATOR_EXPRS /* * True if this node is a desugared generator expression. diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 65a305838738..c58efa1e08c9 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -837,6 +837,12 @@ js::DefineArg(ParseNode *pn, JSAtom *atom, uintN i, TreeContext *tc) typedef JSBool (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc); +static JSBool +BindLet(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc); + +static JSBool +BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc); + struct BindData { BindData() : fresh(true) {} @@ -846,10 +852,26 @@ struct BindData { Binder binder; /* binder, discriminates u */ union { struct { + VarContext varContext; + JSObject *blockObj; uintN overflow; } let; }; bool fresh; + + void initLet(VarContext varContext, JSObject *blockObj, uintN overflow) { + this->pn = NULL; + this->op = JSOP_NOP; + this->binder = BindLet; + this->let.varContext = varContext; + this->let.blockObj = blockObj; + this->let.overflow = overflow; + } + + void initVarOrConst(JSOp op) { + this->op = op; + this->binder = BindVarOrConst; + } }; static bool @@ -1877,6 +1899,19 @@ MatchLabel(JSContext *cx, TokenStream *ts, PropertyName **label) return true; } +static bool +ReportRedeclaration(JSContext *cx, TreeContext *tc, ParseNode *pn, bool isConst, JSAtom *atom) +{ + JSAutoByteString name; + if (js_AtomToPrintableString(cx, atom, &name)) { + ReportCompileErrorNumber(cx, TS(tc->parser), pn, + JSREPORT_ERROR, JSMSG_REDECLARED_VAR, + isConst ? "const" : "variable", + name.ptr()); + } + return false; +} + /* * Define a let-variable in a block, let-expression, or comprehension scope. tc * must already be in such a scope. @@ -1889,46 +1924,30 @@ MatchLabel(JSContext *cx, TokenStream *ts, PropertyName **label) static JSBool BindLet(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc) { - ParseNode *pn; - JSObject *blockObj; - jsint n; - - /* - * Body-level 'let' is the same as 'var' currently -- this may change in a - * successor standard to ES5 that specifies 'let'. - */ - JS_ASSERT(!tc->atBodyLevel()); - - pn = data->pn; + ParseNode *pn = data->pn; if (!CheckStrictBinding(cx, tc, atom->asPropertyName(), pn)) return false; - blockObj = tc->blockChain; - Definition *dn = tc->decls.lookupFirst(atom); - if (dn && dn->pn_blockid == tc->blockid()) { - JSAutoByteString name; - if (js_AtomToPrintableString(cx, atom, &name)) { - ReportCompileErrorNumber(cx, TS(tc->parser), pn, - JSREPORT_ERROR, JSMSG_REDECLARED_VAR, - dn->isConst() ? "const" : "variable", - name.ptr()); - } - return false; - } - - n = OBJ_BLOCK_COUNT(cx, blockObj); - if (n == JS_BIT(16)) { + JSObject *blockObj = data->let.blockObj; + uintN blockCount = OBJ_BLOCK_COUNT(cx, blockObj); + if (blockCount == JS_BIT(16)) { ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR, data->let.overflow); return false; } /* - * Pass push = true to Define so it pushes an ale ahead of any outer scope. - * This is balanced by PopStatement, defined immediately below. + * For bindings that are hoisted to the beginning of the block/function, + * Define() right now. For the rest, delay Define() until PushLetScope. */ - if (!Define(pn, atom, tc, true)) - return false; + if (data->let.varContext == HoistVars) { + JS_ASSERT(!tc->atBodyLevel()); + Definition *dn = tc->decls.lookupFirst(atom); + if (dn && dn->pn_blockid == tc->blockid()) + return ReportRedeclaration(cx, tc, pn, dn->isConst(), atom); + if (!Define(pn, atom, tc, true)) + return false; + } /* * Assign block-local index to pn->pn_cookie right away, encoding it as an @@ -1938,47 +1957,61 @@ BindLet(JSContext *cx, BindData *data, JSAtom *atom, TreeContext *tc) * again to include script->nfixed. */ pn->setOp(JSOP_GETLOCAL); - pn->pn_cookie.set(tc->staticLevel, uint16_t(n)); + pn->pn_cookie.set(tc->staticLevel, uint16_t(blockCount)); pn->pn_dflags |= PND_LET | PND_BOUND; /* * Define the let binding's property before storing pn in the the binding's - * slot indexed by n off the class-reserved slot base. + * slot indexed by blockCount off the class-reserved slot base. */ - const Shape *shape = blockObj->defineBlockVariable(cx, ATOM_TO_JSID(atom), n); - if (!shape) + bool redeclared; + jsid id = ATOM_TO_JSID(atom); + const Shape *shape = blockObj->defineBlockVariable(cx, id, blockCount, &redeclared); + if (!shape) { + if (redeclared) + ReportRedeclaration(cx, tc, pn, false, atom); return false; + } /* - * Store pn temporarily in what would be shape-mapped slots in a cloned - * block object (once the prototype's final population is known, after all - * 'let' bindings for this block have been parsed). We free these slots in - * BytecodeEmitter.cpp:EmitEnterBlock so they don't tie up unused space - * in the so-called "static" prototype Block. + * Store pn temporarily in the shape-mapped slots in the static block + * object. This value is clobbered in EmitEnterBlock. */ blockObj->setSlot(shape->slot(), PrivateValue(pn)); return true; } +template +static inline bool +ForEachLetDef(TreeContext *tc, JSObject *blockObj, Op op) +{ + for (Shape::Range r = blockObj->lastProperty()->all(); !r.empty(); r.popFront()) { + const Shape &shape = r.front(); + + /* Beware the destructuring dummy slots. */ + if (JSID_IS_INT(shape.propid())) + continue; + + if (!op(tc, blockObj, shape, JSID_TO_ATOM(shape.propid()))) + return false; + } + return true; +} + +struct RemoveDecl { + bool operator()(TreeContext *tc, JSObject *, const Shape &, JSAtom *atom) { + tc->decls.remove(atom); + return true; + } +}; + static void PopStatement(TreeContext *tc) { - StmtInfo *stmt = tc->topStmt; - - if (stmt->flags & SIF_SCOPE) { - JSObject *obj = stmt->blockObj; - JS_ASSERT(!obj->isClonedBlock()); - - for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) { - JSAtom *atom = JSID_TO_ATOM(r.front().propid()); - - /* Beware the empty destructuring dummy. */ - if (atom == tc->parser->context->runtime->atomState.emptyAtom) - continue; - tc->decls.remove(atom); - } - + if (tc->topStmt->flags & SIF_SCOPE) { + JSObject *obj = tc->topStmt->blockObj; JS_ASSERT(!obj->inDictionaryMode()); + ForEachLetDef(tc, obj, RemoveDecl()); } PopStatementTC(tc); } @@ -2536,9 +2569,13 @@ BindDestructuringLHS(JSContext *cx, ParseNode *pn, TreeContext *tc) * See also UndominateInitializers, immediately below. If you change * either of these functions, you might have to change the other to * match. + * + * The 'toplevel' is a private detail of the recursive strategy used by + * CheckDestructuring and callers should use the default value. */ static bool -CheckDestructuring(JSContext *cx, BindData *data, ParseNode *left, TreeContext *tc) +CheckDestructuring(JSContext *cx, BindData *data, ParseNode *left, TreeContext *tc, + bool toplevel = true) { bool ok; @@ -2548,12 +2585,15 @@ CheckDestructuring(JSContext *cx, BindData *data, ParseNode *left, TreeContext * return false; } + JSObject *blockObj = data && data->binder == BindLet ? data->let.blockObj : NULL; + uint32_t blockCountBefore = blockObj ? OBJ_BLOCK_COUNT(cx, blockObj) : 0; + if (left->isKind(PNK_RB)) { for (ParseNode *pn = left->pn_head; pn; pn = pn->pn_next) { /* Nullary comma is an elision; binary comma is an expression.*/ - if (!pn->isKind(PNK_COMMA) || !pn->isArity(PN_NULLARY)) { + if (!pn->isArrayHole()) { if (pn->isKind(PNK_RB) || pn->isKind(PNK_RC)) { - ok = CheckDestructuring(cx, data, pn, tc); + ok = CheckDestructuring(cx, data, pn, tc, false); } else { if (data) { if (!pn->isKind(PNK_NAME)) { @@ -2577,7 +2617,7 @@ CheckDestructuring(JSContext *cx, BindData *data, ParseNode *left, TreeContext * ParseNode *pn = pair->pn_right; if (pn->isKind(PNK_RB) || pn->isKind(PNK_RC)) { - ok = CheckDestructuring(cx, data, pn, tc); + ok = CheckDestructuring(cx, data, pn, tc, false); } else if (data) { if (!pn->isKind(PNK_NAME)) { ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR, @@ -2603,23 +2643,28 @@ CheckDestructuring(JSContext *cx, BindData *data, ParseNode *left, TreeContext * * let [] = 1; * * would violate this assumption as the there would be no let locals to - * store on the stack. To satisfy it we add an empty property to such - * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of - * slots, would be always positive. + * store on the stack. * - * Note that we add such a property even if the block has locals due to - * later let declarations in it. We optimize for code simplicity here, - * not the fastest runtime performance with empty [] or {}. + * Furthermore, the decompiler needs an abstract stack location to store + * the decompilation of each let block/expr initializer. E.g., given: + * + * let (x = 1, [[]] = b, y = 3, {a:[]} = c) { ... } + * + * four slots are needed. + * + * To satisfy both constraints, we push a dummy slot (and add a + * corresponding dummy property to the block object) for each initializer + * that doesn't introduce at least one binding. */ - if (data && - data->binder == BindLet && - OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0 && - !DefineNativeProperty(cx, tc->blockChain, - ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), - UndefinedValue(), NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT, - Shape::HAS_SHORTID, 0)) { - return false; + if (toplevel && blockObj && blockCountBefore == OBJ_BLOCK_COUNT(cx, blockObj)) { + if (!DefineNativeProperty(cx, blockObj, + INT_TO_JSID(blockCountBefore), + UndefinedValue(), NULL, NULL, + JSPROP_ENUMERATE | JSPROP_PERMANENT, + Shape::HAS_SHORTID, blockCountBefore)) { + return false; + } + JS_ASSERT(OBJ_BLOCK_COUNT(cx, blockObj) == blockCountBefore + 1); } return true; @@ -2764,16 +2809,12 @@ Parser::returnOrYield(bool useAssignExpr) } static ParseNode * -PushLexicalScope(JSContext *cx, TokenStream *ts, TreeContext *tc, StmtInfo *stmt) +PushLexicalScope(JSContext *cx, TreeContext *tc, JSObject *obj, StmtInfo *stmt) { ParseNode *pn = LexicalScopeNode::create(PNK_LEXICALSCOPE, tc); if (!pn) return NULL; - JSObject *obj = js_NewBlockObject(cx); - if (!obj) - return NULL; - ObjectBox *blockbox = tc->parser->newObjectBox(obj); if (!blockbox) return NULL; @@ -2789,36 +2830,85 @@ PushLexicalScope(JSContext *cx, TokenStream *ts, TreeContext *tc, StmtInfo *stmt return pn; } +static ParseNode * +PushLexicalScope(JSContext *cx, TreeContext *tc, StmtInfo *stmt) +{ + JSObject *obj = js_NewBlockObject(cx); + if (!obj) + return NULL; + + return PushLexicalScope(cx, tc, obj, stmt); +} + #if JS_HAS_BLOCK_SCOPE +struct AddDecl +{ + uint32_t blockid; + + AddDecl(uint32_t blockid) : blockid(blockid) {} + + bool operator()(TreeContext *tc, JSObject *blockObj, const Shape &shape, JSAtom *atom) + { + ParseNode *def = (ParseNode *) blockObj->getSlot(shape.slot()).toPrivate(); + def->pn_blockid = blockid; + return Define(def, atom, tc, true); + } +}; + +static ParseNode * +PushLetScope(JSContext *cx, TreeContext *tc, JSObject *blockObj, StmtInfo *stmt) +{ + ParseNode *pn = PushLexicalScope(cx, tc, blockObj, stmt); + if (!pn) + return NULL; + + /* Tell codegen to emit JSOP_ENTERLETx (not JSOP_ENTERBLOCK). */ + pn->pn_dflags |= PND_LET; + + /* Populate the new scope with decls found in the head with updated blockid. */ + if (!ForEachLetDef(tc, blockObj, AddDecl(stmt->blockid))) + return NULL; + + return pn; +} + +/* + * Parse a let block statement or let expression (determined by 'letContext'). + * In both cases, bindings are not hoisted to the top of the enclosing block + * and thus must be carefully injected between variables() and the let body. + */ ParseNode * -Parser::letBlock(JSBool statement) +Parser::letBlock(LetContext letContext) { JS_ASSERT(tokenStream.currentToken().type == TOK_LET); - /* Create the let binary node. */ ParseNode *pnlet = BinaryNode::create(PNK_LET, tc); if (!pnlet) return NULL; + JSObject *blockObj = js_NewBlockObject(context); + if (!blockObj) + return NULL; + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET); - /* This is a let block or expression of the form: let (a, b, c) .... */ - StmtInfo stmtInfo; - ParseNode *pnblock = PushLexicalScope(context, &tokenStream, tc, &stmtInfo); - if (!pnblock) + ParseNode *vars = variables(PNK_LET, blockObj, DontHoistVars); + if (!vars) return NULL; - ParseNode *pn = pnblock; - pn->pn_expr = pnlet; - - pnlet->pn_left = variables(PNK_LP, true); - if (!pnlet->pn_left) - return NULL; - pnlet->pn_left->pn_xflags = PNX_POPVAR; MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET); - if (statement && !tokenStream.matchToken(TOK_LC, TSF_OPERAND)) { + StmtInfo stmtInfo; + ParseNode *block = PushLetScope(context, tc, blockObj, &stmtInfo); + if (!block) + return NULL; + + pnlet->pn_left = vars; + pnlet->pn_right = block; + + ParseNode *ret; + if (letContext == LetStatement && !tokenStream.matchToken(TOK_LC, TSF_OPERAND)) { /* * Strict mode eliminates a grammar ambiguity with unparenthesized * LetExpressions in an ExpressionStatement. If followed immediately @@ -2837,33 +2927,35 @@ Parser::letBlock(JSBool statement) * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop * the return value of the expression. */ - pn = UnaryNode::create(PNK_SEMI, tc); - if (!pn) + ParseNode *semi = UnaryNode::create(PNK_SEMI, tc); + if (!semi) return NULL; - pn->pn_num = -1; - pn->pn_kid = pnblock; - statement = JS_FALSE; + semi->pn_num = -1; + semi->pn_kid = pnlet; + + letContext = LetExpresion; + ret = semi; + } else { + ret = pnlet; } - if (statement) { - pnlet->pn_right = statements(); - if (!pnlet->pn_right) + if (letContext == LetStatement) { + JS_ASSERT(block->getOp() == JSOP_LEAVEBLOCK); + block->pn_expr = statements(); + if (!block->pn_expr) return NULL; MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET); } else { - /* - * Change pnblock's opcode to the variant that propagates the last - * result down after popping the block, and clear statement. - */ - pnblock->setOp(JSOP_LEAVEBLOCKEXPR); - pnlet->pn_right = assignExpr(); - if (!pnlet->pn_right) + JS_ASSERT(letContext == LetExpresion); + block->setOp(JSOP_LEAVEBLOCKEXPR); + block->pn_expr = assignExpr(); + if (!block->pn_expr) return NULL; } PopStatement(tc); - return pn; + return ret; } #endif /* JS_HAS_BLOCK_SCOPE */ @@ -2876,43 +2968,50 @@ PushBlocklikeStatement(StmtInfo *stmt, StmtType type, TreeContext *tc) } static ParseNode * -NewBindingNode(JSAtom *atom, TreeContext *tc, bool let = false) +NewBindingNode(JSAtom *atom, TreeContext *tc, JSObject *blockObj = NULL, + VarContext varContext = HoistVars) { - ParseNode *pn; - AtomDefnPtr removal; + /* + * If this name is being injected into an existing block/function, see if + * it has already been declared or if it resolves an outstanding lexdep. + * Otherwise, this is a let block/expr that introduces a new scope and thus + * shadows existing decls and doesn't resolve existing lexdeps. Duplicate + * names are caught by BindLet. + */ + if (!blockObj || varContext == HoistVars) { + ParseNode *pn = tc->decls.lookupFirst(atom); + AtomDefnPtr removal; + if (pn) { + JS_ASSERT(!pn->isPlaceholder()); + } else { + removal = tc->lexdeps->lookup(atom); + pn = removal ? removal.value() : NULL; + JS_ASSERT_IF(pn, pn->isPlaceholder()); + } - if ((pn = tc->decls.lookupFirst(atom))) { - JS_ASSERT(!pn->isPlaceholder()); - } else { - removal = tc->lexdeps->lookup(atom); - pn = removal ? removal.value() : NULL; - JS_ASSERT_IF(pn, pn->isPlaceholder()); - } + if (pn) { + JS_ASSERT(pn->isDefn()); - if (pn) { - JS_ASSERT(pn->isDefn()); + /* + * A let binding at top level becomes a var before we get here, so if + * pn and tc have the same blockid then that id must not be the bodyid. + * If pn is a forward placeholder definition from the same or a higher + * block then we claim it. + */ + JS_ASSERT_IF(blockObj && pn->pn_blockid == tc->blockid(), + pn->pn_blockid != tc->bodyid); - /* - * A let binding at top level becomes a var before we get here, so if - * pn and tc have the same blockid then that id must not be the bodyid. - * If pn is a forward placeholder definition from the same or a higher - * block then we claim it. - */ - JS_ASSERT_IF(let && pn->pn_blockid == tc->blockid(), - pn->pn_blockid != tc->bodyid); - - if (pn->isPlaceholder() && pn->pn_blockid >= (let ? tc->blockid() : tc->bodyid)) { - if (let) + if (pn->isPlaceholder() && pn->pn_blockid >= tc->blockid()) { pn->pn_blockid = tc->blockid(); - - tc->lexdeps->remove(removal); - return pn; + tc->lexdeps->remove(removal); + return pn; + } } } /* Make a new node for this declarator name (or destructuring pattern). */ JS_ASSERT(tc->parser->tokenStream.currentToken().type == TOK_NAME); - pn = NameNode::create(PNK_NAME, atom, tc); + ParseNode *pn = NameNode::create(PNK_NAME, atom, tc); if (!pn) return NULL; @@ -3043,18 +3142,13 @@ Parser::forStatement() { JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); - ParseNode *pnseq = NULL; -#if JS_HAS_BLOCK_SCOPE - ParseNode *pnlet = NULL; - StmtInfo blockInfo; -#endif - /* A FOR node is binary, left is loop control and right is the body. */ ParseNode *pn = BinaryNode::create(PNK_FOR, tc); if (!pn) return NULL; - StmtInfo stmtInfo; - PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1); + + StmtInfo forStmt; + PushStatement(tc, &forStmt, STMT_FOR_LOOP, -1); pn->setOp(JSOP_ITER); pn->pn_iflags = 0; @@ -3067,16 +3161,15 @@ Parser::forStatement() MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); -#ifdef JS_HAS_BLOCK_SCOPE - bool let = false; -#endif - /* * True if we have 'for (var/let/const ...)', except in the oddball case * where 'let' begins a let-expression in 'for (let (...) ...)'. */ bool forDecl = false; + /* Non-null when forDecl is true for a 'for (let ...)' statement. */ + JSObject *blockObj = NULL; + /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */ ParseNode *pn1; @@ -3107,21 +3200,19 @@ Parser::forStatement() if (tt == TOK_VAR || tt == TOK_CONST) { forDecl = true; tokenStream.consumeKnownToken(tt); - pn1 = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST, false); + pn1 = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST); } #if JS_HAS_BLOCK_SCOPE else if (tt == TOK_LET) { - let = true; (void) tokenStream.getToken(); if (tokenStream.peekToken() == TOK_LP) { - pn1 = letBlock(JS_FALSE); + pn1 = letBlock(LetExpresion); } else { forDecl = true; - pnlet = PushLexicalScope(context, &tokenStream, tc, &blockInfo); - if (!pnlet) + blockObj = js_NewBlockObject(context); + if (!blockObj) return NULL; - blockInfo.flags |= SIF_FOR_BLOCK; - pn1 = variables(PNK_LET, false); + pn1 = variables(PNK_LET, blockObj, DontHoistVars); } } #endif @@ -3134,14 +3225,23 @@ Parser::forStatement() } } + JS_ASSERT_IF(forDecl, pn1->isArity(PN_LIST)); + JS_ASSERT(!!blockObj == (forDecl && pn1->isOp(JSOP_NOP))); + + const TokenPos pos = tokenStream.currentToken().pos; + + /* If non-null, the parent that should be returned instead of forHead. */ + ParseNode *forParent = NULL; + /* * We can be sure that it's a for/in loop if there's still an 'in' * keyword here, even if JavaScript recognizes 'in' as an operator, * as we've excluded 'in' from being parsed in RelExpr by setting * the TCF_IN_FOR_INIT flag in our TreeContext. */ - TokenPos pos = tokenStream.currentToken().pos; - ParseNode *pn2, *pn3, *pn4; + ParseNode *forHead; /* initialized by both branches. */ + StmtInfo letStmt; /* used if blockObj != NULL. */ + ParseNode *pn2, *pn3; /* forHead->pn_kid1 and pn_kid2. */ if (pn1 && tokenStream.matchToken(TOK_IN)) { /* * Parse the rest of the for/in head. @@ -3151,7 +3251,7 @@ Parser::forStatement() * enumeration value each iteration, and pn3 is the rhs of 'in'. */ pn->pn_iflags |= JSITER_ENUMERATE; - stmtInfo.type = STMT_FOR_IN_LOOP; + forStmt.type = STMT_FOR_IN_LOOP; /* Check that the left side of the 'in' is valid. */ if (forDecl @@ -3214,16 +3314,15 @@ Parser::forStatement() * the loop head. */ #if JS_HAS_BLOCK_SCOPE - if (let) { + if (blockObj) { reportErrorNumber(pn2, JSREPORT_ERROR, JSMSG_INVALID_FOR_IN_INIT); return NULL; } #endif /* JS_HAS_BLOCK_SCOPE */ - pnseq = ListNode::create(PNK_SEQ, tc); + ParseNode *pnseq = ListNode::create(PNK_SEQ, tc); if (!pnseq) return NULL; - pnseq->pn_pos.begin = pn->pn_pos.begin; dflag = PND_INITIALIZED; @@ -3238,6 +3337,7 @@ Parser::forStatement() pn1->pn_xflags &= ~PNX_FORINVAR; pn1->pn_xflags |= PNX_POPVAR; pnseq->initList(pn1); + pn1 = NULL; #if JS_HAS_DESTRUCTURING if (pn2->isKind(PNK_ASSIGN)) { @@ -3246,18 +3346,12 @@ Parser::forStatement() pn2->isKind(PNK_NAME)); } #endif - pn1 = NULL; + pnseq->append(pn); + forParent = pnseq; } - - /* - * pn2 is part of a declaration. Make a copy that can be passed to - * EmitAssignment. - */ - pn2 = CloneLeftHandSide(pn2, tc); - if (!pn2) - return NULL; } else { /* Not a declaration. */ + JS_ASSERT(!blockObj); pn2 = pn1; pn1 = NULL; @@ -3265,6 +3359,37 @@ Parser::forStatement() return NULL; } + pn3 = expr(); + if (!pn3) + return NULL; + + if (blockObj) { + /* + * Now that the pn3 has been parsed, push the let scope. To hold + * the blockObj for the emitter, wrap the TOK_LEXICALSCOPE node + * created by PushLetScope around the for's initializer. This also + * serves to indicate the let-decl to the emitter. + */ + ParseNode *block = PushLetScope(context, tc, blockObj, &letStmt); + if (!block) + return NULL; + letStmt.flags |= SIF_FOR_BLOCK; + block->pn_expr = pn1; + pn1 = block; + } + + if (forDecl) { + /* + * pn2 is part of a declaration. Make a copy that can be passed to + * EmitAssignment. Take care to do this after PushLetScope has + * Define's the new binding since this pn2->isDefn() which tells + * CloneLeftHandSide to make the new pn2 a use. + */ + pn2 = CloneLeftHandSide(pn2, tc); + if (!pn2) + return NULL; + } + switch (pn2->getKind()) { case PNK_NAME: /* Beware 'for (arguments in ...)' with or without a 'var'. */ @@ -3293,28 +3418,29 @@ Parser::forStatement() default:; } - /* - * Parse the object expression as the right operand of 'in', first - * removing the top statement from the statement-stack if this is a - * 'for (let x in y)' loop. - */ -#if JS_HAS_BLOCK_SCOPE - StmtInfo *save = tc->topStmt; - if (let) - tc->topStmt = save->down; -#endif - pn3 = expr(); - if (!pn3) - return NULL; -#if JS_HAS_BLOCK_SCOPE - if (let) - tc->topStmt = save; -#endif - - pn4 = TernaryNode::create(PNK_FORIN, tc); - if (!pn4) + forHead = TernaryNode::create(PNK_FORIN, tc); + if (!forHead) return NULL; } else { + if (blockObj) { + /* + * Desugar 'for (let A; B; C) D' into 'let (A) { for (; B; C) D }' + * to induce the correct scoping for A. + */ + ParseNode *block = PushLetScope(context, tc, blockObj, &letStmt); + if (!block) + return NULL; + letStmt.flags |= SIF_FOR_BLOCK; + + ParseNode *let = new_(PNK_LET, JSOP_NOP, pos, pn1, block); + if (!let) + return NULL; + + pn1 = NULL; + block->pn_expr = pn; + forParent = let; + } + if (pn->pn_iflags & JSITER_FOREACH) { reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP); return NULL; @@ -3323,8 +3449,7 @@ Parser::forStatement() /* Parse the loop condition or null into pn2. */ MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); - TokenKind tt = tokenStream.peekToken(TSF_OPERAND); - if (tt == TOK_SEMI) { + if (tokenStream.peekToken(TSF_OPERAND) == TOK_SEMI) { pn2 = NULL; } else { pn2 = expr(); @@ -3334,8 +3459,7 @@ Parser::forStatement() /* Parse the update expression or null into pn3. */ MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND); - tt = tokenStream.peekToken(TSF_OPERAND); - if (tt == TOK_RP) { + if (tokenStream.peekToken(TSF_OPERAND) == TOK_RP) { pn3 = NULL; } else { pn3 = expr(); @@ -3343,42 +3467,40 @@ Parser::forStatement() return NULL; } - pn4 = TernaryNode::create(PNK_FORHEAD, tc); - if (!pn4) + forHead = TernaryNode::create(PNK_FORHEAD, tc); + if (!forHead) return NULL; } - pn4->pn_pos = pos; - pn4->setOp(JSOP_NOP); - pn4->pn_kid1 = pn1; - pn4->pn_kid2 = pn2; - pn4->pn_kid3 = pn3; - pn->pn_left = pn4; + + forHead->pn_pos = pos; + forHead->setOp(JSOP_NOP); + forHead->pn_kid1 = pn1; + forHead->pn_kid2 = pn2; + forHead->pn_kid3 = pn3; + pn->pn_left = forHead; MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); - /* Parse the loop body into pn->pn_right. */ - pn2 = statement(); - if (!pn2) + /* Parse the loop body. */ + ParseNode *body = statement(); + if (!body) return NULL; - pn->pn_right = pn2; /* Record the absolute line number for source note emission. */ - pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_pos.end = body->pn_pos.end; + pn->pn_right = body; + + if (forParent) { + forParent->pn_pos.begin = pn->pn_pos.begin; + forParent->pn_pos.end = pn->pn_pos.end; + } #if JS_HAS_BLOCK_SCOPE - if (pnlet) { + if (blockObj) PopStatement(tc); - pnlet->pn_expr = pn; - pn = pnlet; - } #endif - if (pnseq) { - pnseq->pn_pos.end = pn->pn_pos.end; - pnseq->append(pn); - pn = pnseq; - } PopStatement(tc); - return pn; + return forParent ? forParent : pn; } ParseNode * @@ -3442,7 +3564,7 @@ Parser::tryStatement() * Create a lexical scope node around the whole catch clause, * including the head. */ - pnblock = PushLexicalScope(context, &tokenStream, tc, &stmtInfo); + pnblock = PushLexicalScope(context, tc, &stmtInfo); if (!pnblock) return NULL; stmtInfo.type = STMT_CATCH; @@ -3465,10 +3587,8 @@ Parser::tryStatement() * scoped, not a property of a new Object instance. This is * an intentional change that anticipates ECMA Ed. 4. */ - data.pn = NULL; - data.op = JSOP_NOP; - data.binder = BindLet; - data.let.overflow = JSMSG_TOO_MANY_CATCH_VARS; + data.initLet(HoistVars, tc->blockChain, JSMSG_TOO_MANY_CATCH_VARS); + JS_ASSERT(data.let.blockObj && data.let.blockObj == pnblock->pn_objbox->object); tt = tokenStream.getToken(); ParseNode *pn3; @@ -3485,7 +3605,7 @@ Parser::tryStatement() case TOK_NAME: { JSAtom *label = tokenStream.currentToken().name(); - pn3 = NewBindingNode(label, tc, true); + pn3 = NewBindingNode(label, tc); if (!pn3) return NULL; data.pn = pn3; @@ -3611,12 +3731,16 @@ Parser::letStatement() do { /* Check for a let statement or let expression. */ if (tokenStream.peekToken() == TOK_LP) { - pn = letBlock(JS_TRUE); - if (!pn || pn->isOp(JSOP_LEAVEBLOCK)) + pn = letBlock(LetStatement); + if (!pn) + return NULL; + + JS_ASSERT(pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI)); + if (pn->isKind(PNK_LET) && pn->pn_expr->getOp() == JSOP_LEAVEBLOCK) return pn; /* Let expressions require automatic semicolon insertion. */ - JS_ASSERT(pn->isKind(PNK_SEMI) || pn->isOp(JSOP_LEAVEBLOCKEXPR)); + JS_ASSERT(pn->isKind(PNK_SEMI) || pn->isOp(JSOP_NOP)); break; } @@ -3646,7 +3770,7 @@ Parser::letStatement() * ES4 specifies that let at top level and at body-block scope * does not shadow var, so convert back to var. */ - pn = variables(PNK_VAR, false); + pn = variables(PNK_VAR); if (!pn) return NULL; pn->pn_xflags |= PNX_POPVAR; @@ -3707,7 +3831,7 @@ Parser::letStatement() tc->blockNode = pn1; } - pn = variables(PNK_LET, false); + pn = variables(PNK_LET, tc->blockChain, HoistVars); if (!pn) return NULL; pn->pn_xflags = PNX_POPVAR; @@ -4039,7 +4163,7 @@ Parser::statement() return withStatement(); case TOK_VAR: - pn = variables(PNK_VAR, false); + pn = variables(PNK_VAR); if (!pn) return NULL; @@ -4048,7 +4172,7 @@ Parser::statement() break; case TOK_CONST: - pn = variables(PNK_CONST, false); + pn = variables(PNK_CONST); if (!pn) return NULL; @@ -4152,8 +4276,13 @@ Parser::statement() return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL; } +/* + * The 'blockObj' parameter is non-null when parsing the 'vars' in a let + * expression, block statement, non-top-level let declaration in statement + * context, and the let-initializer of a for-statement. + */ ParseNode * -Parser::variables(ParseNodeKind kind, bool inLetHead) +Parser::variables(ParseNodeKind kind, JSObject *blockObj, VarContext varContext) { /* * The four options here are: @@ -4164,29 +4293,11 @@ Parser::variables(ParseNodeKind kind, bool inLetHead) */ JS_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET || kind == PNK_LP); - bool let = (kind == PNK_LET || kind == PNK_LP); - -#if JS_HAS_BLOCK_SCOPE - bool popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT))); - StmtInfo *save = tc->topStmt, *saveScope = tc->topScopeStmt; -#endif - - /* Make sure that statement set up the tree context correctly. */ - StmtInfo *scopeStmt = tc->topScopeStmt; - if (let) { - while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) { - JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt)); - scopeStmt = scopeStmt->downScope; - } - JS_ASSERT(scopeStmt); - } - - BindData data; - data.op = let ? JSOP_NOP : kind == PNK_VAR ? JSOP_DEFVAR : JSOP_DEFCONST; ParseNode *pn = ListNode::create(kind, tc); if (!pn) return NULL; - pn->setOp(data.op); + + pn->setOp(blockObj ? JSOP_NOP : kind == PNK_VAR ? JSOP_DEFVAR : JSOP_DEFCONST); pn->makeEmpty(); /* @@ -4194,13 +4305,11 @@ Parser::variables(ParseNodeKind kind, bool inLetHead) * var, whereas let is block scoped. ES-Harmony wants block-scoped const so * this code will change soon. */ - if (let) { - JS_ASSERT(tc->blockChain == scopeStmt->blockObj); - data.binder = BindLet; - data.let.overflow = JSMSG_TOO_MANY_LOCALS; - } else { - data.binder = BindVarOrConst; - } + BindData data; + if (blockObj) + data.initLet(varContext, blockObj, JSMSG_TOO_MANY_LOCALS); + else + data.initVarOrConst(pn->getOp()); ParseNode *pn2; do { @@ -4223,20 +4332,7 @@ Parser::variables(ParseNodeKind kind, bool inLetHead) MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL); JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NOP); -#if JS_HAS_BLOCK_SCOPE - if (popScope) { - tc->topStmt = save->down; - tc->topScopeStmt = saveScope->downScope; - } -#endif ParseNode *init = assignExpr(); -#if JS_HAS_BLOCK_SCOPE - if (popScope) { - tc->topStmt = save; - tc->topScopeStmt = saveScope; - } -#endif - if (!init) return NULL; UndominateInitializers(pn2, init->pn_pos.end, tc); @@ -4256,7 +4352,7 @@ Parser::variables(ParseNodeKind kind, bool inLetHead) } PropertyName *name = tokenStream.currentToken().name(); - pn2 = NewBindingNode(name, tc, let); + pn2 = NewBindingNode(name, tc, blockObj, varContext); if (!pn2) return NULL; if (data.op == JSOP_DEFCONST) @@ -4269,19 +4365,7 @@ Parser::variables(ParseNodeKind kind, bool inLetHead) if (tokenStream.matchToken(TOK_ASSIGN)) { JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NOP); -#if JS_HAS_BLOCK_SCOPE - if (popScope) { - tc->topStmt = save->down; - tc->topScopeStmt = saveScope->downScope; - } -#endif ParseNode *init = assignExpr(); -#if JS_HAS_BLOCK_SCOPE - if (popScope) { - tc->topStmt = save; - tc->topScopeStmt = saveScope; - } -#endif if (!init) return NULL; @@ -4310,7 +4394,7 @@ Parser::variables(ParseNodeKind kind, bool inLetHead) if (tc->inFunction() && name == context->runtime->atomState.argumentsAtom) { tc->noteArgumentsUse(pn2); - if (!let) + if (!blockObj) tc->flags |= TCF_FUN_HEAVYWEIGHT; } } @@ -5254,7 +5338,7 @@ Parser::comprehensionTail(ParseNode *kid, uintN blockid, bool isGenexp, * yields the next value from a for-in loop (possibly nested, and with * optional if guard). Make pn be the TOK_LC body node. */ - pn = PushLexicalScope(context, &tokenStream, tc, &stmtInfo); + pn = PushLexicalScope(context, tc, &stmtInfo); if (!pn) return NULL; adjust = pn->pn_blockid - blockid; @@ -5274,7 +5358,7 @@ Parser::comprehensionTail(ParseNode *kid, uintN blockid, bool isGenexp, * block scope. */ adjust = tc->blockid(); - pn = PushLexicalScope(context, &tokenStream, tc, &stmtInfo); + pn = PushLexicalScope(context, tc, &stmtInfo); if (!pn) return NULL; @@ -5291,10 +5375,8 @@ Parser::comprehensionTail(ParseNode *kid, uintN blockid, bool isGenexp, CompExprTransplanter transplanter(kid, tc, kind == PNK_SEMI, adjust); transplanter.transplant(kid); - data.pn = NULL; - data.op = JSOP_NOP; - data.binder = BindLet; - data.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG; + JS_ASSERT(tc->blockChain && tc->blockChain == pn->pn_objbox->object); + data.initLet(HoistVars, tc->blockChain, JSMSG_ARRAY_INIT_TOO_BIG); do { /* @@ -5342,7 +5424,7 @@ Parser::comprehensionTail(ParseNode *kid, uintN blockid, bool isGenexp, * and it tries to bind all names to slots, so we must let it do * the deed. */ - pn3 = NewBindingNode(name, tc, true); + pn3 = NewBindingNode(name, tc); if (!pn3) return NULL; break; @@ -6495,40 +6577,6 @@ Parser::parseXMLText(JSObject *chain, bool allowList) #endif /* JS_HAS_XMLSUPPORT */ -#if JS_HAS_BLOCK_SCOPE -/* - * Check whether blockid is an active scoping statement in tc. This code is - * necessary to qualify tc->decls.lookup() hits in primaryExpr's TOK_NAME case - * (below) where the hits come from Scheme-ish let bindings in for loop heads - * and let blocks and expressions (not let declarations). - * - * Unlike let declarations ("let as the new var"), which is a kind of letrec - * due to hoisting, let in a for loop head, let block, or let expression acts - * like Scheme's let: initializers are evaluated without the new let bindings - * being in scope. - * - * Name binding analysis is eager with fixups, rather than multi-pass, and let - * bindings push on the front of the tc->decls AtomDecls (either the singular - * list or on a hash chain -- see JSAtomMultiList::add*) in order to shadow - * outer scope bindings of the same name. - * - * This simplifies binding lookup code at the price of a linear search here, - * but only if code uses let (var predominates), and even then this function's - * loop iterates more than once only in crazy cases. - */ -static inline bool -BlockIdInScope(uintN blockid, TreeContext *tc) -{ - if (blockid > tc->blockid()) - return false; - for (StmtInfo *stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) { - if (stmt->blockid == blockid) - return true; - } - return false; -} -#endif - static ParseNode * PrimaryExprNode(ParseNodeKind kind, JSOp op, TreeContext *tc) { @@ -6891,7 +6939,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) #if JS_HAS_BLOCK_SCOPE case TOK_LET: - pn = letBlock(JS_FALSE); + pn = letBlock(LetExpresion); if (!pn) return NULL; break; @@ -7048,26 +7096,8 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) StmtInfo *stmt = LexicalLookup(tc, pn->pn_atom, NULL); MultiDeclRange mdl = tc->decls.lookupMulti(pn->pn_atom); + Definition *dn; - - if (!mdl.empty()) { - dn = mdl.front(); -#if JS_HAS_BLOCK_SCOPE - /* - * Skip out-of-scope let bindings along an ALE list or hash - * chain. These can happen due to |let (x = x) x| block and - * expression bindings, where the x on the right of = comes - * from an outer scope. See bug 496532. - */ - while (dn->isLet() && !BlockIdInScope(dn->pn_blockid, tc)) { - mdl.popFront(); - if (mdl.empty()) - break; - dn = mdl.front(); - } -#endif - } - if (!mdl.empty()) { dn = mdl.front(); } else { diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 19f0ea7ba11a..03a296588d64 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -61,6 +61,8 @@ typedef struct BindData BindData; namespace js { enum FunctionSyntaxKind { Expression, Statement }; +enum LetContext { LetExpresion, LetStatement }; +enum VarContext { HoistVars, DontHoistVars }; struct Parser : private AutoGCRooter { @@ -196,7 +198,7 @@ struct Parser : private AutoGCRooter ParseNode *letStatement(); #endif ParseNode *expressionStatement(); - ParseNode *variables(ParseNodeKind kind, bool inLetHead); + ParseNode *variables(ParseNodeKind kind, JSObject *blockObj = NULL, VarContext varContext = HoistVars); ParseNode *expr(); ParseNode *assignExpr(); ParseNode *condExpr1(); @@ -240,7 +242,7 @@ struct Parser : private AutoGCRooter ParseNode *generatorExpr(ParseNode *kid); JSBool argumentList(ParseNode *listNode); ParseNode *bracketedExpr(); - ParseNode *letBlock(JSBool statement); + ParseNode *letBlock(LetContext letContext); ParseNode *returnOrYield(bool useAssignExpr); ParseNode *destructuringExpr(BindData *data, TokenKind tt); diff --git a/js/src/jit-test/tests/basic/bug657975.js b/js/src/jit-test/tests/basic/bug657975.js index 534785643411..f3046ff5ac5f 100644 --- a/js/src/jit-test/tests/basic/bug657975.js +++ b/js/src/jit-test/tests/basic/bug657975.js @@ -60,7 +60,7 @@ f9 = (function() { for each(let w in []) {} } }) -trap(f9, 22, undefined); +trap(f9, 23, undefined); for (b in f9()) (function() {})() diff --git a/js/src/jit-test/tests/basic/testBug666292.js b/js/src/jit-test/tests/basic/testBug666292.js index ed0e8fd8dc3c..12238e75ca35 100644 --- a/js/src/jit-test/tests/basic/testBug666292.js +++ b/js/src/jit-test/tests/basic/testBug666292.js @@ -4,7 +4,7 @@ function f(){ this.zzz.zzz; for(let d in []); } -trap(f, 18, '') +trap(f, 16, '') try { f() } catch(e) { diff --git a/js/src/jit-test/tests/basic/testBug683470.js b/js/src/jit-test/tests/basic/testBug683470.js index 83b0945a1b60..e6e27346bea5 100644 --- a/js/src/jit-test/tests/basic/testBug683470.js +++ b/js/src/jit-test/tests/basic/testBug683470.js @@ -11,5 +11,5 @@ f = (function() { } catch (e) {} } }) -trap(f, 39, undefined); +trap(f, 40, undefined); f() diff --git a/js/src/jit-test/tests/basic/testBug692274-1.js b/js/src/jit-test/tests/basic/testBug692274-1.js new file mode 100644 index 000000000000..953a1cbda8aa --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug692274-1.js @@ -0,0 +1,6 @@ +// |jit-test| debug; error: TypeError +function f() { + ""(this.z) +} +trap(f, 0, '') +f() diff --git a/js/src/jit-test/tests/basic/testBug692274-2.js b/js/src/jit-test/tests/basic/testBug692274-2.js new file mode 100644 index 000000000000..dff5c905236b --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug692274-2.js @@ -0,0 +1,7 @@ +function f() { + var ss = [new f("abc"), new String("foobar"), new String("quux")]; + for (let a6 = this ;; ) {} +} +try { + f(); +} catch (e) {} diff --git a/js/src/jit-test/tests/basic/testBug692274-3.js b/js/src/jit-test/tests/basic/testBug692274-3.js new file mode 100644 index 000000000000..a8c0afe65f03 --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug692274-3.js @@ -0,0 +1,16 @@ +var x = -false; +switch(x) { + case 11: + let y = 42; +} +switch(x) { + case 11: + let y = 42; + let z = 'ponies'; +} +switch(x) { + case 11: + let y = 42; + let z = 'ponies'; + let a = false; +} diff --git a/js/src/jit-test/tests/basic/testBug692274-4.js b/js/src/jit-test/tests/basic/testBug692274-4.js new file mode 100644 index 000000000000..8c02cdc11380 --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug692274-4.js @@ -0,0 +1,4 @@ +// |jit-test| error: TypeError +var obj = {}; +let ([] = print) 3; +let ( i = "a" ) new i [ obj[i] ]; diff --git a/js/src/jit-test/tests/basic/testBug703857.js b/js/src/jit-test/tests/basic/testBug703857.js new file mode 100644 index 000000000000..0f230e461ab6 --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug703857.js @@ -0,0 +1,12 @@ +Function.prototype.X = 42; +function ownProperties() { + var props = {}; + var r = function () {}; + for (var a in r) { + let (a = function() { for (var r=0;r<6;++r) ++a; }) { + a(); + } + props[a] = true; + } +} +ownProperties(); diff --git a/js/src/jit-test/tests/basic/testBug709633.js b/js/src/jit-test/tests/basic/testBug709633.js new file mode 100644 index 000000000000..1ce097b85d46 --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug709633.js @@ -0,0 +1,9 @@ +test(); +function test() { + var f; + f = function() { (let(x) {y: z}) } + let (f = function() { + for (var t=0;t<6;++t) ++f; + }) { f(); } // { } + actual = f + ''; +} diff --git a/js/src/jit-test/tests/basic/testLet.js b/js/src/jit-test/tests/basic/testLet.js new file mode 100644 index 000000000000..eb9860e7cccd --- /dev/null +++ b/js/src/jit-test/tests/basic/testLet.js @@ -0,0 +1,342 @@ +function test(str, arg, result) +{ + arg = arg || 'ponies'; + result = result || 'ponies'; + + var fun = new Function('x', str); + + var got = fun.toSource().replace(/\n/g,''); + var expect = '(function anonymous(x) {' + str + '})'; + if (got !== expect) { + print("GOT: " + got); + print("EXPECT: " + expect); + assertEq(got, expect); + } + + Reflect.parse(got); + + var got = fun(arg); + var expect = result; + if (got !== expect) { + print("GOT:" + got); + print("EXPECT: " + expect); + assertEq(got, expect); + } +} + +function isError(str) +{ + var caught = false; + try { + new Function(str); + } catch(e) { + assertEq(String(e).indexOf('TypeError') == 0 || String(e).indexOf('SyntaxError') == 0, true); + caught = true; + } + assertEq(caught, true); +} + +// let expr +test('return let (y) x;'); +test('return let (x) "" + x;', 'unicorns', 'undefined'); +test('return let (y = x) (y++, "" + y);', 'unicorns', 'NaN'); +test('return let (y = 1) (y = x, y);'); +test('return let ([] = x) x;'); +test('return let (x = {a: x}) x.a;'); +test('return let ({a: x} = {a: x}) x;'); +test('return let ([x] = {0: x}) x;'); +test('return let ({0: x} = [x]) x;'); +test('return let ({0: []} = []) x;'); +test('return let ([, ] = x) x;'); +test('return let ([, , , , ] = x) x;'); +test('return let ([[]] = x) x;'); +test('return let ([[[[[[[[[[[[[]]]]]]]]]]]]] = x) x;'); +test('return let ([[], []] = x) x;'); +test('return let ([[[[]]], [], , [], [[]]] = x) x;'); +test('return let ({x: []} = x) x;'); +test('return let ({x: [], y: {x: []}} = x) "ponies";', {y:{}}); +test('return let ({x: []} = x, [{x: []}] = x) "ponies";'); +test('return let (x = x) x;'); +test('return let (x = eval("x")) x;'); +test('return let (x = (let (x = x + 1) x) + 1) x;', 1, 3); +test('return let (x = (let (x = eval("x") + 1) eval("x")) + 1) eval("x");', 1, 3); +test('return let (x = x + 1, y = x) y;'); +test('return let (x = x + 1, [] = x, [[, , ]] = x, y = x) y;'); +test('return let ([{a: x}] = x, [, {b: y}] = x) let (x = x + 1, y = y + 2) x + y;', [{a:"p"},{b:"p"}], "p1p2"); +test('return let ([] = []) x;'); +test('return let ([] = [x]) x;'); +test('return let ([x] = [x]) x;'); +test('return let ([[a, [b, c]]] = [[x, []]]) a;'); +test('return let ([x, y] = [x, x + 1]) x + y;', 1, 3); +test('return let ([x, y, z] = [x, x + 1, x + 2]) x + y + z;', 1, 6); +test('return let ([[x]] = [[x]]) x;'); +test('return let ([x, y] = [x, x + 1]) x;'); +test('return let ([x, [y, z]] = [x, x + 1]) x;'); +test('return let ([{x: [x]}, {y1: y, z1: z}] = [x, x + 1]) x;',{x:['ponies']}); +test('return let (x = (3, x)) x;'); +test('return let (x = x + "s") x;', 'ponie'); +test('return let ([x] = (3, [x])) x;'); +test('return let ([] = [[]] = {}) x;'); +test('return let (y = x) function () {return eval("y");}();'); +test('return eval("let (y = x) y");'); +test('return let (y = x) (eval("var y = 2"), y);', 'ponies', 2); +test('"use strict";return let (y = x) (eval("var y = 2"), y);'); +test('this.y = x;return let (y = 1) this.eval("y");'); +test('try {let (x = x) eval("throw x");} catch (e) {return e;}'); +test('try {return let (x = eval("throw x")) x;} catch (e) {return e;}'); +isError('let (x = 1, x = 2) x'); +isError('let ([x, y] = a, {a:x} = b) x'); +isError('let ([x, y, x] = a) x'); +isError('let ([x, [y, [x]]] = a) x'); +isError('let (x = function() { return x}) x()return x;'); +isError('(let (x = function() { return x}) x())return x;'); + +// let block +test('let (y) {return x;}'); +test('let (y = x) {y++;return "" + y;}', 'unicorns', 'NaN'); +test('let (y = 1) {y = x;return y;}'); +test('let (x) {return "" + x;}', 'unicorns', 'undefined'); +test('let ([] = x) {return x;}'); +test('let (x) {}return x;'); +test('let (x = {a: x}) {return x.a;}'); +test('let ({a: x} = {a: x}) {return x;}'); +test('let ([x] = {0: x}) {return x;}'); +test('let ({0: x} = [x]) {return x;}'); +test('let ({0: []} = []) {return x;}'); +test('let ([, ] = x) {return x;}'); +test('let ([, , , , ] = x) {return x;}'); +test('let ([[]] = x) {return x;}'); +test('let ([[[[[[[[[[[[[]]]]]]]]]]]]] = x) {return x;}'); +test('let ([[], []] = x) {return x;}'); +test('let ([[[[]]], [], , [], [[]]] = x) {return x;}'); +test('let ({x: []} = x) {return x;}'); +test('let ({x: [], y: {x: []}} = x) {return "ponies";}', {y:{}}); +test('let ({x: []} = x, [{x: []}] = x) {return "ponies";}'); +test('let (x = x) {return x;}'); +test('let (x = eval("x")) {return x;}'); +test('let (x = (let (x = x + 1) x) + 1) {return x;}', 1, 3); +test('let (x = (let (x = eval("x") + 1) eval("x")) + 1) {return eval("x");}', 1, 3); +test('let (x = x + 1, y = x) {return y;}'); +test('let (x = x + 1, [] = x, [[, , ]] = x, y = x) {return y;}'); +test('let ([{a: x}] = x, [, {b: y}] = x) {let (x = x + 1, y = y + 2) {return x + y;}}', [{a:"p"},{b:"p"}], "p1p2"); +test('let ([] = []) {return x;}'); +test('let ([] = [x]) {return x;}'); +test('let ([x] = [x]) {return x;}'); +test('let ([[a, [b, c]]] = [[x, []]]) {return a;}'); +test('let ([x, y] = [x, x + 1]) {return x + y;}', 1, 3); +test('let ([x, y, z] = [x, x + 1, x + 2]) {return x + y + z;}', 1, 6); +test('let ([[x]] = [[x]]) {return x;}'); +test('let ([x, y] = [x, x + 1]) {return x;}'); +test('let ([x, [y, z]] = [x, x + 1]) {return x;}'); +test('let ([{x: [x]}, {y1: y, z1: z}] = [x, x + 1]) {return x;}',{x:['ponies']}); +test('let (y = x[1]) {let (x = x[0]) {try {let (y = "unicorns") {throw y;}} catch (e) {return x + y;}}}', ['pon','ies']); +test('let (x = x) {try {let (x = "unicorns") eval("throw x");} catch (e) {return x;}}'); +test('let ([] = [[]] = {}) {return x;}'); +test('let (y = x) {return function () {return eval("y");}();}'); +test('return eval("let (y = x) {y;}");'); +test('let (y = x) {eval("var y = 2");return y;}', 'ponies', 2); +test('"use strict";let (y = x) {eval("var y = 2");return y;}'); +test('this.y = x;let (y = 1) {return this.eval("y");}'); +isError('let (x = 1, x = 2) {x}'); +isError('let ([x, y] = a, {a:x} = b) {x}'); +isError('let ([x, y, x] = a) {x}'); +isError('let ([x, [y, [x]]] = a) {x}'); + +// var declarations +test('var y;return x;'); +test('var y = x;return x;'); +test('var [] = x;return x;'); +test('var [, ] = x;return x;'); +test('var [, , , , ] = x;return x;'); +test('var [[]] = x;return x;'); +test('var [[[[[[[[[[[[[]]]]]]]]]]]]] = x;return x;'); +test('var [[], []] = x;return x;'); +test('var [[[[]]], [], , [], [[]]] = x;return x;'); +test('var {x: []} = x;return x;'); +test('var {x: [], y: {x: []}} = x;return "ponies";', {y:{}}); +test('var {x: []} = x, [{x: []}] = x;return "ponies";'); +test('var x = x;return x;'); +test('var y = y;return "" + y;', 'unicorns', 'undefined'); +test('var x = eval("x");return x;'); +test('var x = (let (x = x + 1) x) + 1;return x;', 1, 3); +test('var x = (let (x = eval("x") + 1) eval("x")) + 1;return eval("x");', 1, 3); +test('var X = x + 1, y = x;return y;'); +test('var X = x + 1, [] = X, [[, , ]] = X, y = x;return y;'); +test('var [{a: X}] = x, [, {b: y}] = x;var X = X + 1, y = y + 2;return X + y;', [{a:"p"},{b:"p"}], "p1p2"); +test('var [x] = [x];return x;'); +test('var [[a, [b, c]]] = [[x, []]];return a;'); +test('var [y] = [x];return y;'); +test('var [x, y] = [x, x + 1];return x + y;', 1, 3); +test('var [x, y, z] = [x, x + 1, x + 2];return x + y + z;', 1, 6); +test('var [[x]] = [[x]];return x;'); +test('var [x, y] = [x, x + 1];return x;'); +test('var [x, [y, z]] = [x, x + 1];return x;'); +test('var [{x: [x]}, {y1: y, z1: z}] = [x, x + 1];return x;',{x:['ponies']}); +test('var [] = [[]] = {};return x;'); +test('if (x) {var y = x;return x;}'); +test('if (x) {y = x;var y = y;return y;}'); +test('if (x) {var z = y;var [y] = x;z += y;}return z;', ['-'], 'undefined-'); + +// let declaration in context +test('if (x) {let y;return x;}'); +test('if (x) {let x;return "" + x;}', 'unicorns', 'undefined'); +test('if (x) {let y = x;return x;}'); +test('if (x) {y = x;let y = y;return y;}'); +test('if (x) {var z = y;let [y] = x;z += y;}return z;', ['-'], 'undefined-'); +test('if (x) {let y = x;return x;}'); +test('if (x) {let [] = x;return x;}'); +test('if (x) {let [, ] = x;return x;}'); +test('if (x) {let [, , , , ] = x;return x;}'); +test('if (x) {let [[]] = x;return x;}'); +test('if (x) {let [[[[[[[[[[[[[]]]]]]]]]]]]] = x;return x;}'); +test('if (x) {let [[], []] = x;return x;}'); +test('if (x) {let [[[[]]], [], , [], [[]]] = x;return x;}'); +test('if (x) {let {x: []} = x;return x;}'); +test('if (x) {let {x: [], y: {x: []}} = x;return "ponies";}', {y:{}}); +test('if (x) {let {x: []} = x, [{x: []}] = x;return "ponies";}'); +test('if (x) {let x = x;return "" + x;}', 'unicorns', 'undefined'); +test('if (x) {let y = y;return "" + y;}', 'unicorns', 'undefined'); +test('if (x) {let x = eval("x");return "" + x;}', 'unicorns', 'undefined'); +test('if (x) {let y = (let (x = x + 1) x) + 1;return y;}', 1, 3); +test('if (x) {let y = (let (x = eval("x") + 1) eval("x")) + 1;return eval("y");}', 1, 3); +test('if (x) {let X = x + 1, y = x;return y;}'); +test('if (x) {let X = x + 1, [] = X, [[, , ]] = X, y = x;return y;}'); +test('if (x) {let [{a: X}] = x, [, {b: Y}] = x;var XX = X + 1, YY = Y + 2;return XX + YY;}', [{a:"p"},{b:"p"}], "p1p2"); +test('if (x) {let [[a, [b, c]]] = [[x, []]];return a;}'); +test('if (x) {let [X] = [x];return X;}'); +test('if (x) {let [y] = [x];return y;}'); +test('if (x) {let [X, y] = [x, x + 1];return X + y;}', 1, 3); +test('if (x) {let [X, y, z] = [x, x + 1, x + 2];return X + y + z;}', 1, 6); +test('if (x) {let [[X]] = [[x]];return X;}'); +test('if (x) {let [X, y] = [x, x + 1];return X;}'); +test('if (x) {let [X, [y, z]] = [x, x + 1];return X;}'); +test('if (x) {let [{x: [X]}, {y1: y, z1: z}] = [x, x + 1];return X;}',{x:['ponies']}); +test('if (x) {let y = x;try {let x = 1;throw 2;} catch (e) {return y;}}'); +test('if (x) {let [] = [[]] = {};return x;}'); +test('let (y, [] = x) {}try {let a = b(), b;} catch (e) {return x;}'); +test('try {let x = 1;throw 2;} catch (e) {return x;}'); +test('let (y = x) {let x;return y;}'); +test('let (y = x) {let x = y;return x;}'); +test('let ([y, z] = x) {let a = x, b = y;return a;}'); +test('let ([y, z] = x, a = x, [] = x) {let b = x, c = y;return a;}'); +test('function f() {return unicorns;}try {let (x = 1) {let a, b;f();}} catch (e) {return x;}'); +test('function f() {return unicorns;}try {let (x = 1) {let a, b;}f();} catch (e) {return x;}'); +test('x.foo;{let y = x;return y;}'); +test('x.foo;if (x) {x.bar;let y = x;return y;}'); +test('if (x) {let y = x;return function () {return eval("y");}();}'); +test('return eval("let y = x; y");'); +test('if (x) {let y = x;eval("var y = 2");return y;}', 'ponies', 2); +test('"use strict";if (x) {let y = x;eval("var y = 2");return y;}'); +test('"use strict";if (x) {let y = x;eval("let y = 2");return y;}'); +test('"use strict";if (x) {let y = 1;return eval("let y = x;y;");}'); +test('this.y = x;if (x) {let y = 1;return this.eval("y");}'); +isError('if (x) {let (x = 1, x = 2) {x}}'); +isError('if (x) {let ([x, y] = a, {a:x} = b) {x}}'); +isError('if (x) {let ([x, y, x] = a) {x}}'); +isError('if (x) {let ([x, [y, [x]]] = a) {x}}'); +isError('let ([x, y] = x) {let x;}'); + +// for(;;) +test('for (;;) {return x;}'); +test('for (let y = 1;;) {return x;}'); +test('for (let y = 1;; ++y) {return x;}'); +test('for (let y = 1; ++y;) {return x;}'); +test('for (let (x = 1) x; x != 1; ++x) {return x;}'); +test('for (let [, {a: [], b: []}] = x, [] = x; x;) {return x;}'); +test('for (let x = 1, [y, z] = x, a = x; z < 4; ++z) {return x + y;}', [2,3], 3); +test('for (let (x = 1, [{a: b, c: d}] = [{a: 1, c: 2}]) x; x != 1; ++x) {return x;}'); +test('for (let [[a, [b, c]]] = [[x, []]];;) {return a;}'); +test('var sum = 0;for (let y = x; y < 4; ++y) {sum += y;}return sum;', 1, 6); +test('var sum = 0;for (let x = x, y = 10; x < 4; ++x) {sum += x;}return sum;', 1, 6); +test('var sum = 0;for (let x = x; x < 4; ++x) {sum += x;}return x;', 1, 1); +test('var sum = 0;for (let x = eval("x"); x < 4; ++x) {sum += x;}return sum;', 1, 6); +test('var sum = 0;for (let x = x; eval("x") < 4; ++x) {sum += eval("x");}return sum;', 1, 6); +test('var sum = 0;for (let x = eval("x"); eval("x") < 4; ++x) {sum += eval("x");}return sum;', 1, 6); +test('for (var y = 1;;) {return x;}'); +test('for (var y = 1;; ++y) {return x;}'); +test('for (var y = 1; ++y;) {return x;}'); +test('for (var [, {a: [], b: []}] = x, [] = x; x;) {return x;}'); +test('for (var X = 1, [y, z] = x, a = x; z < 4; ++z) {return X + y;}', [2,3], 3); +test('var sum = 0;for (var y = x; y < 4; ++y) {sum += y;}return sum;', 1, 6); +test('var sum = 0;for (var X = x, y = 10; X < 4; ++X) {sum += X;}return sum;', 1, 6); +test('var sum = 0;for (var X = x; X < 4; ++X) {sum += X;}return x;', 1, 1); +test('var sum = 0;for (var X = eval("x"); X < 4; ++X) {sum += X;}return sum;', 1, 6); +test('var sum = 0;for (var X = x; eval("X") < 4; ++X) {sum += eval("X");}return sum;', 1, 6); +test('var sum = 0;for (var X = eval("x"); eval("X") < 4; ++X) {sum += eval("X");}return sum;', 1, 6); +test('try {for (let x = eval("throw x");;) {}} catch (e) {return e;}'); +test('try {for (let x = x + "s"; eval("throw x");) {}} catch (e) {return e;}', 'ponie'); +test('for (let y = x;;) {let x;return y;}'); +test('for (let y = x;;) {let y;return x;}'); +test('for (let y;;) {let y;return x;}'); +test('for (let a = x;;) {let c = x, d = x;return c;}'); +test('for (let [a, b] = x;;) {let c = x, d = x;return c;}'); +test('for (let [] = [[]] = {};;) {return x;}'); +isError('for (let x = 1, x = 2;;) {}'); +isError('for (let [x, y] = a, {a:x} = b;;) {}'); +isError('for (let [x, y, x] = a;;) {}'); +isError('for (let [x, [y, [x]]] = a;;) {}'); + +// for(in) +test('for (let i in x) {return x;}'); +test('for (let i in x) {let y;return x;}'); +test('for each (let [a, b] in x) {let y;return x;}'); +test('for (let i in x) {let (i = x) {return i;}}'); +test('for (let i in x) {let i = x;return i;}'); +test('for each (let [x, y] in x) {return x + y;}', [['ponies', '']]); +test('for each (let [{0: x, 1: y}, z] in x) {return x + y + z;}', [[['po','nies'], '']]); +test('var s = "";for (let a in x) {for (let b in x) {s += a + b;}}return s;', [1,2], '00011011'); +test('var res = "";for (let i in x) {res += x[i];}return res;'); +test('var res = "";for (var i in x) {res += x[i];}return res;'); +test('for each (let {x: y, y: x} in [{x: x, y: x}]) {return y;}'); +test('for (let x in eval("x")) {return x;}', {ponies:true}); +test('for (let x in x) {return eval("x");}', {ponies:true}); +test('for (let x in eval("x")) {return eval("x");}', {ponies:true}); +test('for ((let (x = {y: true}) x).y in eval("x")) {return eval("x");}'); +test('for (let i in x) {break;}return x;'); +test('for (let i in x) {break;}return eval("x");'); +test('for (let x in x) {break;}return x;'); +test('for (let x in x) {break;}return eval("x");'); +test('a:for (let i in x) {for (let j in x) {break a;}}return x;'); +test('a:for (let i in x) {for (let j in x) {break a;}}return eval("x");'); +test('var j;for (let i in x) {j = i;break;}return j;', {ponies:true}); +test('try {for (let x in eval("throw x")) {}} catch (e) {return e;}'); +test('try {for each (let x in x) {eval("throw x");}} catch (e) {return e;}', ['ponies']); +isError('for (let [x, x] in o) {}'); +isError('for (let [x, y, x] in o) {}'); +isError('for (let [x, [y, [x]]] in o) {}'); + +// genexps +test('return (i for (i in x)).next();', {ponies:true}); +test('return (eval("i") for (i in x)).next();', {ponies:true}); +test('return (eval("i") for (i in eval("x"))).next();', {ponies:true}); +test('try {return (eval("throw i") for (i in x)).next();} catch (e) {return e;}', {ponies:true}); + +// array comprehension +test('return [i for (i in x)][0];', {ponies:true}); +test('return [eval("i") for (i in x)][0];', {ponies:true}); +test('return [eval("i") for (i in eval("x"))][0];', {ponies:true}); +test('try {return [eval("throw i") for (i in x)][0];} catch (e) {return e;}', {ponies:true}); + +// don't forget about switch craziness +test('var y = 3;switch (function () {return eval("y");}()) {case 3:let y;return x;default:;}'); +test('switch (x) {case 3:let y;return 3;case 4:let z;return 4;default:return x;}'); +test('switch (x) {case 3:let x;break;default:if (x === undefined) {return "ponies";}}'); +test('switch (x) {case 3:default:let y;let (y = x) {return y;}}'); +isError('switch (x) {case 3:let y;return 3;case 4:let y;return 4;default:;}'); + +// test weird cases where the decompiler changes tokens +function testWeird(str, printedAs, arg, result) +{ + var fun = new Function('x', str); + + // this is lame and doesn't normalize whitespace so if an assert fails + // here, see if its just whitespace and fix the caller + assertEq(fun.toSource(), '(function anonymous(x) {' + printedAs + '})'); + + test(printedAs, arg, result); +} + +testWeird('let y = x;return x;', 'var y = x;return x;'); +testWeird('let y = 1, y = x;return y;', 'var y = 1, y = x;return y;'); +testWeird('return let ({x:x, y:y} = x) x + y', 'return let ({x, y} = x) x + y;', {x:'pon', y:'ies'}); +testWeird('let ({x:x, y:y} = x) {return x + y;}', 'let ({x, y} = x) {return x + y;}', {x:'pon', y:'ies'}); diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index 4c77bfa86124..7f47bcc3ecb4 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -119,11 +119,11 @@ ScriptAnalysis::checkAliasedName(JSContext *cx, jsbytecode *pc) JSAtom *atom; if (JSOp(*pc) == JSOP_DEFFUN) { - JSFunction *fun = script->getFunction(js_GetIndexFromBytecode(cx, script, pc, 0)); + JSFunction *fun = script->getFunction(js_GetIndexFromBytecode(script, pc, 0)); atom = fun->atom; } else { JS_ASSERT(JOF_TYPE(js_CodeSpec[*pc].format) == JOF_ATOM); - atom = script->getAtom(js_GetIndexFromBytecode(cx, script, pc, 0)); + atom = script->getAtom(js_GetIndexFromBytecode(script, pc, 0)); } uintN index; @@ -389,6 +389,8 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) isInlineable = canTrackVars = false; break; + case JSOP_ENTERLET0: + case JSOP_ENTERLET1: case JSOP_ENTERBLOCK: case JSOP_LEAVEBLOCK: addsScopeObjects_ = true; diff --git a/js/src/jsanalyze.h b/js/src/jsanalyze.h index d1d24405da0c..021ebe0235f3 100644 --- a/js/src/jsanalyze.h +++ b/js/src/jsanalyze.h @@ -203,9 +203,6 @@ GetDefCount(JSScript *script, unsigned offset) JS_ASSERT(offset < script->length); jsbytecode *pc = script->code + offset; - if (js_CodeSpec[*pc].ndefs == -1) - return js_GetEnterBlockStackDefs(NULL, script, pc); - /* * Add an extra pushed value for OR/AND opcodes, so that they are included * in the pushed array of stack values for type inference. @@ -227,7 +224,7 @@ GetDefCount(JSScript *script, unsigned offset) */ return (pc[1] + 1); default: - return js_CodeSpec[*pc].ndefs; + return StackDefs(script, pc); } } @@ -240,7 +237,7 @@ GetUseCount(JSScript *script, unsigned offset) if (JSOp(*pc) == JSOP_PICK) return (pc[1] + 1); if (js_CodeSpec[*pc].nuses == -1) - return js_GetVariableStackUses(JSOp(*pc), pc); + return StackUses(script, pc); return js_CodeSpec[*pc].nuses; } diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 06ce15384420..cdcde6c2496b 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2032,21 +2032,21 @@ TypeCompartment::newAllocationSiteTypeObject(JSContext *cx, const AllocationSite static inline jsid GetAtomId(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset) { - unsigned index = js_GetIndexFromBytecode(cx, script, (jsbytecode*) pc, offset); + unsigned index = js_GetIndexFromBytecode(script, (jsbytecode*) pc, offset); return MakeTypeId(cx, ATOM_TO_JSID(script->getAtom(index))); } static inline JSObject * GetScriptObject(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset) { - unsigned index = js_GetIndexFromBytecode(cx, script, (jsbytecode*) pc, offset); + unsigned index = js_GetIndexFromBytecode(script, (jsbytecode*) pc, offset); return script->getObject(index); } static inline const Value & GetScriptConst(JSContext *cx, JSScript *script, const jsbytecode *pc) { - unsigned index = js_GetIndexFromBytecode(cx, script, (jsbytecode*) pc, 0); + unsigned index = js_GetIndexFromBytecode(script, (jsbytecode*) pc, 0); return script->getConst(index); } @@ -3956,6 +3956,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, case JSOP_ENTERWITH: case JSOP_ENTERBLOCK: + case JSOP_ENTERLET0: /* * Scope lookups can occur on the values being pushed here. We don't track * the value or its properties, and just monitor all name opcodes in the @@ -3963,6 +3964,16 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, */ break; + case JSOP_ENTERLET1: + /* + * JSOP_ENTERLET1 enters a let block with an unrelated value on top of + * the stack (such as the condition to a switch) whose constraints must + * be propagated. The other values are ignored for the same reason as + * JSOP_ENTERLET0. + */ + poppedTypes(pc, 0)->addSubset(cx, &pushed[defCount - 1]); + break; + case JSOP_ITER: { /* * Use a per-script type set to unify the possible target types of all @@ -4021,6 +4032,9 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, poppedTypes(pc, 0)->addSubset(cx, &pushed[0]); break; + case JSOP_LEAVEFORLETIN: + break; + case JSOP_CASE: case JSOP_CASEX: poppedTypes(pc, 1)->addSubset(cx, &pushed[0]); @@ -4554,7 +4568,7 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSO * integer properties and bail out. We can't mark the aggregate * JSID_VOID type property as being in a definite slot. */ - unsigned index = js_GetIndexFromBytecode(cx, script, pc, 0); + unsigned index = js_GetIndexFromBytecode(script, pc, 0); jsid id = ATOM_TO_JSID(script->getAtom(index)); if (MakeTypeId(cx, id) != id) return false; @@ -5421,6 +5435,8 @@ IgnorePushed(const jsbytecode *pc, unsigned index) /* Storage for 'with' and 'let' blocks not monitored. */ case JSOP_ENTERWITH: case JSOP_ENTERBLOCK: + case JSOP_ENTERLET0: + case JSOP_ENTERLET1: return true; /* We don't keep track of the iteration state for 'for in' or 'for each in' loops. */ diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index a1565be9c067..eefd68b0cfb8 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1857,9 +1857,6 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) /* No-ops for ease of decompilation. */ ADD_EMPTY_CASE(JSOP_NOP) ADD_EMPTY_CASE(JSOP_UNUSED0) -ADD_EMPTY_CASE(JSOP_UNUSED1) -ADD_EMPTY_CASE(JSOP_UNUSED2) -ADD_EMPTY_CASE(JSOP_UNUSED3) ADD_EMPTY_CASE(JSOP_CONDSWITCH) ADD_EMPTY_CASE(JSOP_TRY) #if JS_HAS_XML_SUPPORT @@ -5109,16 +5106,28 @@ END_CASE(JSOP_GETFUNNS) #endif /* JS_HAS_XML_SUPPORT */ BEGIN_CASE(JSOP_ENTERBLOCK) +BEGIN_CASE(JSOP_ENTERLET0) +BEGIN_CASE(JSOP_ENTERLET1) { JSObject *obj; LOAD_OBJECT(0, obj); JS_ASSERT(obj->isStaticBlock()); - JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp); - Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj); - JS_ASSERT(regs.sp < vp); - JS_ASSERT(vp <= regs.fp()->slots() + script->nslots); - SetValueRangeToUndefined(regs.sp, vp); - regs.sp = vp; + JS_ASSERT(regs.fp()->maybeBlockChain() == obj->staticBlockScopeChain()); + + if (op == JSOP_ENTERBLOCK) { + JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp); + Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj); + JS_ASSERT(regs.sp < vp); + JS_ASSERT(vp <= regs.fp()->slots() + script->nslots); + SetValueRangeToUndefined(regs.sp, vp); + regs.sp = vp; + } else if (op == JSOP_ENTERLET0) { + JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj) + == regs.sp); + } else if (op == JSOP_ENTERLET1) { + JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj) + == regs.sp - 1); + } #ifdef DEBUG JS_ASSERT(regs.fp()->maybeBlockChain() == obj->staticBlockScopeChain()); @@ -5134,7 +5143,8 @@ BEGIN_CASE(JSOP_ENTERBLOCK) while (obj2->isWith()) obj2 = obj2->internalScopeChain(); if (obj2->isBlock() && - obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp())) { + obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp())) + { JSObject *youngestProto = obj2->getProto(); JS_ASSERT(youngestProto->isStaticBlock()); JSObject *parent = obj; @@ -5147,14 +5157,14 @@ BEGIN_CASE(JSOP_ENTERBLOCK) } END_CASE(JSOP_ENTERBLOCK) -BEGIN_CASE(JSOP_LEAVEBLOCKEXPR) BEGIN_CASE(JSOP_LEAVEBLOCK) +BEGIN_CASE(JSOP_LEAVEFORLETIN) +BEGIN_CASE(JSOP_LEAVEBLOCKEXPR) { -#ifdef DEBUG JS_ASSERT(regs.fp()->blockChain().isStaticBlock()); - uintN blockDepth = OBJ_BLOCK_DEPTH(cx, ®s.fp()->blockChain()); + DebugOnly blockDepth = OBJ_BLOCK_DEPTH(cx, ®s.fp()->blockChain()); JS_ASSERT(blockDepth <= StackDepth(script)); -#endif + /* * If we're about to leave the dynamic scope of a block that has been * cloned onto fp->scopeChain, clear its private data, move its locals from @@ -5167,19 +5177,22 @@ BEGIN_CASE(JSOP_LEAVEBLOCK) goto error; } - /* Pop the block chain, too. */ regs.fp()->setBlockChain(regs.fp()->blockChain().staticBlockScopeChain()); - /* Move the result of the expression to the new topmost stack slot. */ - Value *vp = NULL; /* silence GCC warnings */ - if (op == JSOP_LEAVEBLOCKEXPR) - vp = ®s.sp[-1]; - regs.sp -= GET_UINT16(regs.pc); - if (op == JSOP_LEAVEBLOCKEXPR) { + if (op == JSOP_LEAVEBLOCK) { + /* Pop the block's slots. */ + regs.sp -= GET_UINT16(regs.pc); + JS_ASSERT(regs.fp()->base() + blockDepth == regs.sp); + } else if (op == JSOP_LEAVEBLOCKEXPR) { + /* Pop the block's slots maintaining the topmost expr. */ + Value *vp = ®s.sp[-1]; + regs.sp -= GET_UINT16(regs.pc); JS_ASSERT(regs.fp()->base() + blockDepth == regs.sp - 1); regs.sp[-1] = *vp; } else { - JS_ASSERT(regs.fp()->base() + blockDepth == regs.sp); + /* Another op will pop; nothing to do here. */ + len = JSOP_LEAVEFORLETIN_LENGTH; + DO_NEXT_OP(len); } } END_CASE(JSOP_LEAVEBLOCK) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 11837e87669a..708f2616bf1d 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3678,20 +3678,28 @@ block_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *v } const Shape * -JSObject::defineBlockVariable(JSContext *cx, jsid id, intN index) +JSObject::defineBlockVariable(JSContext *cx, jsid id, intN index, bool *redeclared) { JS_ASSERT(isStaticBlock()); + *redeclared = false; + + /* Inline JSObject::addProperty in order to trap the redefinition case. */ + Shape **spp = nativeSearch(cx, id, true); + if (SHAPE_FETCH(spp)) { + *redeclared = true; + return NULL; + } + /* - * Use JSPROP_ENUMERATE to aid the disassembler, and don't convert this - * object to dictionary mode so that we can clone the block's shape later. + * Don't convert this object to dictionary mode so that we can clone the + * block's shape later. */ uint32_t slot = JSSLOT_FREE(&BlockClass) + index; - const Shape *shape = addProperty(cx, id, - block_getProperty, block_setProperty, - slot, JSPROP_ENUMERATE | JSPROP_PERMANENT, - Shape::HAS_SHORTID, index, - /* allowDictionary = */ false); + const Shape *shape = addPropertyInternal(cx, id, block_getProperty, block_setProperty, + slot, JSPROP_ENUMERATE | JSPROP_PERMANENT, + Shape::HAS_SHORTID, index, spp, + /* allowDictionary = */ false); if (!shape) return NULL; return shape; @@ -4191,8 +4199,11 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp) if (!js_XDRAtom(xdr, &atom)) return false; - if (!obj->defineBlockVariable(cx, ATOM_TO_JSID(atom), i)) + bool redeclared; + if (!obj->defineBlockVariable(cx, ATOM_TO_JSID(atom), i, &redeclared)) { + JS_ASSERT(!redeclared); return false; + } } } else { AutoShapeVector shapes(cx); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 55ab602c490b..dcd9398a7f2d 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1341,7 +1341,7 @@ struct JSObject : js::gc::Cell bool swap(JSContext *cx, JSObject *other); - const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index); + const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index, bool *redeclared); inline bool isArguments() const; inline bool isArrayBuffer() const; diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 03ec9628ed0b..0bda5f52dd40 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -157,8 +157,7 @@ GetJumpOffset(jsbytecode *pc, jsbytecode *pc2) } uintN -js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, - ptrdiff_t pcoff) +js_GetIndexFromBytecode(JSScript *script, jsbytecode *pc, ptrdiff_t pcoff) { JSOp op = JSOp(*pc); JS_ASSERT(js_CodeSpec[op].length >= 1 + pcoff + UINT16_LEN); @@ -220,10 +219,26 @@ js_GetVariableBytecodeLength(jsbytecode *pc) } } -uintN -js_GetVariableStackUses(JSOp op, jsbytecode *pc) +static uint32_t +NumBlockSlots(JSScript *script, jsbytecode *pc) { - JS_ASSERT(*pc == op); + JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1); + JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET0_LENGTH); + JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET1_LENGTH); + + JSObject *obj = NULL; + GET_OBJECT_FROM_BYTECODE(script, pc, 0, obj); + return OBJ_BLOCK_COUNT(NULL, obj); +} + +uintN +js::StackUses(JSScript *script, jsbytecode *pc) +{ + JSOp op = (JSOp) *pc; + const JSCodeSpec &cs = js_CodeSpec[op]; + if (cs.nuses >= 0) + return cs.nuses; + JS_ASSERT(js_CodeSpec[op].nuses == -1); switch (op) { case JSOP_POPN: @@ -232,6 +247,10 @@ js_GetVariableStackUses(JSOp op, jsbytecode *pc) return GET_UINT16(pc); case JSOP_LEAVEBLOCKEXPR: return GET_UINT16(pc) + 1; + case JSOP_ENTERLET0: + return NumBlockSlots(script, pc); + case JSOP_ENTERLET1: + return NumBlockSlots(script, pc) + 1; default: /* stack: fun, this, [argc arguments] */ JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL || op == JSOP_EVAL || @@ -241,13 +260,15 @@ js_GetVariableStackUses(JSOp op, jsbytecode *pc) } uintN -js_GetEnterBlockStackDefs(JSContext *cx, JSScript *script, jsbytecode *pc) +js::StackDefs(JSScript *script, jsbytecode *pc) { - JSObject *obj; + JSOp op = (JSOp) *pc; + const JSCodeSpec &cs = js_CodeSpec[op]; + if (cs.ndefs >= 0) + return cs.ndefs; - JS_ASSERT(*pc == JSOP_ENTERBLOCK); - GET_OBJECT_FROM_BYTECODE(script, pc, 0, obj); - return OBJ_BLOCK_COUNT(cx, obj); + uint32_t n = NumBlockSlots(script, pc); + return op == JSOP_ENTERLET1 ? n + 1 : n; } static const char * countBaseNames[] = { @@ -483,8 +504,12 @@ ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes) Shape::Range r = obj->lastProperty()->all(); while (!r.empty()) { const Shape &shape = r.front(); + JSAtom *atom = JSID_IS_INT(shape.propid()) + ? cx->runtime->atomState.emptyAtom + : JSID_TO_ATOM(shape.propid()); + JSAutoByteString bytes; - if (!js_AtomToPrintableString(cx, JSID_TO_ATOM(shape.propid()), &bytes)) + if (!js_AtomToPrintableString(cx, atom, &bytes)) return false; r.popFront(); @@ -572,7 +597,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, case JOF_ATOM: case JOF_OBJECT: case JOF_REGEXP: { - uintN index = js_GetIndexFromBytecode(cx, script, pc, 0); + uintN index = js_GetIndexFromBytecode(script, pc, 0); jsval v; if (type == JOF_ATOM) { if (op == JSOP_DOUBLE) { @@ -668,7 +693,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, case JOF_SLOTATOM: case JOF_SLOTOBJECT: { Sprint(sp, " %u", GET_SLOTNO(pc)); - uintN index = js_GetIndexFromBytecode(cx, script, pc, SLOTNO_LEN); + uintN index = js_GetIndexFromBytecode(script, pc, SLOTNO_LEN); jsval v; if (type == JOF_SLOTATOM) { JSAtom *atom = script->getAtom(index); @@ -1281,7 +1306,6 @@ GetOff(SprintStack *ss, uintN i) if (off >= 0) return off; - JS_ASSERT(off <= -2); JS_ASSERT(ss->printer->pcstack); if (off <= -2 && ss->printer->pcstack) { pc = ss->printer->pcstack[-2 - off]; @@ -1361,6 +1385,15 @@ PushOff(SprintStack *ss, ptrdiff_t off, JSOp op, jsbytecode *pc = NULL) return JS_TRUE; } +static bool +PushStr(SprintStack *ss, const char *str, JSOp op) +{ + ptrdiff_t off = SprintCString(&ss->sprinter, str); + if (off < 0) + return false; + return PushOff(ss, off, op); +} + static ptrdiff_t PopOffPrec(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL) { @@ -1673,7 +1706,9 @@ GetLocalInSlot(SprintStack *ss, jsint i, jsint slot, JSObject *obj) const Shape &shape = r.front(); if (shape.shortid() == slot) { - LOCAL_ASSERT(JSID_IS_ATOM(shape.propid())); + /* Ignore the empty destructuring dummy. */ + if (!JSID_IS_ATOM(shape.propid())) + continue; JSAtom *atom = JSID_TO_ATOM(shape.propid()); const char *rval = QuoteString(&ss->sprinter, atom, 0); @@ -1719,8 +1754,7 @@ GetLocal(SprintStack *ss, jsint i) JS_ASSERT(pc < (ss->printer->script->code + ss->printer->script->length)); if (JSOP_ENTERBLOCK == (JSOp)*pc) { - jsatomid j = js_GetIndexFromBytecode(ss->sprinter.context, - ss->printer->script, pc, 0); + jsatomid j = js_GetIndexFromBytecode(ss->printer->script, pc, 0); JSObject *obj = script->getObject(j); if (obj->isBlock()) { @@ -1773,17 +1807,32 @@ IsVarSlot(JSPrinter *jp, jsbytecode *pc, jsint *indexp) #define LOAD_ATOM(PCOFF) \ GET_ATOM_FROM_BYTECODE(jp->script, pc, PCOFF, atom) +typedef Vector AtomVector; +typedef AtomVector::Range AtomRange; + #if JS_HAS_DESTRUCTURING #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL) #define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length) static jsbytecode * -DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc); +DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, + AtomRange *letNames = NULL); +/* + * Decompile a single element of a compound {}/[] destructuring lhs, sprinting + * the result in-place (without pushing/popping the stack) and advancing the pc + * to either the next element or the final pop. + * + * For normal (SRC_DESTRUCT) destructuring, the names of assigned/initialized + * variables are read from their slots. However, for SRC_DESTRUCTLET, the slots + * have not been pushed yet; the caller must pass the names to use via + * 'letNames'. Each variable initialized in this destructuring lhs results in + * popping a name from 'letNames'. + */ static jsbytecode * -DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, - JSBool *hole) +DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JSBool *hole, + AtomRange *letNames = NULL) { JSPrinter *jp; JSOp op; @@ -1804,24 +1853,74 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, return NULL; break; + case JSOP_PICK: + /* + * For 'let ([x, y] = y)', the emitter generates + * + * push evaluation of y + * dup + * 1 one + * 2 getelem + * 3 pick + * 4 two + * getelem + * pick + * pop + * + * Thus 'x' consists of 1 - 3. The caller (DecompileDestructuring or + * DecompileGroupAssignment) will have taken care of 1 - 2, so pc is + * now pointing at 3. The pick indicates a primitive let var init so + * pop a name and advance the pc to 4. + */ + LOCAL_ASSERT(letNames && !letNames->empty()); + if (!QuoteString(&ss->sprinter, letNames->popCopyFront(), 0)) + return NULL; + break; + case JSOP_DUP: - pc = DecompileDestructuring(ss, pc, endpc); + { + /* Compound lhs, e.g., '[x,y]' in 'let [[x,y], z] = a;'. */ + pc = DecompileDestructuring(ss, pc, endpc, letNames); if (!pc) return NULL; if (pc == endpc) return pc; LOAD_OP_DATA(pc); + + /* + * By its post-condition, DecompileDestructuring pushed one string + * containing the whole decompiled lhs. Our post-condition is to sprint + * in-place so pop/concat this pushed string. + */ lval = PopStr(ss, JSOP_NOP); if (SprintCString(&ss->sprinter, lval) < 0) return NULL; + LOCAL_ASSERT(*pc == JSOP_POP); + + /* + * To put block slots in the right place, the emitter follows a + * compound lhs with a pick (if at least one slot was pushed). The pick + * is not part of the compound lhs so DecompileDestructuring did not + * advance over it but it is part of the lhs so advance over it here. + */ + jsbytecode *nextpc = pc + JSOP_POP_LENGTH; + LOCAL_ASSERT(nextpc <= endpc); + if (letNames && *nextpc == JSOP_PICK) { + LOCAL_ASSERT(nextpc < endpc); + pc = nextpc; + LOAD_OP_DATA(pc); + } break; + } case JSOP_SETARG: case JSOP_SETLOCAL: + LOCAL_ASSERT(!letNames); LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_POPN); /* FALL THROUGH */ case JSOP_SETLOCALPOP: + LOCAL_ASSERT(!letNames); if (op == JSOP_SETARG) { atom = GetArgOrVarAtom(jp, GET_SLOTNO(pc)); LOCAL_ASSERT(atom); @@ -1849,6 +1948,7 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, break; default: { + LOCAL_ASSERT(!letNames); /* * We may need to auto-parenthesize the left-most value decompiled * here, so add back PAREN_SLOP temporarily. Then decompile until the @@ -1893,59 +1993,54 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, } /* - * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring - * left-hand side object or array initialiser, including nested destructuring - * initialisers. On successful return, the decompilation will be pushed on ss - * and the return value will point to the POP or GROUP bytecode following the - * destructuring expression. + * Decompile a destructuring lhs object or array initialiser, including nested + * destructuring initialisers. On return a single string is pushed containing + * the entire lhs (regardless of how many variables were bound). Thus, the + * caller must take care of fixing up the decompiler stack. * - * At any point, if pc is equal to endpc and would otherwise advance, we stop - * immediately and return endpc. + * See DecompileDestructuringLHS for description of 'letNames'. */ static jsbytecode * -DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) +DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, + AtomRange *letNames) { - ptrdiff_t head; - JSContext *cx; - JSPrinter *jp; - JSOp op; - const JSCodeSpec *cs; - uintN oplen; - jsint i, lasti; - jsdouble d; - const char *lval; - JSAtom *atom; - jssrcnote *sn; - JSBool hole; - LOCAL_ASSERT(*pc == JSOP_DUP); pc += JSOP_DUP_LENGTH; + JSContext *cx = ss->sprinter.context; + JSPrinter *jp = ss->printer; + jsbytecode *startpc = pc; + /* * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP * chars so the destructuring decompilation accumulates contiguously in * ss->sprinter starting with "[". */ - head = SprintPut(&ss->sprinter, "[", 1); + ptrdiff_t head = SprintPut(&ss->sprinter, "[", 1); if (head < 0 || !PushOff(ss, head, JSOP_NOP)) return NULL; ss->sprinter.offset -= PAREN_SLOP; LOCAL_ASSERT(head == ss->sprinter.offset - 1); LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '['); - cx = ss->sprinter.context; - jp = ss->printer; - lasti = -1; + int lasti = -1; while (pc < endpc) { #if JS_HAS_DESTRUCTURING_SHORTHAND ptrdiff_t nameoff = -1; #endif + const JSCodeSpec *cs; + uintN oplen; + JSOp op; LOAD_OP_DATA(pc); + int i; + double d; switch (op) { case JSOP_POP: + /* Empty destructuring lhs. */ + LOCAL_ASSERT(startpc == pc); pc += oplen; goto out; @@ -1963,7 +2058,8 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) i = (jsint)d; do_getelem: - sn = js_GetSrcNote(jp->script, pc); + { + jssrcnote *sn = js_GetSrcNote(jp->script, pc); pc += oplen; if (pc == endpc) return pc; @@ -1986,10 +2082,12 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) } } break; + } case JSOP_GETPROP: case JSOP_LENGTH: { + JSAtom *atom; LOAD_ATOM(0); *OFF2STR(&ss->sprinter, head) = '{'; #if JS_HAS_DESTRUCTURING_SHORTHAND @@ -2015,7 +2113,8 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) * and continues for a bounded number of bytecodes or stack operations * (and which in any event stops before endpc). */ - pc = DecompileDestructuringLHS(ss, pc, endpc, &hole); + JSBool hole; + pc = DecompileDestructuringLHS(ss, pc, endpc, &hole, letNames); if (!pc) return NULL; @@ -2060,11 +2159,11 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) * means another destructuring initialiser abuts this one like in * '[a] = [b] = c'. */ - sn = js_GetSrcNote(jp->script, pc); + jssrcnote *sn = js_GetSrcNote(jp->script, pc); if (!sn) break; if (SN_TYPE(sn) != SRC_CONTINUE) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT); + LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT || SN_TYPE(sn) == SRC_DESTRUCTLET); break; } @@ -2075,7 +2174,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) } out: - lval = OFF2STR(&ss->sprinter, head); + const char *lval = OFF2STR(&ss->sprinter, head); if (SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1) < 0) return NULL; return pc; @@ -2146,8 +2245,6 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, false) -typedef Vector AtomVector; - /* * The names of the vars of a let block/expr are stored as the ids of the * shapes of the block object. Shapes are stored in a singly-linked list in @@ -2168,7 +2265,9 @@ GetBlockNames(JSContext *cx, JSObject *blockObj, AtomVector *atoms) LOCAL_ASSERT(shape.hasShortID()); --i; LOCAL_ASSERT((uintN)shape.shortid() == i); - (*atoms)[i] = JSID_TO_ATOM(shape.propid()); + (*atoms)[i] = JSID_IS_INT(shape.propid()) + ? cx->runtime->atomState.emptyAtom + : JSID_TO_ATOM(shape.propid()); } LOCAL_ASSERT(i == 0); @@ -2186,9 +2285,28 @@ PushBlockNames(JSContext *cx, SprintStack *ss, const AtomVector &atoms) return true; } +/* + * In the scope of a let, the variables' (decompiler) stack slots must contain + * the corresponding variable's name. This function updates the N top slots + * with the N variable names stored in 'atoms'. + */ +static bool +AssignBlockNamesToPushedSlots(JSContext *cx, SprintStack *ss, const AtomVector &atoms) +{ + /* For simplicity, just pop and push. */ + LOCAL_ASSERT(atoms.length() <= (uintN)ss->top); + for (size_t i = 0; i < atoms.length(); ++i) + PopStr(ss, JSOP_NOP); + return PushBlockNames(cx, ss, atoms); +} + +static const char SkipString[] = "/*skip*/"; +static const char DestructuredString[] = "/*destructured*/"; +static const unsigned DestructuredStringLength = ArrayLength(DestructuredString) - 1; + static ptrdiff_t -SprintLet(JSContext *cx, JSPrinter *jp, SprintStack *ss, jsbytecode *pc, ptrdiff_t bodyLength, - const char *headChars) +SprintLetBody(JSContext *cx, JSPrinter *jp, SprintStack *ss, jsbytecode *pc, ptrdiff_t bodyLength, + const char *headChars) { if (pc[bodyLength] == JSOP_LEAVEBLOCK) { js_printf(jp, "\tlet (%s) {\n", headChars); @@ -2245,16 +2363,15 @@ GetTokenForAssignment(JSPrinter *jp, jssrcnote *sn, JSOp lastop, } static ptrdiff_t -SprintNormalFor(JSContext *cx, JSPrinter *jp, SprintStack *ss, - const char *init, jsbytecode *initpc, - jsbytecode **ppc, ptrdiff_t *plen) +SprintNormalFor(JSContext *cx, JSPrinter *jp, SprintStack *ss, const char *initPrefix, + const char *init, jsbytecode *initpc, jsbytecode **ppc, ptrdiff_t *plen) { jsbytecode *pc = *ppc; jssrcnote *sn = js_GetSrcNote(jp->script, pc); JS_ASSERT(SN_TYPE(sn) == SRC_FOR); /* Print the keyword and the possibly empty init-part. */ - js_printf(jp, "\tfor ("); + js_printf(jp, "\tfor (%s", initPrefix); SprintOpcodePermanent(jp, init, initpc); js_printf(jp, ";"); @@ -2522,7 +2639,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } saveop = op; len = oplen = cs->length; - nuses = js_GetStackUses(cs, op, pc); + nuses = StackUses(jp->script, pc); /* * Here it is possible that nuses > ss->top when the op has a hidden @@ -2531,7 +2648,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) */ if (nb < 0) { LOCAL_ASSERT(ss->top >= nuses); - uintN ndefs = js_GetStackDefs(cx, cs, op, jp->script, pc); + uintN ndefs = StackDefs(jp->script, pc); if ((uintN) -(nb + 1) == ss->top - nuses + ndefs) return pc; } @@ -2671,6 +2788,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case 0: + sn = js_GetSrcNote(jp->script, pc); + if (sn && SN_TYPE(sn) == SRC_CONTINUE) { + /* Hoisted let decl (e.g. 'y' in 'let (x) { let y; }'). */ + todo = SprintCString(&ss->sprinter, SkipString); + break; + } todo = SprintCString(&ss->sprinter, token); break; @@ -2712,7 +2835,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case SRC_FOR: /* for loop with empty initializer. */ - todo = SprintNormalFor(cx, jp, ss, "", NULL, &pc, &len); + todo = SprintNormalFor(cx, jp, ss, "", "", NULL, &pc, &len); break; case SRC_ENDBRACE: @@ -2875,8 +2998,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } else { /* * Kill newtop before the end_groupassignment: label by - * retracting/popping early. Control will either jump - * to do_letheadbody: or else break from our case. + * retracting/popping early. */ LOCAL_ASSERT(newtop < oldtop); ss->sprinter.offset = GetOff(ss, newtop); @@ -2908,31 +3030,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (SN_TYPE(sn) == SRC_FOR) { op = JSOP_NOP; pc = pc2; - todo = SprintNormalFor(cx, jp, ss, rval, rvalpc, &pc, &len); + todo = SprintNormalFor(cx, jp, ss, "", rval, rvalpc, &pc, &len); break; } - - if (SN_TYPE(sn) == SRC_DECL) { - if (ss->top == StackDepth(jp->script)) { - /* - * This must be an empty destructuring - * in the head of a let whose body block - * is also empty. - */ - pc = pc2 + JSOP_NOP_LENGTH; - len = js_GetSrcNoteOffset(sn, 0); - LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK); - js_printf(jp, "\tlet (%s) {\n", rval); - js_printf(jp, "\t}\n"); - break; - } - todo = SprintCString(&ss->sprinter, rval); - if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) - return NULL; - op = JSOP_POP; - pc = pc2 + JSOP_NOP_LENGTH; - goto do_letheadbody; - } } else { /* * An unnannotated NOP following a POPN must be the @@ -2988,7 +3088,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (ss->opcodes[ss->top-1] == JSOP_IN) op = JSOP_LSH; rval = PopStr(ss, op, &rvalpc); - todo = SprintNormalFor(cx, jp, ss, rval, rvalpc, &pc, &len); + todo = SprintNormalFor(cx, jp, ss, "", rval, rvalpc, &pc, &len); break; case SRC_PCDELTA: @@ -3023,22 +3123,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) todo = -2; break; - case SRC_DECL: - { - /* This pop is at the end of the let block/expr head. */ - pc += JSOP_POP_LENGTH; -#if JS_HAS_DESTRUCTURING - do_letheadbody: -#endif - DupBuffer head(cx); - if (!Dup(POP_STR(), &head)) - return NULL; - - len = js_GetSrcNoteOffset(sn, 0); - saveop = (JSOp) pc[len]; - todo = SprintLet(cx, jp, ss, pc, len, head.begin()); - } - break; + case SRC_CONTINUE: + /* Pop the stack, don't print: end of a for-let-in. */ + (void) PopOff(ss, op); + todo = -2; + break; default: { @@ -3236,6 +3325,157 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; } + case JSOP_ENTERLET0: + { + LOAD_OBJECT(0); + + AtomVector atoms(cx); + if (!GetBlockNames(cx, obj, &atoms)) + return NULL; + + sn = js_GetSrcNote(jp->script, pc); + LOCAL_ASSERT(SN_TYPE(sn) == SRC_DECL); + ptrdiff_t letData = js_GetSrcNoteOffset(sn, 0); + bool groupAssign = LetDataToGroupAssign(letData); + uintN letDepth = OBJ_BLOCK_DEPTH(cx, obj); + LOCAL_ASSERT(letDepth == (uintN)ss->top - OBJ_BLOCK_COUNT(cx, obj)); + LOCAL_ASSERT(atoms.length() == OBJ_BLOCK_COUNT(cx, obj)); + + /* + * Build the list of decompiled rhs expressions. Do this before + * sprinting the let-head since GetStr can inject stuff on top + * of the stack (in case js_DecompileValueGenerator). + */ + Vector rhsExprs(cx); + if (!rhsExprs.resize(atoms.length())) + return false; + for (size_t i = 0; i < rhsExprs.length(); ++i) { + rhsExprs[i] = GetStr(ss, letDepth + i); + if (!rhsExprs[i]) + return false; + } + + /* Build the let head starting at headBegin. */ + ptrdiff_t headBegin = ss->sprinter.offset; + + /* + * For group assignment, prepend the '[lhs-vars] = [' here, + * append rhsExprs in the next loop and append ']' after. + */ + if (groupAssign) { + if (Sprint(&ss->sprinter, "[") < 0) + return false; + for (size_t i = 0; i < atoms.length(); ++i) { + if (i && Sprint(&ss->sprinter, ", ") < 0) + return false; + if (!QuoteString(&ss->sprinter, atoms[i], 0)) + return false; + } + if (Sprint(&ss->sprinter, "] = [") < 0) + return false; + } + + for (size_t i = 0; i < atoms.length(); ++i) { + const char *rhs = rhsExprs[i]; + if (!strcmp(rhs, SkipString)) + continue; + + if (i && Sprint(&ss->sprinter, ", ") < 0) + return false; + + if (groupAssign) { + if (SprintCString(&ss->sprinter, rhs) < 0) + return false; + } else if (!strncmp(rhs, DestructuredString, DestructuredStringLength)) { + if (SprintCString(&ss->sprinter, rhs + DestructuredStringLength) < 0) + return false; + } else { + JS_ASSERT(atoms[i] != cx->runtime->atomState.emptyAtom); + if (!QuoteString(&ss->sprinter, atoms[i], 0)) + return false; + if (*rhs) { + uint8_t prec = js_CodeSpec[ss->opcodes[letDepth + i]].prec; + const char *fmt = prec && prec < js_CodeSpec[JSOP_SETLOCAL].prec + ? " = (%s)" + : " = %s"; + if (Sprint(&ss->sprinter, fmt, rhs) < 0) + return false; + } + } + } + + if (groupAssign && Sprint(&ss->sprinter, "]") < 0) + return false; + + /* Clone the let head chars before clobbering the stack. */ + DupBuffer head(cx); + if (!Dup(OFF2STR(&ss->sprinter, headBegin), &head)) + return NULL; + if (!AssignBlockNamesToPushedSlots(cx, ss, atoms)) + return NULL; + + /* Detect 'for (let ...)' desugared into 'let (...) {for}'. */ + jsbytecode *nextpc = pc + JSOP_ENTERLET0_LENGTH; + if (*nextpc == JSOP_NOP) { + jssrcnote *nextsn = js_GetSrcNote(jp->script, nextpc); + if (nextsn && SN_TYPE(nextsn) == SRC_FOR) { + pc = nextpc; + todo = SprintNormalFor(cx, jp, ss, "let ", head.begin(), pc, &pc, &len); + break; + } + } + + /* Decompile the body and then complete the let block/expr. */ + len = LetDataToOffset(letData); + pc = nextpc; + saveop = (JSOp) pc[len]; + todo = SprintLetBody(cx, jp, ss, pc, len, head.begin()); + break; + } + + /* + * With 'for (let lhs in rhs)' and 'switch (c) { let-decl }', + * placeholder slots have already been pushed (by JSOP_UNDEFINED). + * In both the for-let-in and switch-hoisted-let cases: + * - there is a non-let slot on top of the stack (hence enterlet1) + * - there is no further special let-handling required: + * for-let-in will decompile the let head when it decompiles + * the loop body prologue; there is no let head to decompile + * with switch. + * Hence, the only thing to do is update the let vars' slots with + * their names, taking care to preserve the iter/condition value + * on top of the stack. + */ + case JSOP_ENTERLET1: + { + LOAD_OBJECT(0); + + AtomVector atoms(cx); + if (!GetBlockNames(cx, obj, &atoms)) + return NULL; + + LOCAL_ASSERT(js_GetSrcNote(jp->script, pc) == NULL); + LOCAL_ASSERT(ss->top - 1 == OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)); + jsbytecode *nextpc = pc + JSOP_ENTERLET1_LENGTH; + if (*nextpc == JSOP_GOTO || *nextpc == JSOP_GOTOX) { + LOCAL_ASSERT(SN_TYPE(js_GetSrcNote(jp->script, nextpc)) == SRC_FOR_IN); + } else { + LOCAL_ASSERT(*nextpc == JSOP_CONDSWITCH || + *nextpc == JSOP_TABLESWITCH || *nextpc == JSOP_TABLESWITCHX || + *nextpc == JSOP_LOOKUPSWITCH || *nextpc == JSOP_LOOKUPSWITCHX); + } + + DupBuffer rhs(cx); + if (!Dup(PopStr(ss, JSOP_NOP), &rhs)) + return NULL; + if (!AssignBlockNamesToPushedSlots(cx, ss, atoms)) + return NULL; + if (!PushStr(ss, rhs.begin(), op)) + return NULL; + todo = -2; + break; + } + case JSOP_GETFCSLOT: case JSOP_CALLFCSLOT: { @@ -3591,7 +3831,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } /* - * Do not AddParentSlop here, as we will push the + * Do not AddParenSlop here, as we will push the * top-most offset again, which will add paren slop * for us. We must push to balance the stack budget * when nesting for heads in a comprehension. @@ -3857,25 +4097,106 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) #if JS_HAS_DESTRUCTURING sn = js_GetSrcNote(jp->script, pc); if (sn) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT); - pc = DecompileDestructuring(ss, pc, endpc); - if (!pc) - return NULL; - len = 0; - lval = POP_STR(); - op = saveop = JSOP_ENUMELEM; - rval = POP_STR(); + if (SN_TYPE(sn) == SRC_DESTRUCT) { + pc = DecompileDestructuring(ss, pc, endpc); + if (!pc) + return NULL; - if (strcmp(rval, forelem_cookie) == 0) { - todo = Sprint(&ss->sprinter, ss_format, - VarPrefix(sn), lval); + lval = POP_STR(); /* Pop the decompiler result. */ + rval = POP_STR(); /* Pop the initializer expression. */ - // Skip POP so the SRC_FOR_IN code can pop for itself. - if (*pc == JSOP_POP) - len = JSOP_POP_LENGTH; + if (strcmp(rval, forelem_cookie) == 0) { + todo = Sprint(&ss->sprinter, ss_format, + VarPrefix(sn), lval); + + /* Skip POP so the SRC_FOR_IN code can pop for itself. */ + if (*pc == JSOP_POP) + len = JSOP_POP_LENGTH; + } else { + todo = Sprint(&ss->sprinter, "%s%s = %s", + VarPrefix(sn), lval, rval); + } + + op = saveop = JSOP_ENUMELEM; + len = 0; } else { - todo = Sprint(&ss->sprinter, "%s%s = %s", - VarPrefix(sn), lval, rval); + LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCTLET); + + ptrdiff_t offsetToLet = js_GetSrcNoteOffset(sn, 0); + LOCAL_ASSERT(*(pc + offsetToLet) == JSOP_ENTERLET0); + + GET_OBJECT_FROM_BYTECODE(jp->script, pc + offsetToLet, 0, obj); + + uint32_t blockDepth = OBJ_BLOCK_DEPTH(cx, obj); + LOCAL_ASSERT(blockDepth < ss->top); + LOCAL_ASSERT(ss->top <= blockDepth + OBJ_BLOCK_COUNT(cx, obj)); + + AtomVector atoms(cx); + if (!GetBlockNames(cx, obj, &atoms)) + return NULL; + + /* + * Skip any initializers preceding this one. E.g., in + * let (w=1, x=2, [y,z] = a) { ... } + * skip 'w' and 'x' for the JSOP_DUP of '[y,z] = a'. + */ + AtomRange letNames = atoms.all(); + uint32_t curDepth = ss->top - 1 /* initializer */; + for (uint32_t i = blockDepth; i < curDepth; ++i) + letNames.popFront(); + + /* + * Pop and copy the rhs before it gets clobbered. + * Use JSOP_SETLOCAL's precedence since this is =. + */ + DupBuffer rhs(cx); + if (!Dup(PopStr(ss, JSOP_SETLOCAL), &rhs)) + return NULL; + + /* Destructure, tracking how many vars were bound. */ + size_t remainBefore = letNames.remain(); + pc = DecompileDestructuring(ss, pc, endpc, &letNames); + if (!pc) + return NULL; + size_t remainAfter = letNames.remain(); + + /* + * Merge the lhs and rhs and prefix with a cookie to + * tell enterlet0 not to prepend "name = ". + */ + const char *lhs = PopStr(ss, JSOP_NOP); + ptrdiff_t off = Sprint(&ss->sprinter, "%s%s = %s", + DestructuredString, lhs, rhs.begin()); + if (off < 0 || !PushOff(ss, off, JSOP_NOP)) + return NULL; + + /* + * Only one slot has been pushed (holding the entire + * decompiled destructuring expression). However, the + * abstract depth needs one slot per bound var, so push + * empty strings for the remainder. We don't have to + * worry about empty destructuring because the parser + * ensures that there is always at least one pushed + * slot for each destructuring lhs. + */ + LOCAL_ASSERT(remainBefore >= remainAfter); + LOCAL_ASSERT(remainBefore > remainAfter || remainAfter > 0); + for (size_t i = remainBefore - 1; i > remainAfter; --i) { + if (!PushStr(ss, SkipString, JSOP_NOP)) + return NULL; + } + + LOCAL_ASSERT(*pc == JSOP_POP); + pc += JSOP_POP_LENGTH; + + /* Eat up the JSOP_UNDEFINED following empty destructuring. */ + if (remainBefore == remainAfter) { + LOCAL_ASSERT(*pc == JSOP_UNDEFINED); + pc += JSOP_UNDEFINED_LENGTH; + } + + len = 0; + todo = -2; } break; } @@ -5534,8 +5855,8 @@ static intN SimulateOp(JSContext *cx, JSScript *script, JSOp op, const JSCodeSpec *cs, jsbytecode *pc, jsbytecode **pcstack, uintN &pcdepth) { - uintN nuses = js_GetStackUses(cs, op, pc); - uintN ndefs = js_GetStackDefs(cx, cs, op, script, pc); + uintN nuses = StackUses(script, pc); + uintN ndefs = StackDefs(script, pc); LOCAL_ASSERT(pcdepth >= nuses); pcdepth -= nuses; LOCAL_ASSERT(pcdepth + ndefs <= StackDepth(script)); diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 90fe87a7fa8a..bc9b312873b3 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -332,8 +332,7 @@ js_puts(JSPrinter *jp, const char *s); * lexical environments. */ uintN -js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, - ptrdiff_t pcoff); +js_GetIndexFromBytecode(JSScript *script, jsbytecode *pc, ptrdiff_t pcoff); /* * A slower version of GET_ATOM when the caller does not want to maintain @@ -342,72 +341,46 @@ js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, #define GET_ATOM_FROM_BYTECODE(script, pc, pcoff, atom) \ JS_BEGIN_MACRO \ JS_ASSERT(*(pc) != JSOP_DOUBLE); \ - uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ + uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \ (atom) = (script)->getAtom(index_); \ JS_END_MACRO #define GET_DOUBLE_FROM_BYTECODE(script, pc, pcoff, dbl) \ JS_BEGIN_MACRO \ - uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ + uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \ JS_ASSERT(index_ < (script)->consts()->length); \ (dbl) = (script)->getConst(index_).toDouble(); \ JS_END_MACRO #define GET_OBJECT_FROM_BYTECODE(script, pc, pcoff, obj) \ JS_BEGIN_MACRO \ - uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ + uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \ obj = (script)->getObject(index_); \ JS_END_MACRO #define GET_FUNCTION_FROM_BYTECODE(script, pc, pcoff, fun) \ JS_BEGIN_MACRO \ - uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ + uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \ fun = (script)->getFunction(index_); \ JS_END_MACRO #define GET_REGEXP_FROM_BYTECODE(script, pc, pcoff, obj) \ JS_BEGIN_MACRO \ - uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ + uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \ obj = (script)->getRegExp(index_); \ JS_END_MACRO -/* - * Find the number of stack slots used by a variadic opcode such as JSOP_CALL - * (for such ops, JSCodeSpec.nuses is -1). - */ +#ifdef __cplusplus +namespace js { + extern uintN -js_GetVariableStackUses(JSOp op, jsbytecode *pc); +StackUses(JSScript *script, jsbytecode *pc); -/* - * Find the number of stack slots defined by JSOP_ENTERBLOCK (for this op, - * JSCodeSpec.ndefs is -1). - */ extern uintN -js_GetEnterBlockStackDefs(JSContext *cx, JSScript *script, jsbytecode *pc); +StackDefs(JSScript *script, jsbytecode *pc); -#ifdef __cplusplus /* Aargh, libgjs, bug 492720. */ -static JS_INLINE uintN -js_GetStackUses(const JSCodeSpec *cs, JSOp op, jsbytecode *pc) -{ - JS_ASSERT(cs == &js_CodeSpec[op]); - if (cs->nuses >= 0) - return cs->nuses; - return js_GetVariableStackUses(op, pc); -} - -static JS_INLINE uintN -js_GetStackDefs(JSContext *cx, const JSCodeSpec *cs, JSOp op, JSScript *script, - jsbytecode *pc) -{ - JS_ASSERT(cs == &js_CodeSpec[op]); - if (cs->ndefs >= 0) - return cs->ndefs; - - /* Only JSOP_ENTERBLOCK has a variable number of stack defs. */ - JS_ASSERT(op == JSOP_ENTERBLOCK); - return js_GetEnterBlockStackDefs(cx, script, pc); -} -#endif +} /* namespace js */ +#endif /* __cplusplus */ /* * Decompilers, for script, function, and expression pretty-printing. diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 01248a20cec1..2d8e00c35788 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -271,7 +271,8 @@ OPDEF(JSOP_DECLOCAL, 102,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL| OPDEF(JSOP_LOCALINC, 103,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3) OPDEF(JSOP_LOCALDEC, 104,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3) -OPDEF(JSOP_UNUSED1, 105,"unused0", NULL, 1, 0, 0, 0, JOF_BYTE) +/* Leave a for-let-in block leaving its storage pushed (to be popped after enditer). */ +OPDEF(JSOP_LEAVEFORLETIN, 105,"leaveforletin",NULL, 1, 0, 0, 0, JOF_BYTE) /* The argument is the offset to the next statement and is used by IonMonkey. */ OPDEF(JSOP_LABEL, 106,"label", NULL, 3, 0, 0, 0, JOF_JUMP) @@ -440,8 +441,11 @@ OPDEF(JSOP_DELDESC, 183,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|J OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_CALLOP|JOF_TMPSLOT3) -OPDEF(JSOP_UNUSED2, 185,"unused1", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_UNUSED3, 186,"unused2", NULL, 1, 0, 0, 0, JOF_BYTE) +/* Enter a let block/expr whose slots are at the top of the stack. */ +OPDEF(JSOP_ENTERLET0, 185,"enterlet0", NULL, 3, -1, -1, 0, JOF_OBJECT) + +/* Enter a let block/expr whose slots are 1 below the top of the stack. */ +OPDEF(JSOP_ENTERLET1, 186,"enterlet1", NULL, 3, -1, -1, 0, JOF_OBJECT) /* * Opcode to hold 24-bit immediate integer operands. diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index 269d5b9f52e5..77504c2f78c2 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -1623,7 +1623,7 @@ class ASTSerializer bool declaration(ParseNode *pn, Value *dst); bool variableDeclaration(ParseNode *pn, bool let, Value *dst); bool variableDeclarator(ParseNode *pn, VarDeclKind *pkind, Value *dst); - bool letHead(ParseNode *pn, NodeVector &dtors); + bool let(ParseNode *pn, bool expr, Value *dst); bool optStatement(ParseNode *pn, Value *dst) { if (!pn) { @@ -1963,14 +1963,21 @@ ASTSerializer::variableDeclarator(ParseNode *pn, VarDeclKind *pkind, Value *dst) } bool -ASTSerializer::letHead(ParseNode *pn, NodeVector &dtors) +ASTSerializer::let(ParseNode *pn, bool expr, Value *dst) { - if (!dtors.reserve(pn->pn_count)) + ParseNode *letHead = pn->pn_left; + LOCAL_ASSERT(letHead->isArity(PN_LIST)); + + ParseNode *letBody = pn->pn_right; + LOCAL_ASSERT(letBody->isKind(PNK_LEXICALSCOPE)); + + NodeVector dtors(cx); + if (!dtors.reserve(letHead->pn_count)) return false; VarDeclKind kind = VARDECL_LET_HEAD; - for (ParseNode *next = pn->pn_head; next; next = next->pn_next) { + for (ParseNode *next = letHead->pn_head; next; next = next->pn_next) { Value child; /* * Unlike in |variableDeclaration|, this does not update |kind|; since let-heads do @@ -1981,7 +1988,12 @@ ASTSerializer::letHead(ParseNode *pn, NodeVector &dtors) dtors.infallibleAppend(child); } - return true; + Value v; + return expr + ? expression(letBody->pn_expr, &v) && + builder.letExpression(dtors, v, &pn->pn_pos, dst) + : statement(letBody->pn_expr, &v) && + builder.letStatement(dtors, v, &pn->pn_pos, dst); } bool @@ -2078,8 +2090,6 @@ ASTSerializer::forInit(ParseNode *pn, Value *dst) return (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)) ? variableDeclaration(pn, false, dst) - : pn->isKind(PNK_LET) - ? variableDeclaration(pn, true, dst) : expression(pn, dst); } @@ -2091,9 +2101,13 @@ ASTSerializer::statement(ParseNode *pn, Value *dst) case PNK_FUNCTION: case PNK_VAR: case PNK_CONST: - case PNK_LET: return declaration(pn, dst); + case PNK_LET: + return pn->isArity(PN_BINARY) + ? let(pn, false, dst) + : declaration(pn, dst); + case PNK_NAME: LOCAL_ASSERT(pn->isUsed()); return statement(pn->pn_lexdef, dst); @@ -2108,15 +2122,6 @@ ASTSerializer::statement(ParseNode *pn, Value *dst) case PNK_LEXICALSCOPE: pn = pn->pn_expr; - if (pn->isKind(PNK_LET)) { - NodeVector dtors(cx); - Value stmt; - - return letHead(pn->pn_left, dtors) && - statement(pn->pn_right, &stmt) && - builder.letStatement(dtors, stmt, &pn->pn_pos, dst); - } - if (!pn->isKind(PNK_STATEMENTLIST)) return statement(pn, dst); /* FALL THROUGH */ @@ -2176,9 +2181,9 @@ ASTSerializer::statement(ParseNode *pn, Value *dst) return (!head->pn_kid1 ? pattern(head->pn_kid2, NULL, &var) - : variableDeclaration(head->pn_kid1, - head->pn_kid1->isKind(PNK_LET), - &var)) && + : head->pn_kid1->isKind(PNK_LEXICALSCOPE) + ? variableDeclaration(head->pn_kid1->pn_expr, true, &var) + : variableDeclaration(head->pn_kid1, false, &var)) && expression(head->pn_kid3, &expr) && builder.forInStatement(var, expr, stmt, isForEach, &pn->pn_pos, dst); } @@ -2633,17 +2638,8 @@ ASTSerializer::expression(ParseNode *pn, Value *dst) return comprehension(pn->pn_head->pn_expr, dst); - case PNK_LEXICALSCOPE: - { - pn = pn->pn_expr; - - NodeVector dtors(cx); - Value expr; - - return letHead(pn->pn_left, dtors) && - expression(pn->pn_right, &expr) && - builder.letExpression(dtors, expr, &pn->pn_pos, dst); - } + case PNK_LET: + return let(pn, true, dst); #ifdef JS_HAS_XML_SUPPORT case PNK_XMLUNARY: diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index ab3072da4336..8c57cdd0ca0f 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -226,7 +226,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32_t id); * and saved versions. If deserialization fails, the data should be * invalidated if possible. */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 99) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 100) /* * Library-private functions. diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 0d5d94896c73..01555b690f01 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -2678,6 +2678,8 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_GETXPROP) BEGIN_CASE(JSOP_ENTERBLOCK) + BEGIN_CASE(JSOP_ENTERLET0) + BEGIN_CASE(JSOP_ENTERLET1) enterBlock(script->getObject(fullAtomIndex(PC))); END_CASE(JSOP_ENTERBLOCK); @@ -7134,9 +7136,9 @@ mjit::Compiler::enterBlock(JSObject *obj) /* For now, don't bother doing anything for this opcode. */ frame.syncAndForgetEverything(); masm.move(ImmPtr(obj), Registers::ArgReg1); - uint32_t n = js_GetEnterBlockStackDefs(cx, script, PC); INLINE_STUBCALL(stubs::EnterBlock, REJOIN_NONE); - frame.enterBlock(n); + if (*PC == JSOP_ENTERBLOCK) + frame.enterBlock(StackDefs(script, PC)); } void @@ -7146,7 +7148,7 @@ mjit::Compiler::leaveBlock() * Note: After bug 535912, we can pass the block obj directly, inline * PutBlockObject, and do away with the muckiness in PutBlockObject. */ - uint32_t n = js_GetVariableStackUses(JSOP_LEAVEBLOCK, PC); + uint32_t n = StackUses(script, PC); prepareStubCall(Uses(n)); INLINE_STUBCALL(stubs::LeaveBlock, REJOIN_NONE); frame.leaveBlock(n); diff --git a/js/src/methodjit/LoopState.cpp b/js/src/methodjit/LoopState.cpp index 865bd5246905..116ed20b856b 100644 --- a/js/src/methodjit/LoopState.cpp +++ b/js/src/methodjit/LoopState.cpp @@ -1869,7 +1869,7 @@ LoopState::analyzeLoopBody(unsigned frame) case JSOP_SETPROP: case JSOP_SETMETHOD: { - JSAtom *atom = script->getAtom(js_GetIndexFromBytecode(cx, script, pc, 0)); + JSAtom *atom = script->getAtom(js_GetIndexFromBytecode(script, pc, 0)); jsid id = MakeTypeId(cx, ATOM_TO_JSID(atom)); TypeSet *objTypes = analysis->poppedTypes(pc, 1); @@ -2185,7 +2185,7 @@ LoopState::getEntryValue(const CrossSSAValue &iv, uint32_t *pslot, int32_t *pcon } case JSOP_GETPROP: { - JSAtom *atom = script->getAtom(js_GetIndexFromBytecode(cx, script, pc, 0)); + JSAtom *atom = script->getAtom(js_GetIndexFromBytecode(script, pc, 0)); jsid id = ATOM_TO_JSID(atom); CrossSSAValue objcv(cv.frame, analysis->poppedValue(v.pushedOffset(), 0)); FrameEntry *tmp = invariantProperty(objcv, id); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index fd448bb90785..39dbd8bf64f1 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1821,12 +1821,15 @@ stubs::EnterBlock(VMFrame &f, JSObject *obj) JS_ASSERT(!f.regs.inlined()); JS_ASSERT(obj->isStaticBlock()); - JS_ASSERT(fp->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp); - Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj); - JS_ASSERT(regs.sp < vp); - JS_ASSERT(vp <= fp->slots() + fp->script()->nslots); - SetValueRangeToUndefined(regs.sp, vp); - regs.sp = vp; + + if (*regs.pc == JSOP_ENTERBLOCK) { + JS_ASSERT(fp->base() + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp); + Value *vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj); + JS_ASSERT(regs.sp < vp); + JS_ASSERT(vp <= fp->slots() + fp->script()->nslots); + SetValueRangeToUndefined(regs.sp, vp); + regs.sp = vp; + } #ifdef DEBUG JSContext *cx = f.cx; diff --git a/js/src/tests/js1_8/regress/regress-465567-01.js b/js/src/tests/js1_8/regress/regress-465567-01.js index 38310e37f9df..8736d5565593 100644 --- a/js/src/tests/js1_8/regress/regress-465567-01.js +++ b/js/src/tests/js1_8/regress/regress-465567-01.js @@ -49,11 +49,11 @@ expect = '99999'; jit(true); for (let j = 0; j < 5; ++j) { + var e; e = 9; print(actual += '' + e); e = 47; if (e & 0) { - var e; let e; } } diff --git a/js/src/tests/js1_8_5/extensions/reflect-parse.js b/js/src/tests/js1_8_5/extensions/reflect-parse.js index 701382fc530f..879c60e3a909 100644 --- a/js/src/tests/js1_8_5/extensions/reflect-parse.js +++ b/js/src/tests/js1_8_5/extensions/reflect-parse.js @@ -575,7 +575,7 @@ function testVarPatternCombinations(makePattSrc, makePattPatt) { assertStmt("for (var " + pattSrcs[i].join(",") + "; foo; bar);", forStmt(varDecl(pattPatts[i]), ident("foo"), ident("bar"), emptyStmt)); assertStmt("for (let " + pattSrcs[i].join(",") + "; foo; bar);", - forStmt(letDecl(pattPatts[i]), ident("foo"), ident("bar"), emptyStmt)); + letStmt(pattPatts[i], forStmt(null, ident("foo"), ident("bar"), emptyStmt))); assertStmt("for (const " + pattSrcs[i].join(",") + "; foo; bar);", forStmt(constDecl(pattPatts[i]), ident("foo"), ident("bar"), emptyStmt)); } diff --git a/js/src/tests/js1_8_5/extensions/regress-672804-1.js b/js/src/tests/js1_8_5/extensions/regress-672804-1.js index 2197f89278e8..be8cc82f311a 100644 --- a/js/src/tests/js1_8_5/extensions/regress-672804-1.js +++ b/js/src/tests/js1_8_5/extensions/regress-672804-1.js @@ -6,7 +6,7 @@ function f() { let (a = let (x = 1) x) {} } -trap(f, 3, 'assertEq(evalInFrame(1, "a"), 0)'); +trap(f, 4, 'assertEq(evalInFrame(1, "a"), 0)'); f(); reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-672804-3.js b/js/src/tests/js1_8_5/extensions/regress-672804-3.js index 029e82f07a3a..3d60fc36e81b 100644 --- a/js/src/tests/js1_8_5/extensions/regress-672804-3.js +++ b/js/src/tests/js1_8_5/extensions/regress-672804-3.js @@ -5,7 +5,7 @@ var e = [], x = {b: []}; function f() { let (a = [[] for (x in e)], {b: []} = x) {} } -trap(f, 4, ''); +trap(f, 3, ''); f(); reportCompare(0, 0, 'ok'); From 347c7c6e99b2d46bc3722ceea27ec07d39af8475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20=C3=81vila=20de=20Esp=C3=ADndola?= Date: Thu, 22 Dec 2011 11:00:48 -0500 Subject: [PATCH 16/26] Bug 708031 - refactor the ifdefs in nsStackWalk.cpp. r=dbaron. --HG-- extra : rebase_source : 806f8437d30787c59e0059054c094fee77b5dce3 --- xpcom/base/nsStackWalk.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/xpcom/base/nsStackWalk.cpp b/xpcom/base/nsStackWalk.cpp index d34e385aff47..c66b059c0b86 100644 --- a/xpcom/base/nsStackWalk.cpp +++ b/xpcom/base/nsStackWalk.cpp @@ -58,7 +58,20 @@ static CriticalAddress gCriticalAddress; #include #endif -#if defined(XP_MACOSX) && (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE)) +#define NSSTACKWALK_SUPPORTS_MACOSX \ + (defined(XP_MACOSX) && \ + (defined(__i386) || defined(__ppc__) || defined(HAVE__UNWIND_BACKTRACE))) + +#define NSSTACKWALK_SUPPORTS_LINUX \ + (defined(linux) && \ + ((defined(__GNUC__) && (defined(__i386) || defined(PPC))) || \ + defined(HAVE__UNWIND_BACKTRACE))) + +#define NSSTACKWALK_SUPPORTS_SOLARIS \ + (defined(__sun) && \ + (defined(__sparc) || defined(sparc) || defined(__i386) || defined(i386))) + +#if NSSTACKWALK_SUPPORTS_MACOSX #include #include #include @@ -1251,7 +1264,7 @@ NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails, // WIN32 x86 stack walking code // i386 or PPC Linux stackwalking code or Solaris -#elif HAVE_DLADDR && (HAVE__UNWIND_BACKTRACE || (defined(linux) && defined(__GNUC__) && (defined(__i386) || defined(PPC))) || (defined(__sun) && (defined(__sparc) || defined(sparc) || defined(__i386) || defined(i386))) || (defined(XP_MACOSX) && (defined(__ppc__) || defined(__i386)))) +#elif HAVE_DLADDR && (HAVE__UNWIND_BACKTRACE || NSSTACKWALK_SUPPORTS_LINUX || NSSTACKWALK_SUPPORTS_SOLARIS || NSSTACKWALK_SUPPORTS_MACOSX) #include #include @@ -1294,7 +1307,7 @@ void DemangleSymbol(const char * aSymbol, } -#if defined(__sun) && (defined(__sparc) || defined(sparc) || defined(__i386) || defined(i386)) +#if NSSTACKWALK_SUPPORTS_SOLARIS /* * Stack walking code for Solaris courtesy of Bart Smaalder's "memtrak". @@ -1533,7 +1546,8 @@ NS_FormatCodeAddressDetails(void *aPC, const nsCodeAddressDetails *aDetails, #else // not __sun-specific -#if (defined(linux) && defined(__GNUC__) && (defined(__i386) || defined(PPC))) || (defined(XP_MACOSX) && (defined(__i386) || defined(__ppc__))) // i386 or PPC Linux or Mac stackwalking code +#define X86_OR_PPC (defined(__i386) || defined(PPC) || defined(__ppc__)) +#if X86_OR_PPC && (NSSTACKWALK_SUPPORTS_MACOSX || NSSTACKWALK_SUPPORTS_LINUX) // i386 or PPC Linux or Mac stackwalking code #if __GLIBC__ > 2 || __GLIBC_MINOR > 1 #define HAVE___LIBC_STACK_END 1 From afbe1e8534add209ae77d550c626ef33e845cbf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20=C3=81vila=20de=20Esp=C3=ADndola?= Date: Thu, 22 Dec 2011 11:21:37 -0500 Subject: [PATCH 17/26] Bug 702848 - Finalize the statements in services sync/modules/engines/forms.js. r=dolske. --- services/sync/modules/engines/forms.js | 11 +++++++++++ toolkit/components/satchel/nsFormHistory.js | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/services/sync/modules/engines/forms.js b/services/sync/modules/engines/forms.js index 76a2a992f46f..03610ef5c0d7 100644 --- a/services/sync/modules/engines/forms.js +++ b/services/sync/modules/engines/forms.js @@ -79,6 +79,13 @@ let FormWrapper = { return this._stmts[query] = db.createAsyncStatement(query); }, + _finalize : function () { + for each (let stmt in FormWrapper._stmts) { + stmt.finalize(); + } + FormWrapper._stmts = {}; + }, + get _getAllEntriesStmt() { const query = "SELECT fieldname name, value FROM moz_formhistory " + @@ -270,6 +277,7 @@ function FormTracker(name) { Tracker.call(this, name); Svc.Obs.add("weave:engine:start-tracking", this); Svc.Obs.add("weave:engine:stop-tracking", this); + Svc.Obs.add("profile-change-teardown", this); } FormTracker.prototype = { __proto__: Tracker.prototype, @@ -320,6 +328,9 @@ FormTracker.prototype = { this.trackEntry(name, value); } break; + case "profile-change-teardown": + FormWrapper._finalize(); + break; } }, diff --git a/toolkit/components/satchel/nsFormHistory.js b/toolkit/components/satchel/nsFormHistory.js index cdacd67ac15b..6a1974665314 100644 --- a/toolkit/components/satchel/nsFormHistory.js +++ b/toolkit/components/satchel/nsFormHistory.js @@ -137,6 +137,7 @@ FormHistory.prototype = { this.messageManager.addMessageListener("FormHistory:FormSubmitEntries", this); // Add observers + Services.obs.addObserver(this, "profile-change-teardown", true); Services.obs.addObserver(this, "profile-before-change", true); Services.obs.addObserver(this, "idle-daily", true); Services.obs.addObserver(this, "formhistory-expire-now", true); @@ -402,6 +403,9 @@ FormHistory.prototype = { this.expireOldEntries(); break; case "profile-before-change": + // FIXME (bug 696486): close the connection in here. + break; + case "profile-change-teardown": this._dbFinalize(); break; default: @@ -870,7 +874,6 @@ FormHistory.prototype = { * Finalize all statements to allow closing the connection correctly. */ _dbFinalize : function FH__dbFinalize() { - // FIXME (bug 696486): close the connection in here. for each (let stmt in this.dbStmts) { stmt.finalize(); } From 4f79e0b92f4269609ce7d0fad18dd41991d533c7 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Thu, 22 Dec 2011 18:17:06 -0500 Subject: [PATCH 18/26] Make various returns in jsopcode.cpp properly return NULL rather than false. Followup to bug 692274, r=themaid --- js/src/jsopcode.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 0bda5f52dd40..066301981169 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -3348,11 +3348,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) */ Vector rhsExprs(cx); if (!rhsExprs.resize(atoms.length())) - return false; + return NULL; for (size_t i = 0; i < rhsExprs.length(); ++i) { rhsExprs[i] = GetStr(ss, letDepth + i); if (!rhsExprs[i]) - return false; + return NULL; } /* Build the let head starting at headBegin. */ @@ -3364,15 +3364,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) */ if (groupAssign) { if (Sprint(&ss->sprinter, "[") < 0) - return false; + return NULL; for (size_t i = 0; i < atoms.length(); ++i) { if (i && Sprint(&ss->sprinter, ", ") < 0) - return false; + return NULL; if (!QuoteString(&ss->sprinter, atoms[i], 0)) - return false; + return NULL; } if (Sprint(&ss->sprinter, "] = [") < 0) - return false; + return NULL; } for (size_t i = 0; i < atoms.length(); ++i) { @@ -3381,31 +3381,31 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) continue; if (i && Sprint(&ss->sprinter, ", ") < 0) - return false; + return NULL; if (groupAssign) { if (SprintCString(&ss->sprinter, rhs) < 0) - return false; + return NULL; } else if (!strncmp(rhs, DestructuredString, DestructuredStringLength)) { if (SprintCString(&ss->sprinter, rhs + DestructuredStringLength) < 0) - return false; + return NULL; } else { JS_ASSERT(atoms[i] != cx->runtime->atomState.emptyAtom); if (!QuoteString(&ss->sprinter, atoms[i], 0)) - return false; + return NULL; if (*rhs) { uint8_t prec = js_CodeSpec[ss->opcodes[letDepth + i]].prec; const char *fmt = prec && prec < js_CodeSpec[JSOP_SETLOCAL].prec ? " = (%s)" : " = %s"; if (Sprint(&ss->sprinter, fmt, rhs) < 0) - return false; + return NULL; } } } if (groupAssign && Sprint(&ss->sprinter, "]") < 0) - return false; + return NULL; /* Clone the let head chars before clobbering the stack. */ DupBuffer head(cx); From 0c44680201e47baf1d74739417f846cdacd51640 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Thu, 22 Dec 2011 21:21:20 -0500 Subject: [PATCH 19/26] bug 694576 r=bz --- content/base/src/nsContentUtils.cpp | 41 ++++++++++++------------- netwerk/base/public/nsNetUtil.h | 27 ++++++++++++++++ netwerk/protocol/http/nsHttpHandler.cpp | 21 +------------ 3 files changed, 47 insertions(+), 42 deletions(-) diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index e088a81da370..1cfb1d57a38e 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -4968,18 +4968,16 @@ nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsCString& aOrigin) rv = uri->GetScheme(scheme); NS_ENSURE_SUCCESS(rv, rv); - aOrigin = scheme + NS_LITERAL_CSTRING("://") + host; - - // If needed, append the port - PRInt32 port; + PRInt32 port = -1; uri->GetPort(&port); - if (port != -1) { - PRInt32 defaultPort = NS_GetDefaultPort(scheme.get()); - if (port != defaultPort) { - aOrigin.Append(':'); - aOrigin.AppendInt(port); - } - } + if (port != -1 && port == NS_GetDefaultPort(scheme.get())) + port = -1; + + nsCString hostPort; + rv = NS_GenerateHostPort(host, port, hostPort); + NS_ENSURE_SUCCESS(rv, rv); + + aOrigin = scheme + NS_LITERAL_CSTRING("://") + hostPort; } else { aOrigin.AssignLiteral("null"); @@ -5028,18 +5026,17 @@ nsContentUtils::GetUTFOrigin(nsIURI* aURI, nsString& aOrigin) rv = uri->GetScheme(scheme); NS_ENSURE_SUCCESS(rv, rv); - aOrigin = NS_ConvertUTF8toUTF16(scheme + NS_LITERAL_CSTRING("://") + host); - - // If needed, append the port - PRInt32 port; + PRInt32 port = -1; uri->GetPort(&port); - if (port != -1) { - PRInt32 defaultPort = NS_GetDefaultPort(scheme.get()); - if (port != defaultPort) { - aOrigin.Append(':'); - aOrigin.AppendInt(port); - } - } + if (port != -1 && port == NS_GetDefaultPort(scheme.get())) + port = -1; + + nsCString hostPort; + rv = NS_GenerateHostPort(host, port, hostPort); + NS_ENSURE_SUCCESS(rv, rv); + + aOrigin = NS_ConvertUTF8toUTF16( + scheme + NS_LITERAL_CSTRING("://") + hostPort); } else { aOrigin.AssignLiteral("null"); diff --git a/netwerk/base/public/nsNetUtil.h b/netwerk/base/public/nsNetUtil.h index c718b92233a4..d8bd8a89524c 100644 --- a/netwerk/base/public/nsNetUtil.h +++ b/netwerk/base/public/nsNetUtil.h @@ -2030,4 +2030,31 @@ NS_IsAboutBlank(nsIURI *uri) return str.EqualsLiteral("about:blank"); } + +inline nsresult +NS_GenerateHostPort(const nsCString& host, PRInt32 port, + nsCString& hostLine) +{ + if (strchr(host.get(), ':')) { + // host is an IPv6 address literal and must be encapsulated in []'s + hostLine.Assign('['); + // scope id is not needed for Host header. + int scopeIdPos = host.FindChar('%'); + if (scopeIdPos == -1) + hostLine.Append(host); + else if (scopeIdPos > 0) + hostLine.Append(Substring(host, 0, scopeIdPos)); + else + return NS_ERROR_MALFORMED_URI; + hostLine.Append(']'); + } + else + hostLine.Assign(host); + if (port != -1) { + hostLine.Append(':'); + hostLine.AppendInt(port); + } + return NS_OK; +} + #endif // !nsNetUtil_h__ diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index d4735249e3ab..3d6929a74428 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -552,26 +552,7 @@ nsHttpHandler::AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan, nsHttpHandler::GenerateHostPort(const nsCString& host, PRInt32 port, nsCString& hostLine) { - if (strchr(host.get(), ':')) { - // host is an IPv6 address literal and must be encapsulated in []'s - hostLine.Assign('['); - // scope id is not needed for Host header. - int scopeIdPos = host.FindChar('%'); - if (scopeIdPos == kNotFound) - hostLine.Append(host); - else if (scopeIdPos > 0) - hostLine.Append(Substring(host, 0, scopeIdPos)); - else - return NS_ERROR_MALFORMED_URI; - hostLine.Append(']'); - } - else - hostLine.Assign(host); - if (port != -1) { - hostLine.Append(':'); - hostLine.AppendInt(port); - } - return NS_OK; + return NS_GenerateHostPort(host, port, hostLine); } //----------------------------------------------------------------------------- From e87f44e6c147bdc666c5b3f2b7fde194c79ed191 Mon Sep 17 00:00:00 2001 From: Cameron Kaiser Date: Thu, 22 Dec 2011 19:09:53 -0800 Subject: [PATCH 20/26] Bug 712990: add missing big-endian code to Yarr JIT, r=dmandelin --- js/src/yarr/YarrJIT.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js/src/yarr/YarrJIT.cpp b/js/src/yarr/YarrJIT.cpp index ea529f876319..aae6a1c47081 100644 --- a/js/src/yarr/YarrJIT.cpp +++ b/js/src/yarr/YarrJIT.cpp @@ -696,10 +696,17 @@ class YarrGenerator : private MacroAssembler { #endif if (m_pattern.m_ignoreCase) { +#if WTF_CPU_BIG_ENDIAN + if (isASCIIAlpha(ch)) + mask |= 32 << 16; + if (isASCIIAlpha(ch2)) + mask |= 32; +#else if (isASCIIAlpha(ch)) mask |= 32; if (isASCIIAlpha(ch2)) mask |= 32 << 16; +#endif } BaseIndex address(input, index, TimesTwo, (term->inputPosition - m_checked) * sizeof(UChar)); From e1ff790f8b2907a135fc90921d51fa7473786001 Mon Sep 17 00:00:00 2001 From: William Chen Date: Thu, 22 Dec 2011 18:34:45 -0500 Subject: [PATCH 21/26] Bug 497995: Part 1 - Implement border-image revisions in latest css3-background spec. r=dbaron --HG-- extra : rebase_source : 5a6cdac6a4b1353170f23f39dd2a209374e72531 --- dom/interfaces/css/nsIDOMCSS2Properties.idl | 17 +- layout/base/nsCSSRendering.cpp | 206 ++++++----- layout/base/nsDisplayList.cpp | 5 +- layout/base/nsImageLoader.cpp | 13 - layout/base/nsImageLoader.h | 7 +- layout/base/nsPresContext.cpp | 2 - layout/base/nsStyleConsts.h | 9 +- layout/generic/nsFrame.cpp | 25 +- layout/style/Declaration.cpp | 11 + layout/style/nsCSSParser.cpp | 360 ++++++++++++++++---- layout/style/nsCSSPropList.h | 54 ++- layout/style/nsCSSProps.cpp | 28 +- layout/style/nsCSSProps.h | 3 +- layout/style/nsCSSValue.cpp | 43 ++- layout/style/nsCSSValue.h | 4 +- layout/style/nsComputedDOMStyle.cpp | 132 ++++--- layout/style/nsComputedDOMStyle.h | 8 +- layout/style/nsRuleNode.cpp | 251 +++++++++----- layout/style/nsStyleStruct.cpp | 132 +++---- layout/style/nsStyleStruct.h | 88 ++--- layout/style/nsStyleStructInlines.h | 10 +- 21 files changed, 981 insertions(+), 427 deletions(-) diff --git a/dom/interfaces/css/nsIDOMCSS2Properties.idl b/dom/interfaces/css/nsIDOMCSS2Properties.idl index 2942e572dfe7..3ba5d06937f1 100644 --- a/dom/interfaces/css/nsIDOMCSS2Properties.idl +++ b/dom/interfaces/css/nsIDOMCSS2Properties.idl @@ -51,7 +51,7 @@ * http://www.w3.org/TR/DOM-Level-2-Style */ -[builtinclass, scriptable, uuid(0a6fc4c6-a62a-4f52-9ab6-3d398b958843)] +[builtinclass, scriptable, uuid(b477527a-a6f4-4f86-a16b-563e602e930a)] interface nsIDOMCSS2Properties : nsISupports { attribute DOMString background; @@ -767,4 +767,19 @@ interface nsIDOMCSS2Properties : nsISupports attribute DOMString MozTextSizeAdjust; // raises(DOMException) on setting + + attribute DOMString MozBorderImageSource; + // raises(DOMException) on setting + + attribute DOMString MozBorderImageSlice; + // raises(DOMException) on setting + + attribute DOMString MozBorderImageWidth; + // raises(DOMException) on setting + + attribute DOMString MozBorderImageOutset; + // raises(DOMException) on setting + + attribute DOMString MozBorderImageRepeat; + // raises(DOMException) on setting }; diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index e3fd99f9db71..18112094c570 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -2713,7 +2713,7 @@ nsCSSRendering::GetBackgroundLayerRect(nsPresContext* aPresContext, static void DrawBorderImage(nsPresContext* aPresContext, - nsRenderingContext& aRenderingContext, + nsRenderingContext& aRenderingContext, nsIFrame* aForFrame, const nsRect& aBorderArea, const nsStyleBorder& aStyleBorder, @@ -2735,9 +2735,7 @@ DrawBorderImage(nsPresContext* aPresContext, imgIRequest *req = aStyleBorder.GetBorderImage(); - // Get the actual image, and determine where the split points are. - // Note that mBorderImageSplit is in image pixels, not necessarily - // CSS pixels. + // Get the actual image. nsCOMPtr imgContainer; req->GetImage(getter_AddRefs(imgContainer)); @@ -2753,13 +2751,21 @@ DrawBorderImage(nsPresContext* aPresContext, nsPresContext::AppUnitsToIntCSSPixels(aBorderArea.height); } - // Convert percentages and clamp values to the image size. - nsIntMargin split; + // Determine the border image area, which by default corresponds to the + // border box but can be modified by 'border-image-outset'. + nsRect borderImgArea(aBorderArea); + borderImgArea.Inflate(aStyleBorder.GetImageOutset()); + + // Compute the used values of 'border-image-slice' and 'border-image-width'; + // we do them together because the latter can depend on the former. + nsIntMargin slice; + nsMargin border; NS_FOR_CSS_SIDES(s) { - nsStyleCoord coord = aStyleBorder.mBorderImageSplit.Get(s); - PRInt32 imgDimension = ((s == NS_SIDE_TOP || s == NS_SIDE_BOTTOM) - ? imageSize.height - : imageSize.width); + nsStyleCoord coord = aStyleBorder.mBorderImageSlice.Get(s); + PRInt32 imgDimension = NS_SIDE_IS_VERTICAL(s) + ? imageSize.width : imageSize.height; + nscoord borderDimension = NS_SIDE_IS_VERTICAL(s) + ? borderImgArea.width : borderImgArea.height; double value; switch (coord.GetUnit()) { case eStyleUnit_Percent: @@ -2769,8 +2775,7 @@ DrawBorderImage(nsPresContext* aPresContext, value = coord.GetFactorValue(); break; default: - NS_ASSERTION(coord.GetUnit() == eStyleUnit_Null, - "unexpected CSS unit for image split"); + NS_NOTREACHED("unexpected CSS unit for image slice"); value = 0; break; } @@ -2778,74 +2783,123 @@ DrawBorderImage(nsPresContext* aPresContext, value = 0; if (value > imgDimension) value = imgDimension; - split.Side(s) = NS_lround(value); + slice.Side(s) = NS_lround(value); + + nsMargin borderWidths(aStyleBorder.GetActualBorder()); + coord = aStyleBorder.mBorderImageWidth.Get(s); + switch (coord.GetUnit()) { + case eStyleUnit_Coord: // absolute dimension + value = coord.GetCoordValue(); + break; + case eStyleUnit_Percent: + value = coord.GetPercentValue() * borderDimension; + break; + case eStyleUnit_Factor: + value = coord.GetFactorValue() * borderWidths.Side(s); + break; + case eStyleUnit_Auto: // same as the slice value, in CSS pixels + value = nsPresContext::CSSPixelsToAppUnits(slice.Side(s)); + break; + default: + NS_NOTREACHED("unexpected CSS unit for border image area division"); + value = 0; + break; + } + border.Side(s) = NS_lround(value); } - nsMargin border(aStyleBorder.GetActualBorder()); + // "If two opposite border-image-width offsets are large enough that they + // overlap, their used values are proportionately reduced until they no + // longer overlap." + double scaleX = border.left + border.right > borderImgArea.width + ? borderImgArea.width / double(border.left + border.right) + : 1.0; + double scaleY = border.top + border.bottom > borderImgArea.height + ? borderImgArea.height / double(border.top + border.bottom) + : 1.0; + double scale = NS_MIN(scaleX, scaleY); + if (scale < 1.0) { + border.left *= scale; + border.right *= scale; + border.top *= scale; + border.bottom *= scale; + NS_ASSERTION(border.left + border.right <= borderImgArea.width && + border.top + border.bottom <= borderImgArea.height, + "rounding error in width reduction???"); + } - // These helper tables recharacterize the 'split' and 'border' margins + // These helper tables recharacterize the 'slice' and 'width' margins // in a more convenient form: they are the x/y/width/height coords // required for various bands of the border, and they have been transformed - // to be relative to the image (for 'split') or the page (for 'border'). + // to be relative to the innerRect (for 'slice') or the page (for 'border'). enum { LEFT, MIDDLE, RIGHT, TOP = LEFT, BOTTOM = RIGHT }; const nscoord borderX[3] = { - aBorderArea.x + 0, - aBorderArea.x + border.left, - aBorderArea.x + aBorderArea.width - border.right, + borderImgArea.x + 0, + borderImgArea.x + border.left, + borderImgArea.x + borderImgArea.width - border.right, }; const nscoord borderY[3] = { - aBorderArea.y + 0, - aBorderArea.y + border.top, - aBorderArea.y + aBorderArea.height - border.bottom, + borderImgArea.y + 0, + borderImgArea.y + border.top, + borderImgArea.y + borderImgArea.height - border.bottom, }; const nscoord borderWidth[3] = { border.left, - aBorderArea.width - border.left - border.right, + borderImgArea.width - border.left - border.right, border.right, }; const nscoord borderHeight[3] = { border.top, - aBorderArea.height - border.top - border.bottom, + borderImgArea.height - border.top - border.bottom, border.bottom, }; - - const PRInt32 splitX[3] = { + const PRInt32 sliceX[3] = { 0, - split.left, - imageSize.width - split.right, + slice.left, + imageSize.width - slice.right, }; - const PRInt32 splitY[3] = { + const PRInt32 sliceY[3] = { 0, - split.top, - imageSize.height - split.bottom, + slice.top, + imageSize.height - slice.bottom, }; - const PRInt32 splitWidth[3] = { - split.left, - imageSize.width - split.left - split.right, - split.right, + const PRInt32 sliceWidth[3] = { + slice.left, + PR_MAX(imageSize.width - slice.left - slice.right, 0), + slice.right, }; - const PRInt32 splitHeight[3] = { - split.top, - imageSize.height - split.top - split.bottom, - split.bottom, + const PRInt32 sliceHeight[3] = { + slice.top, + PR_MAX(imageSize.height - slice.top - slice.bottom, 0), + slice.bottom, }; // In all the 'factor' calculations below, 'border' measurements are - // in app units but 'split' measurements are in image/CSS pixels, so + // in app units but 'slice' measurements are in image/CSS pixels, so // the factor corresponding to no additional scaling is // CSSPixelsToAppUnits(1), not simply 1. for (int i = LEFT; i <= RIGHT; i++) { for (int j = TOP; j <= BOTTOM; j++) { nsRect destArea(borderX[i], borderY[j], borderWidth[i], borderHeight[j]); - nsIntRect subArea(splitX[i], splitY[j], splitWidth[i], splitHeight[j]); + nsIntRect subArea(sliceX[i], sliceY[j], sliceWidth[i], sliceHeight[j]); PRUint8 fillStyleH, fillStyleV; nsSize unitSize; if (i == MIDDLE && j == MIDDLE) { + // Discard the middle portion unless set to fill. + if (NS_STYLE_BORDER_IMAGE_SLICE_NOFILL == + aStyleBorder.mBorderImageFill) { + continue; + } + + NS_ASSERTION(NS_STYLE_BORDER_IMAGE_SLICE_FILL == + aStyleBorder.mBorderImageFill, + "Unexpected border image fill"); + // css-background: // The middle image's width is scaled by the same factor as the // top image unless that factor is zero or infinity, in which @@ -2857,57 +2911,57 @@ DrawBorderImage(nsPresContext* aPresContext, // that, the height is not scaled. gfxFloat hFactor, vFactor; - if (0 < border.left && 0 < split.left) - vFactor = gfxFloat(border.left)/split.left; - else if (0 < border.right && 0 < split.right) - vFactor = gfxFloat(border.right)/split.right; + if (0 < border.left && 0 < slice.left) + vFactor = gfxFloat(border.left)/slice.left; + else if (0 < border.right && 0 < slice.right) + vFactor = gfxFloat(border.right)/slice.right; else vFactor = nsPresContext::CSSPixelsToAppUnits(1); - if (0 < border.top && 0 < split.top) - hFactor = gfxFloat(border.top)/split.top; - else if (0 < border.bottom && 0 < split.bottom) - hFactor = gfxFloat(border.bottom)/split.bottom; + if (0 < border.top && 0 < slice.top) + hFactor = gfxFloat(border.top)/slice.top; + else if (0 < border.bottom && 0 < slice.bottom) + hFactor = gfxFloat(border.bottom)/slice.bottom; else hFactor = nsPresContext::CSSPixelsToAppUnits(1); - unitSize.width = splitWidth[i]*hFactor; - unitSize.height = splitHeight[j]*vFactor; - fillStyleH = aStyleBorder.mBorderImageHFill; - fillStyleV = aStyleBorder.mBorderImageVFill; + unitSize.width = sliceWidth[i]*hFactor; + unitSize.height = sliceHeight[j]*vFactor; + fillStyleH = aStyleBorder.mBorderImageRepeatH; + fillStyleV = aStyleBorder.mBorderImageRepeatV; } else if (i == MIDDLE) { // top, bottom // Sides are always stretched to the thickness of their border, // and stretched proportionately on the other axis. gfxFloat factor; - if (0 < borderHeight[j] && 0 < splitHeight[j]) - factor = gfxFloat(borderHeight[j])/splitHeight[j]; + if (0 < borderHeight[j] && 0 < sliceHeight[j]) + factor = gfxFloat(borderHeight[j])/sliceHeight[j]; else factor = nsPresContext::CSSPixelsToAppUnits(1); - unitSize.width = splitWidth[i]*factor; + unitSize.width = sliceWidth[i]*factor; unitSize.height = borderHeight[j]; - fillStyleH = aStyleBorder.mBorderImageHFill; - fillStyleV = NS_STYLE_BORDER_IMAGE_STRETCH; + fillStyleH = aStyleBorder.mBorderImageRepeatH; + fillStyleV = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH; } else if (j == MIDDLE) { // left, right gfxFloat factor; - if (0 < borderWidth[i] && 0 < splitWidth[i]) - factor = gfxFloat(borderWidth[i])/splitWidth[i]; + if (0 < borderWidth[i] && 0 < sliceWidth[i]) + factor = gfxFloat(borderWidth[i])/sliceWidth[i]; else factor = nsPresContext::CSSPixelsToAppUnits(1); unitSize.width = borderWidth[i]; - unitSize.height = splitHeight[j]*factor; - fillStyleH = NS_STYLE_BORDER_IMAGE_STRETCH; - fillStyleV = aStyleBorder.mBorderImageVFill; + unitSize.height = sliceHeight[j]*factor; + fillStyleH = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH; + fillStyleV = aStyleBorder.mBorderImageRepeatV; } else { // Corners are always stretched to fit the corner. unitSize.width = borderWidth[i]; unitSize.height = borderHeight[j]; - fillStyleH = NS_STYLE_BORDER_IMAGE_STRETCH; - fillStyleV = NS_STYLE_BORDER_IMAGE_STRETCH; + fillStyleH = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH; + fillStyleV = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH; } DrawBorderImageComponent(aRenderingContext, aForFrame, @@ -2920,7 +2974,7 @@ DrawBorderImage(nsPresContext* aPresContext, } static void -DrawBorderImageComponent(nsRenderingContext& aRenderingContext, +DrawBorderImageComponent(nsRenderingContext& aRenderingContext, nsIFrame* aForFrame, imgIContainer* aImage, const nsRect& aDirtyRect, @@ -2956,8 +3010,8 @@ DrawBorderImageComponent(nsRenderingContext& aRenderingContext, // If we have no tiling in either direction, we can skip the intermediate // scaling step. - if ((aHFill == NS_STYLE_BORDER_IMAGE_STRETCH && - aVFill == NS_STYLE_BORDER_IMAGE_STRETCH) || + if ((aHFill == NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH && + aVFill == NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH) || (aUnitSize.width == aFill.width && aUnitSize.height == aFill.height)) { nsLayoutUtils::DrawSingleImage(&aRenderingContext, subImage, @@ -2969,39 +3023,35 @@ DrawBorderImageComponent(nsRenderingContext& aRenderingContext, // Compute the scale and position of the master copy of the image. nsRect tile; switch (aHFill) { - case NS_STYLE_BORDER_IMAGE_STRETCH: + case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH: tile.x = aFill.x; tile.width = aFill.width; break; - case NS_STYLE_BORDER_IMAGE_REPEAT: + case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT: tile.x = aFill.x + aFill.width/2 - aUnitSize.width/2; tile.width = aUnitSize.width; break; - - case NS_STYLE_BORDER_IMAGE_ROUND: + case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND: tile.x = aFill.x; tile.width = aFill.width / ceil(gfxFloat(aFill.width)/aUnitSize.width); break; - default: NS_NOTREACHED("unrecognized border-image fill style"); } switch (aVFill) { - case NS_STYLE_BORDER_IMAGE_STRETCH: + case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH: tile.y = aFill.y; tile.height = aFill.height; break; - case NS_STYLE_BORDER_IMAGE_REPEAT: + case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT: tile.y = aFill.y + aFill.height/2 - aUnitSize.height/2; tile.height = aUnitSize.height; break; - - case NS_STYLE_BORDER_IMAGE_ROUND: + case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND: tile.y = aFill.y; tile.height = aFill.height/ceil(gfxFloat(aFill.height)/aUnitSize.height); break; - default: NS_NOTREACHED("unrecognized border-image fill style"); } diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index dd54236d7e10..1ff4ce89acf4 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1451,8 +1451,9 @@ nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder, nsRect nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder) { - return SnapBounds(mSnappingEnabled, mFrame->PresContext(), - nsRect(ToReferenceFrame(), mFrame->GetSize())); + nsRect borderBounds(ToReferenceFrame(), mFrame->GetSize()); + borderBounds.Inflate(mFrame->GetStyleBorder()->GetImageOutset()); + return SnapBounds(mSnappingEnabled, mFrame->PresContext(), borderBounds); } // Given a region, compute a conservative approximation to it as a list diff --git a/layout/base/nsImageLoader.cpp b/layout/base/nsImageLoader.cpp index b8f03de013bb..347b1738b5aa 100644 --- a/layout/base/nsImageLoader.cpp +++ b/layout/base/nsImageLoader.cpp @@ -179,9 +179,6 @@ NS_IMETHODIMP nsImageLoader::OnStopFrame(imgIRequest *aRequest, } // Take requested actions - if (mActions & ACTION_REFLOW_ON_DECODE) { - DoReflow(); - } if (mActions & ACTION_REDRAW_ON_DECODE) { DoRedraw(nsnull); } @@ -209,9 +206,6 @@ NS_IMETHODIMP nsImageLoader::OnStopRequest(imgIRequest *aRequest, } // Take requested actions - if (mActions & ACTION_REFLOW_ON_LOAD) { - DoReflow(); - } if (mActions & ACTION_REDRAW_ON_LOAD) { DoRedraw(nsnull); } @@ -238,13 +232,6 @@ NS_IMETHODIMP nsImageLoader::FrameChanged(imgIContainer *aContainer, return NS_OK; } -void -nsImageLoader::DoReflow() -{ - nsIPresShell *shell = mFrame->PresContext()->GetPresShell(); - shell->FrameNeedsReflow(mFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); -} - void nsImageLoader::DoRedraw(const nsRect* aDamageRect) { diff --git a/layout/base/nsImageLoader.h b/layout/base/nsImageLoader.h index 8cc8232f5ed8..0cdba70d7f1a 100644 --- a/layout/base/nsImageLoader.h +++ b/layout/base/nsImageLoader.h @@ -69,10 +69,8 @@ public: * from the network. */ enum { - ACTION_REFLOW_ON_DECODE = 0x01, - ACTION_REDRAW_ON_DECODE = 0x02, - ACTION_REFLOW_ON_LOAD = 0x04, - ACTION_REDRAW_ON_LOAD = 0x08 + ACTION_REDRAW_ON_DECODE = 0x01, + ACTION_REDRAW_ON_LOAD = 0x02, }; static already_AddRefed Create(nsIFrame *aFrame, imgIRequest *aRequest, @@ -102,7 +100,6 @@ public: private: nsresult Load(imgIRequest *aImage); - void DoReflow(); /* if aDamageRect is nsnull, the whole frame is redrawn. */ void DoRedraw(const nsRect* aDamageRect); diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index d2d1c4b9ce0a..9ca898217ab5 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -1382,8 +1382,6 @@ nsPresContext::SetupBorderImageLoaders(nsIFrame* aFrame, } PRUint32 actions = nsImageLoader::ACTION_REDRAW_ON_LOAD; - if (aStyleBorder->ImageBorderDiffers()) - actions |= nsImageLoader::ACTION_REFLOW_ON_LOAD; nsRefPtr loader = nsImageLoader::Create(aFrame, borderImage, actions, nsnull); SetImageLoaders(aFrame, BORDER_IMAGE, loader); diff --git a/layout/base/nsStyleConsts.h b/layout/base/nsStyleConsts.h index 144630346973..65b9c4b2fe5f 100644 --- a/layout/base/nsStyleConsts.h +++ b/layout/base/nsStyleConsts.h @@ -328,9 +328,12 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) { #define NS_STYLE_BORDER_STYLE_AUTO 10 // for outline-style only // See nsStyleBorder mBorderImage -#define NS_STYLE_BORDER_IMAGE_STRETCH 0 -#define NS_STYLE_BORDER_IMAGE_REPEAT 1 -#define NS_STYLE_BORDER_IMAGE_ROUND 2 +#define NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH 0 +#define NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT 1 +#define NS_STYLE_BORDER_IMAGE_REPEAT_ROUND 2 + +#define NS_STYLE_BORDER_IMAGE_SLICE_NOFILL 0 +#define NS_STYLE_BORDER_IMAGE_SLICE_FILL 1 // See nsStyleDisplay #define NS_STYLE_CLEAR_NONE 0 diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 9ed608ae006c..56da35c346d5 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -4748,7 +4748,30 @@ ComputeOutlineAndEffectsRect(nsIFrame* aFrame, bool* aAnyOutlineOrEffects, *aAnyOutlineOrEffects = true; } } - + + // border-image-outset. + // We need to include border-image-outset because it can cause the + // border image to be drawn beyond the border box. + + // (1) It's important we not check whether there's a border-image + // since the style hint for a change in border image doesn't cause + // reflow, and that's probably more important than optimizing the + // overflow areas for the silly case of border-image-outset without + // border-image + // (2) It's important that we not check whether the border-image + // is actually loaded, since that would require us to reflow when + // the image loads. + const nsStyleBorder* styleBorder = aFrame->GetStyleBorder(); + nsMargin outsetMargin = styleBorder->GetImageOutset(); + + if (outsetMargin != nsMargin(0, 0, 0, 0)) { + nsRect outsetRect(nsPoint(0, 0), aNewSize); + outsetRect.Inflate(outsetMargin); + r.UnionRect(r, outsetRect); + + *aAnyOutlineOrEffects = true; + } + // Note that we don't remove the outlineInnerRect if a frame loses outline // style. That would require an extra property lookup for every frame, // or a new frame state bit to track whether a property had been stored, diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp index dae6bd63aaf1..d6ba485a02bc 100644 --- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -299,6 +299,17 @@ Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const } break; } + case eCSSProperty_border_image: + AppendValueToString(eCSSProperty_border_image_source, aValue); + aValue.Append(PRUnichar(' ')); + AppendValueToString(eCSSProperty_border_image_slice, aValue); + aValue.Append(NS_LITERAL_STRING(" / ")); + AppendValueToString(eCSSProperty_border_image_width, aValue); + aValue.Append(NS_LITERAL_STRING(" / ")); + AppendValueToString(eCSSProperty_border_image_outset, aValue); + aValue.Append(PRUnichar(' ')); + AppendValueToString(eCSSProperty_border_image_repeat, aValue); + break; case eCSSProperty_border: { const nsCSSProperty* subproptables[3] = { nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color), diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 256f91f3c3c2..b55baff566a0 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=78: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -128,6 +129,7 @@ using namespace mozilla; // Common combinations of variants #define VARIANT_AL (VARIANT_AUTO | VARIANT_LENGTH) #define VARIANT_LP (VARIANT_LENGTH | VARIANT_PERCENT) +#define VARIANT_LN (VARIANT_LENGTH | VARIANT_NUMBER) #define VARIANT_AH (VARIANT_AUTO | VARIANT_INHERIT) #define VARIANT_AHLP (VARIANT_AH | VARIANT_LP) #define VARIANT_AHI (VARIANT_AH | VARIANT_INTEGER) @@ -154,6 +156,8 @@ using namespace mozilla; #define VARIANT_HUO (VARIANT_INHERIT | VARIANT_URL | VARIANT_NONE) #define VARIANT_AHUO (VARIANT_AUTO | VARIANT_HUO) #define VARIANT_HPN (VARIANT_INHERIT | VARIANT_PERCENT | VARIANT_NUMBER) +#define VARIANT_PN (VARIANT_PERCENT | VARIANT_NUMBER) +#define VARIANT_ALPN (VARIANT_AL | VARIANT_PN) #define VARIANT_HN (VARIANT_INHERIT | VARIANT_NUMBER) #define VARIANT_HON (VARIANT_HN | VARIANT_NONE) #define VARIANT_HOS (VARIANT_INHERIT | VARIANT_NONE | VARIANT_STRING) @@ -478,6 +482,15 @@ protected: bool ParseBackgroundSizeValues(nsCSSValuePair& aOut); bool ParseBorderColor(); bool ParseBorderColors(nsCSSProperty aProperty); + void SetBorderImageInitialValues(); + bool ParseBorderImageRepeat(bool aAcceptsInherit); + // If ParseBorderImageSlice returns false, aConsumedTokens indicates + // whether or not any tokens were consumed (in other words, was the property + // in error or just not present). If ParseBorderImageSlice returns true + // aConsumedTokens is always true. + bool ParseBorderImageSlice(bool aAcceptsInherit, bool* aConsumedTokens); + bool ParseBorderImageWidth(bool aAcceptsInherit); + bool ParseBorderImageOutset(bool aAcceptsInherit); bool ParseBorderImage(); bool ParseBorderSpacing(); bool ParseBorderSide(const nsCSSProperty aPropIDs[], @@ -551,6 +564,8 @@ protected: // Reused utility parsing routines void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue); bool ParseBoxProperties(const nsCSSProperty aPropIDs[]); + bool ParseGroupedBoxProperty(PRInt32 aVariantMask, + nsCSSValue& aValue); bool ParseDirectionalBoxProperty(nsCSSProperty aProperty, PRInt32 aSourceType); bool ParseBoxCornerRadius(const nsCSSProperty aPropID); @@ -5210,6 +5225,40 @@ CSSParserImpl::ParseBoxProperties(const nsCSSProperty aPropIDs[]) return true; } +// Similar to ParseBoxProperties, except there is only one property +// with the result as its value, not four. Requires values be nonnegative. +bool +CSSParserImpl::ParseGroupedBoxProperty(PRInt32 aVariantMask, + /** outparam */ nsCSSValue& aValue) +{ + nsCSSRect& result = aValue.SetRectValue(); + + PRInt32 count = 0; + NS_FOR_CSS_SIDES (index) { + if (!ParseNonNegativeVariant(result.*(nsCSSRect::sides[index]), + aVariantMask, nsnull)) { + break; + } + count++; + } + + if (count == 0) { + return false; + } + + // Provide missing values by replicating some of the values found + switch (count) { + case 1: // Make right == top + result.mRight = result.mTop; + case 2: // Make bottom == top + result.mBottom = result.mTop; + case 3: // Make left == right + result.mLeft = result.mRight; + } + + return true; +} + bool CSSParserImpl::ParseDirectionalBoxProperty(nsCSSProperty aProperty, PRInt32 aSourceType) @@ -5433,6 +5482,14 @@ CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID) case eCSSProperty_border_right_colors: case eCSSProperty_border_top_colors: return ParseBorderColors(aPropID); + case eCSSProperty_border_image_slice: + return ParseBorderImageSlice(true, nsnull); + case eCSSProperty_border_image_width: + return ParseBorderImageWidth(true); + case eCSSProperty_border_image_outset: + return ParseBorderImageOutset(true); + case eCSSProperty_border_image_repeat: + return ParseBorderImageRepeat(true); case eCSSProperty_border_image: return ParseBorderImage(); case eCSSProperty_border_width: @@ -6254,83 +6311,250 @@ CSSParserImpl::ParseBorderColor() return ParseBoxProperties(kBorderColorIDs); } -bool -CSSParserImpl::ParseBorderImage() +void +CSSParserImpl::SetBorderImageInitialValues() { - nsCSSValue val; - if (ParseVariant(val, VARIANT_INHERIT | VARIANT_NONE, nsnull)) { - AppendValue(eCSSProperty_border_image, val); + // border-image-source: none + nsCSSValue source; + source.SetNoneValue(); + AppendValue(eCSSProperty_border_image_source, source); + + // border-image-slice: 100% + nsCSSValue sliceBoxValue; + nsCSSRect& sliceBox = sliceBoxValue.SetRectValue(); + sliceBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Percent)); + nsCSSValue slice; + nsCSSValueList* sliceList = slice.SetListValue(); + sliceList->mValue = sliceBoxValue; + AppendValue(eCSSProperty_border_image_slice, slice); + + // border-image-width: 1 + nsCSSValue width; + nsCSSRect& widthBox = width.SetRectValue(); + widthBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Number)); + AppendValue(eCSSProperty_border_image_width, width); + + // border-image-outset: 0 + nsCSSValue outset; + nsCSSRect& outsetBox = outset.SetRectValue(); + outsetBox.SetAllSidesTo(nsCSSValue(0.0f, eCSSUnit_Number)); + AppendValue(eCSSProperty_border_image_outset, outset); + + // border-image-repeat: repeat + nsCSSValue repeat; + nsCSSValuePair repeatPair; + repeatPair.SetBothValuesTo(nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, + eCSSUnit_Enumerated)); + repeat.SetPairValue(&repeatPair); + AppendValue(eCSSProperty_border_image_repeat, repeat); +} + +bool +CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit, + bool* aConsumedTokens) +{ + // border-image-slice: initial | [|]{1,4} && fill? + nsCSSValue value; + + if (aConsumedTokens) { + *aConsumedTokens = true; + } + + if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nsnull)) { + // Keyword "inherit" can not be mixed, so we are done. + AppendValue(eCSSProperty_border_image_slice, value); return true; } - // [ | ]{1,4} - // [ / {1,4} ]? [stretch | repeat | round]{0,2} - nsRefPtr arr = nsCSSValue::Array::Create(11); + // Try parsing "fill" value. + nsCSSValue imageSliceFillValue; + bool hasFill = ParseEnum(imageSliceFillValue, + nsCSSProps::kBorderImageSliceKTable); - nsCSSValue& url = arr->Item(0); - nsCSSValue& splitTop = arr->Item(1); - nsCSSValue& splitRight = arr->Item(2); - nsCSSValue& splitBottom = arr->Item(3); - nsCSSValue& splitLeft = arr->Item(4); - nsCSSValue& borderWidthTop = arr->Item(5); - nsCSSValue& borderWidthRight = arr->Item(6); - nsCSSValue& borderWidthBottom = arr->Item(7); - nsCSSValue& borderWidthLeft = arr->Item(8); - nsCSSValue& horizontalKeyword = arr->Item(9); - nsCSSValue& verticalKeyword = arr->Item(10); + // Parse the box dimensions. + nsCSSValue imageSliceBoxValue; + if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue)) { + if (!hasFill && aConsumedTokens) { + *aConsumedTokens = false; + } - // - if (!ParseVariant(url, VARIANT_URL, nsnull)) { return false; } - // [ | ]{1,4} - if (!ParseNonNegativeVariant(splitTop, - VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) { - return false; - } - if (!ParseNonNegativeVariant(splitRight, - VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) { - splitRight = splitTop; - } - if (!ParseNonNegativeVariant(splitBottom, - VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) { - splitBottom = splitTop; - } - if (!ParseNonNegativeVariant(splitLeft, - VARIANT_NUMBER | VARIANT_PERCENT, nsnull)) { - splitLeft = splitRight; + // Try parsing "fill" keyword again if the first time failed because keyword + // and slice dimensions can be in any order. + if (!hasFill) { + hasFill = ParseEnum(imageSliceFillValue, + nsCSSProps::kBorderImageSliceKTable); } - // [ / {1,4} ]? - if (ExpectSymbol('/', true)) { - // if have '/', at least one value is required - if (!ParseNonNegativeVariant(borderWidthTop, VARIANT_LENGTH, nsnull)) { - return false; - } - if (!ParseNonNegativeVariant(borderWidthRight, VARIANT_LENGTH, nsnull)) { - borderWidthRight = borderWidthTop; - } - if (!ParseNonNegativeVariant(borderWidthBottom, VARIANT_LENGTH, nsnull)) { - borderWidthBottom = borderWidthTop; - } - if (!ParseNonNegativeVariant(borderWidthLeft, VARIANT_LENGTH, nsnull)) { - borderWidthLeft = borderWidthRight; - } + nsCSSValueList* borderImageSlice = value.SetListValue(); + // Put the box value into the list. + borderImageSlice->mValue = imageSliceBoxValue; + + if (hasFill) { + // Put the "fill" value into the list. + borderImageSlice->mNext = new nsCSSValueList; + borderImageSlice->mNext->mValue = imageSliceFillValue; } - // [stretch | repeat | round]{0,2} - // missing keywords are handled in nsRuleNode::ComputeBorderData() - if (ParseEnum(horizontalKeyword, nsCSSProps::kBorderImageKTable)) { - (void)ParseEnum(verticalKeyword, nsCSSProps::kBorderImageKTable); + AppendValue(eCSSProperty_border_image_slice, value); + return true; +} + +bool +CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit) +{ + // border-image-width: initial | [|||auto]{1,4} + nsCSSValue value; + + if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nsnull)) { + // Keyword "inherit" can no be mixed, so we are done. + AppendValue(eCSSProperty_border_image_width, value); + return true; } - if (!ExpectEndProperty()) { + // Parse the box dimensions. + if (!ParseGroupedBoxProperty(VARIANT_ALPN, value)) { return false; } - val.SetArrayValue(arr, eCSSUnit_Array); - AppendValue(eCSSProperty_border_image, val); + AppendValue(eCSSProperty_border_image_width, value); + return true; +} + +bool +CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit) +{ + // border-image-outset: initial | [|]{1,4} + nsCSSValue value; + + if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nsnull)) { + // Keyword "inherit" can not be mixed, so we are done. + AppendValue(eCSSProperty_border_image_outset, value); + return true; + } + + // Parse the box dimensions. + if (!ParseGroupedBoxProperty(VARIANT_LN, value)) { + return false; + } + + AppendValue(eCSSProperty_border_image_outset, value); + return true; +} + +bool +CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit) +{ + nsCSSValue value; + if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nsnull)) { + // Keyword "inherit" can not be mixed, so we are done. + AppendValue(eCSSProperty_border_image_repeat, value); + return true; + } + + nsCSSValuePair result; + if (!ParseEnum(result.mXValue, nsCSSProps::kBorderImageRepeatKTable)) { + return false; + } + + // optional second keyword, defaults to first + if (!ParseEnum(result.mYValue, nsCSSProps::kBorderImageRepeatKTable)) { + result.mYValue = result.mXValue; + } + + value.SetPairValue(&result); + AppendValue(eCSSProperty_border_image_repeat, value); + return true; +} + +bool +CSSParserImpl::ParseBorderImage() +{ + nsAutoParseCompoundProperty compound(this); + + // border-image: inherit | + // || + // + // [ / ? + // [ / ]?]? || + // + + nsCSSValue value; + if (ParseVariant(value, VARIANT_INHERIT, nsnull)) { + AppendValue(eCSSProperty_border_image_source, value); + AppendValue(eCSSProperty_border_image_slice, value); + AppendValue(eCSSProperty_border_image_width, value); + AppendValue(eCSSProperty_border_image_outset, value); + AppendValue(eCSSProperty_border_image_repeat, value); + // Keyword "inherit" can't be mixed, so we are done. + return true; + } + + // No empty property. + if (CheckEndProperty()) { + return false; + } + + // Shorthand properties are required to set everything they can. + SetBorderImageInitialValues(); + + bool foundSource = false; + bool foundSliceWidthOutset = false; + bool foundRepeat = false; + + // This loop is used to handle the parsing of border-image properties which + // can appear in any order. + nsCSSValue imageSourceValue; + while (!CheckEndProperty()) { + // + if (!foundSource && ParseVariant(imageSourceValue, VARIANT_UO, nsnull)) { + AppendValue(eCSSProperty_border_image_source, imageSourceValue); + foundSource = true; + continue; + } + + // + // ParseBorderImageSlice is weird. It may consume tokens and then return + // false, because it parses a property with two required components that + // can appear in either order. Since the tokens that were consumed cannot + // parse as anything else we care about, this isn't a problem. + if (!foundSliceWidthOutset) { + bool sliceConsumedTokens = false; + if (ParseBorderImageSlice(false, &sliceConsumedTokens)) { + foundSliceWidthOutset = true; + + // [ / ? + if (ExpectSymbol('/', true)) { + ParseBorderImageWidth(false); + + // [ / + if (ExpectSymbol('/', true)) { + if (!ParseBorderImageOutset(false)) { + return false; + } + } + } + + continue; + } else { + // If we consumed some tokens for but did not + // successfully parse it, we have an error. + if (sliceConsumedTokens) { + return false; + } + } + } + + // + if (!foundRepeat && ParseBorderImageRepeat(false)) { + foundRepeat = true; + continue; + } + + return false; + } return true; } @@ -6423,14 +6647,24 @@ CSSParserImpl::ParseBorderSide(const nsCSSProperty aPropIDs[], // initial values. nsCSSValue extraValue; switch (values[0].GetUnit()) { - case eCSSUnit_Inherit: extraValue.SetInheritValue(); break; - case eCSSUnit_Initial: extraValue.SetInitialValue(); break; - default: extraValue.SetNoneValue(); break; + case eCSSUnit_Inherit: + case eCSSUnit_Initial: + extraValue = values[0]; + // Set value of border-image properties to initial/inherit + AppendValue(eCSSProperty_border_image_source, extraValue); + AppendValue(eCSSProperty_border_image_slice, extraValue); + AppendValue(eCSSProperty_border_image_width, extraValue); + AppendValue(eCSSProperty_border_image_outset, extraValue); + AppendValue(eCSSProperty_border_image_repeat, extraValue); + break; + default: + extraValue.SetNoneValue(); + SetBorderImageInitialValues(); + break; } NS_FOR_CSS_SIDES(side) { AppendValue(kBorderColorsProps[side], extraValue); } - AppendValue(eCSSProperty_border_image, extraValue); } else { // Just set our one side diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 69268eee5a4a..0fad63899228 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -699,16 +699,60 @@ CSS_PROP_BORDER( CSS_PROP_NO_OFFSET, eStyleAnimType_None) #endif -CSS_PROP_BORDER( +CSS_PROP_SHORTHAND( -moz-border-image, border_image, CSS_PROP_DOMPROP_PREFIXED(BorderImage), - CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_PARSE_FUNCTION) +CSS_PROP_BORDER( + -moz-border-image-source, + border_image_source, + CSS_PROP_DOMPROP_PREFIXED(BorderImageSource), + CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | - CSS_PROPERTY_START_IMAGE_LOADS | - CSS_PROPERTY_IMAGE_IS_IN_ARRAY_0, + CSS_PROPERTY_START_IMAGE_LOADS, + VARIANT_HUO, + nsnull, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_BORDER( + -moz-border-image-slice, + border_image_slice, + CSS_PROP_DOMPROP_PREFIXED(BorderImageSlice), + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, 0, - kBorderImageKTable, + kBorderImageSliceKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_BORDER( + -moz-border-image-width, + border_image_width, + CSS_PROP_DOMPROP_PREFIXED(BorderImageWidth), + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + 0, + nsnull, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_BORDER( + -moz-border-image-outset, + border_image_outset, + CSS_PROP_DOMPROP_PREFIXED(BorderImageOutset), + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + 0, + nsnull, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_BORDER( + -moz-border-image-repeat, + border_image_repeat, + CSS_PROP_DOMPROP_PREFIXED(BorderImageRepeat), + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + 0, + kBorderImageRepeatKTable, CSS_PROP_NO_OFFSET, eStyleAnimType_None) CSS_PROP_SHORTHAND( diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 152df6afec05..d92f1329a662 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -657,10 +657,15 @@ const PRInt32 nsCSSProps::kBorderColorKTable[] = { eCSSKeyword_UNKNOWN,-1 }; -const PRInt32 nsCSSProps::kBorderImageKTable[] = { - eCSSKeyword_stretch, NS_STYLE_BORDER_IMAGE_STRETCH, - eCSSKeyword_repeat, NS_STYLE_BORDER_IMAGE_REPEAT, - eCSSKeyword_round, NS_STYLE_BORDER_IMAGE_ROUND, +const PRInt32 nsCSSProps::kBorderImageRepeatKTable[] = { + eCSSKeyword_stretch, NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, + eCSSKeyword_repeat, NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT, + eCSSKeyword_round, NS_STYLE_BORDER_IMAGE_REPEAT_ROUND, + eCSSKeyword_UNKNOWN,-1 +}; + +const PRInt32 nsCSSProps::kBorderImageSliceKTable[] = { + eCSSKeyword_fill, NS_STYLE_BORDER_IMAGE_SLICE_FILL, eCSSKeyword_UNKNOWN,-1 }; @@ -1670,7 +1675,11 @@ static const nsCSSProperty gBorderSubpropTable[] = { eCSSProperty_border_right_colors, eCSSProperty_border_bottom_colors, eCSSProperty_border_left_colors, - eCSSProperty_border_image, + eCSSProperty_border_image_source, + eCSSProperty_border_image_slice, + eCSSProperty_border_image_width, + eCSSProperty_border_image_outset, + eCSSProperty_border_image_repeat, eCSSProperty_UNKNOWN }; @@ -2060,6 +2069,15 @@ static const nsCSSProperty gTransitionSubpropTable[] = { eCSSProperty_UNKNOWN }; +static const nsCSSProperty gBorderImageSubpropTable[] = { + eCSSProperty_border_image_source, + eCSSProperty_border_image_slice, + eCSSProperty_border_image_width, + eCSSProperty_border_image_outset, + eCSSProperty_border_image_repeat, + eCSSProperty_UNKNOWN +}; + static const nsCSSProperty gMarkerSubpropTable[] = { eCSSProperty_marker_start, eCSSProperty_marker_mid, diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index ab1dc3f9b7db..3cf6d3f3454e 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -343,7 +343,8 @@ public: static const PRInt32 kBackgroundSizeKTable[]; static const PRInt32 kBorderCollapseKTable[]; static const PRInt32 kBorderColorKTable[]; - static const PRInt32 kBorderImageKTable[]; + static const PRInt32 kBorderImageRepeatKTable[]; + static const PRInt32 kBorderImageSliceKTable[]; static const PRInt32 kBorderStyleKTable[]; static const PRInt32 kBorderWidthKTable[]; static const PRInt32 kBoxAlignKTable[]; diff --git a/layout/style/nsCSSValue.cpp b/layout/style/nsCSSValue.cpp index b1ef513d4c60..e435166b2ec9 100644 --- a/layout/style/nsCSSValue.cpp +++ b/layout/style/nsCSSValue.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -751,14 +752,6 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const nsCSSValue::Array *array = GetArrayValue(); bool mark = false; for (size_t i = 0, i_end = array->Count(); i < i_end; ++i) { - if (aProperty == eCSSProperty_border_image && i >= 5) { - if (array->Item(i).GetUnit() == eCSSUnit_Null) { - continue; - } - if (i == 5) { - aResult.AppendLiteral(" /"); - } - } if (mark && array->Item(i).GetUnit() != eCSSUnit_Null) { if (unit == eCSSUnit_Array && eCSSProperty_transition_timing_function != aProperty) @@ -1211,17 +1204,31 @@ nsCSSRect::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const mTop.GetUnit() != eCSSUnit_Initial, "parser should have used a bare value"); - NS_NAMED_LITERAL_STRING(comma, ", "); + if (eCSSProperty_border_image_slice == aProperty || + eCSSProperty_border_image_width == aProperty || + eCSSProperty_border_image_outset == aProperty) { + NS_NAMED_LITERAL_STRING(space, " "); - aResult.AppendLiteral("rect("); - mTop.AppendToString(aProperty, aResult); - aResult.Append(comma); - mRight.AppendToString(aProperty, aResult); - aResult.Append(comma); - mBottom.AppendToString(aProperty, aResult); - aResult.Append(comma); - mLeft.AppendToString(aProperty, aResult); - aResult.Append(PRUnichar(')')); + mTop.AppendToString(aProperty, aResult); + aResult.Append(space); + mRight.AppendToString(aProperty, aResult); + aResult.Append(space); + mBottom.AppendToString(aProperty, aResult); + aResult.Append(space); + mLeft.AppendToString(aProperty, aResult); + } else { + NS_NAMED_LITERAL_STRING(comma, ", "); + + aResult.AppendLiteral("rect("); + mTop.AppendToString(aProperty, aResult); + aResult.Append(comma); + mRight.AppendToString(aProperty, aResult); + aResult.Append(comma); + mBottom.AppendToString(aProperty, aResult); + aResult.Append(comma); + mLeft.AppendToString(aProperty, aResult); + aResult.Append(PRUnichar(')')); + } } void nsCSSRect::SetAllSidesTo(const nsCSSValue& aValue) diff --git a/layout/style/nsCSSValue.h b/layout/style/nsCSSValue.h index 3fe4cc755245..d49cd9b37909 100644 --- a/layout/style/nsCSSValue.h +++ b/layout/style/nsCSSValue.h @@ -763,14 +763,14 @@ struct nsCSSRect_heap : public nsCSSRect { inline nsCSSRect& nsCSSValue::GetRectValue() { - NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Rect, "not a pair value"); + NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Rect, "not a rect value"); return *mValue.mRect; } inline const nsCSSRect& nsCSSValue::GetRectValue() const { - NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Rect, "not a pair value"); + NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Rect, "not a rect value"); return *mValue.mRect; } diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 29a3995b8523..89ef8dde81c9 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set tw=78 expandtab softtabstop=2 ts=2 sw=2: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -2820,61 +2821,101 @@ nsComputedDOMStyle::DoGetBoxSizing() return val; } +/* Border image properties */ + nsIDOMCSSValue* -nsComputedDOMStyle::DoGetBorderImage() +nsComputedDOMStyle::DoGetBorderImageSource() { const nsStyleBorder* border = GetStyleBorder(); - // none - if (!border->GetBorderImage()) { - nsROCSSPrimitiveValue *valNone = GetROCSSPrimitiveValue(); - valNone->SetIdent(eCSSKeyword_none); - return valNone; + imgIRequest* imgSrc = border->GetBorderImage(); + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + if (imgSrc) { + nsCOMPtr uri; + imgSrc->GetURI(getter_AddRefs(uri)); + val->SetURI(uri); + } else { + val->SetIdent(eCSSKeyword_none); } + return val; +} + +nsIDOMCSSValue* +nsComputedDOMStyle::DoGetBorderImageSlice() +{ + nsDOMCSSValueList* valueList = GetROCSSValueList(false); + + const nsStyleBorder* border = GetStyleBorder(); + // Four slice numbers. + NS_FOR_CSS_SIDES (side) { + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + valueList->AppendCSSValue(val); + SetValueToCoord(val, border->mBorderImageSlice.Get(side), nsnull, nsnull); + } + + // Fill keyword. + if (NS_STYLE_BORDER_IMAGE_SLICE_FILL == border->mBorderImageFill) { + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + valueList->AppendCSSValue(val); + val->SetIdent(eCSSKeyword_fill); + } + + return valueList; +} + +nsIDOMCSSValue* +nsComputedDOMStyle::DoGetBorderImageWidth() +{ + const nsStyleBorder* border = GetStyleBorder(); + nsDOMCSSValueList* valueList = GetROCSSValueList(false); + NS_FOR_CSS_SIDES (side) { + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + valueList->AppendCSSValue(val); + SetValueToCoord(val, border->mBorderImageWidth.Get(side), + nsnull, nsnull); + } + + return valueList; +} + +nsIDOMCSSValue* +nsComputedDOMStyle::DoGetBorderImageOutset() +{ nsDOMCSSValueList *valueList = GetROCSSValueList(false); - // uri - nsROCSSPrimitiveValue *valURI = GetROCSSPrimitiveValue(); - valueList->AppendCSSValue(valURI); - nsCOMPtr uri; - border->GetBorderImage()->GetURI(getter_AddRefs(uri)); - valURI->SetURI(uri); - - // four split numbers - NS_FOR_CSS_SIDES(side) { - nsROCSSPrimitiveValue *valSplit = GetROCSSPrimitiveValue(); - valueList->AppendCSSValue(valSplit); - SetValueToCoord(valSplit, border->mBorderImageSplit.Get(side), true); + const nsStyleBorder* border = GetStyleBorder(); + // four slice numbers + NS_FOR_CSS_SIDES (side) { + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + valueList->AppendCSSValue(val); + SetValueToCoord(val, border->mBorderImageOutset.Get(side), + nsnull, nsnull); } - // copy of border-width - if (border->mHaveBorderImageWidth) { - nsROCSSPrimitiveValue *slash = GetROCSSPrimitiveValue(); - valueList->AppendCSSValue(slash); - slash->SetString(NS_LITERAL_STRING("/")); - NS_FOR_CSS_SIDES(side) { - nsROCSSPrimitiveValue *borderWidth = GetROCSSPrimitiveValue(); - valueList->AppendCSSValue(borderWidth); - nscoord width = GetStyleBorder()->mBorderImageWidth.Side(side); - borderWidth->SetAppUnits(width); - } - } + return valueList; +} - // first keyword - nsROCSSPrimitiveValue *keyword = GetROCSSPrimitiveValue(); - valueList->AppendCSSValue(keyword); - keyword->SetIdent( - nsCSSProps::ValueToKeywordEnum(GetStyleBorder()->mBorderImageHFill, - nsCSSProps::kBorderImageKTable)); +nsIDOMCSSValue* +nsComputedDOMStyle::DoGetBorderImageRepeat() +{ + nsDOMCSSValueList* valueList = GetROCSSValueList(false); - // second keyword - nsROCSSPrimitiveValue *keyword2 = GetROCSSPrimitiveValue(); - valueList->AppendCSSValue(keyword2); - keyword2->SetIdent( - nsCSSProps::ValueToKeywordEnum(GetStyleBorder()->mBorderImageVFill, - nsCSSProps::kBorderImageKTable)); + const nsStyleBorder* border = GetStyleBorder(); + // horizontal repeat + nsROCSSPrimitiveValue* valX = GetROCSSPrimitiveValue(); + valueList->AppendCSSValue(valX); + valX->SetIdent( + nsCSSProps::ValueToKeywordEnum(border->mBorderImageRepeatH, + nsCSSProps::kBorderImageRepeatKTable)); + + // vertical repeat + nsROCSSPrimitiveValue* valY = GetROCSSPrimitiveValue(); + valueList->AppendCSSValue(valY); + valY->SetIdent( + nsCSSProps::ValueToKeywordEnum(border->mBorderImageRepeatV, + nsCSSProps::kBorderImageRepeatKTable)); return valueList; } @@ -4558,7 +4599,12 @@ nsComputedDOMStyle::GetQueryablePropertyMap(PRUint32* aLength) COMPUTED_STYLE_MAP_ENTRY(_moz_background_inline_policy, BackgroundInlinePolicy), COMPUTED_STYLE_MAP_ENTRY(binding, Binding), COMPUTED_STYLE_MAP_ENTRY(border_bottom_colors, BorderBottomColors), - COMPUTED_STYLE_MAP_ENTRY(border_image, BorderImage), + //// COMPUTED_STYLE_MAP_ENTRY(border_image, BorderImage), + COMPUTED_STYLE_MAP_ENTRY(border_image_outset, BorderImageOutset), + COMPUTED_STYLE_MAP_ENTRY(border_image_repeat, BorderImageRepeat), + COMPUTED_STYLE_MAP_ENTRY(border_image_slice, BorderImageSlice), + COMPUTED_STYLE_MAP_ENTRY(border_image_source, BorderImageSource), + COMPUTED_STYLE_MAP_ENTRY(border_image_width, BorderImageWidth), COMPUTED_STYLE_MAP_ENTRY(border_left_colors, BorderLeftColors), COMPUTED_STYLE_MAP_ENTRY(border_right_colors, BorderRightColors), COMPUTED_STYLE_MAP_ENTRY(border_top_colors, BorderTopColors), diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index d1a432136bdd..bfd76714bdef 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -260,7 +260,13 @@ private: nsIDOMCSSValue* DoGetBorderTopLeftRadius(); nsIDOMCSSValue* DoGetBorderTopRightRadius(); nsIDOMCSSValue* DoGetFloatEdge(); - nsIDOMCSSValue* DoGetBorderImage(); + + /* Border Image */ + nsIDOMCSSValue* DoGetBorderImageSource(); + nsIDOMCSSValue* DoGetBorderImageSlice(); + nsIDOMCSSValue* DoGetBorderImageWidth(); + nsIDOMCSSValue* DoGetBorderImageOutset(); + nsIDOMCSSValue* DoGetBorderImageRepeat(); /* Box Shadow */ nsIDOMCSSValue* DoGetBoxShadow(); diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 4ddd70f340e0..a8d4fbaefd2a 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=78: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -583,10 +584,13 @@ GetFloatFromBoxPosition(PRInt32 aEnumValue) #define SETCOORD_INITIAL_NONE 0x800 #define SETCOORD_INITIAL_NORMAL 0x1000 #define SETCOORD_INITIAL_HALF 0x2000 -#define SETCOORD_CALC_LENGTH_ONLY 0x4000 -#define SETCOORD_CALC_CLAMP_NONNEGATIVE 0x8000 // modifier for CALC_LENGTH_ONLY -#define SETCOORD_STORE_CALC 0x00010000 -#define SETCOORD_BOX_POSITION 0x00020000 // exclusive with _ENUMERATED +#define SETCOORD_INITIAL_HUNDRED_PCT 0x00004000 +#define SETCOORD_INITIAL_FACTOR_ONE 0x00008000 +#define SETCOORD_INITIAL_FACTOR_ZERO 0x00010000 +#define SETCOORD_CALC_LENGTH_ONLY 0x00020000 +#define SETCOORD_CALC_CLAMP_NONNEGATIVE 0x00040000 // modifier for CALC_LENGTH_ONLY +#define SETCOORD_STORE_CALC 0x00080000 +#define SETCOORD_BOX_POSITION 0x00100000 // exclusive with _ENUMERATED #define SETCOORD_LP (SETCOORD_LENGTH | SETCOORD_PERCENT) #define SETCOORD_LH (SETCOORD_LENGTH | SETCOORD_INHERIT) @@ -673,6 +677,10 @@ static bool SetCoord(const nsCSSValue& aValue, nsStyleCoord& aCoord, (aValue.GetUnit() == eCSSUnit_Initial)) { aCoord.SetCoordValue(0); } + else if (((aMask & SETCOORD_INITIAL_FACTOR_ZERO) != 0) && + (aValue.GetUnit() == eCSSUnit_Initial)) { + aCoord.SetFactorValue(0.0f); + } else if (((aMask & SETCOORD_INITIAL_NONE) != 0) && (aValue.GetUnit() == eCSSUnit_Initial)) { aCoord.SetNoneValue(); @@ -685,6 +693,14 @@ static bool SetCoord(const nsCSSValue& aValue, nsStyleCoord& aCoord, (aValue.GetUnit() == eCSSUnit_Initial)) { aCoord.SetPercentValue(0.5f); } + else if (((aMask & SETCOORD_INITIAL_HUNDRED_PCT) != 0) && + (aValue.GetUnit() == eCSSUnit_Initial)) { + aCoord.SetPercentValue(1.0f); + } + else if (((aMask & SETCOORD_INITIAL_FACTOR_ONE) != 0) && + (aValue.GetUnit() == eCSSUnit_Initial)) { + aCoord.SetFactorValue(1.0f); + } else if (((aMask & SETCOORD_STORE_CALC) != 0) && (aValue.IsCalcUnit())) { SpecifiedCalcToComputedCalc(aValue, aCoord, aStyleContext, @@ -5269,6 +5285,80 @@ nsRuleNode::ComputeMarginData(void* aStartStruct, COMPUTE_END_RESET(Margin, margin) } +static void +SetBorderImageRect(const nsCSSValue& aValue, + /** outparam */ nsCSSRect& aRect) +{ + switch (aValue.GetUnit()) { + case eCSSUnit_Null: + aRect.Reset(); + break; + case eCSSUnit_Rect: + aRect = aValue.GetRectValue(); + break; + case eCSSUnit_Inherit: + case eCSSUnit_Initial: + aRect.SetAllSidesTo(aValue); + break; + default: + NS_ASSERTION(false, "Unexpected border image value for rect."); + } +} + +static void +SetBorderImagePair(const nsCSSValue& aValue, + /** outparam */ nsCSSValuePair& aPair) +{ + switch (aValue.GetUnit()) { + case eCSSUnit_Null: + aPair.Reset(); + break; + case eCSSUnit_Pair: + aPair = aValue.GetPairValue(); + break; + case eCSSUnit_Inherit: + case eCSSUnit_Initial: + aPair.SetBothValuesTo(aValue); + break; + default: + NS_ASSERTION(false, "Unexpected border image value for pair."); + } +} + +static void +SetBorderImageSlice(const nsCSSValue& aValue, + /** outparam */ nsCSSValue& aSlice, + /** outparam */ nsCSSValue& aFill) +{ + const nsCSSValueList* valueList; + switch (aValue.GetUnit()) { + case eCSSUnit_Null: + aSlice.Reset(); + aFill.Reset(); + break; + case eCSSUnit_List: + // Get slice dimensions. + valueList = aValue.GetListValue(); + aSlice = valueList->mValue; + + // Get "fill" keyword. + valueList = valueList->mNext; + if (valueList) { + aFill = valueList->mValue; + } else { + aFill.SetInitialValue(); + } + break; + case eCSSUnit_Inherit: + case eCSSUnit_Initial: + aSlice = aValue; + aFill = aValue; + break; + default: + NS_ASSERTION(false, "Unexpected border image value for pair."); + } +} + const void* nsRuleNode::ComputeBorderData(void* aStartStruct, const nsRuleData* aRuleData, @@ -5554,82 +5644,89 @@ nsRuleNode::ComputeBorderData(void* aStartStruct, SETDSC_ENUMERATED, parentBorder->mFloatEdge, NS_STYLE_FLOAT_EDGE_CONTENT, 0, 0, 0, 0); - // border-image - const nsCSSValue* borderImageValue = aRuleData->ValueForBorderImage(); - if (eCSSUnit_Array == borderImageValue->GetUnit()) { - nsCSSValue::Array *arr = borderImageValue->GetArrayValue(); - - // the image - if (eCSSUnit_Image == arr->Item(0).GetUnit()) { - NS_SET_IMAGE_REQUEST(border->SetBorderImage, - aContext, - arr->Item(0).GetImageValue()) - } - - // the numbers saying where to split the image - NS_FOR_CSS_SIDES(side) { - if (SetAbsCoord(arr->Item(1 + side), coord, - SETCOORD_FACTOR | SETCOORD_PERCENT)) { - border->mBorderImageSplit.Set(side, coord); - } - } - - // possible replacement for border-width - // if have one - have all four (see CSSParserImpl::ParseBorderImage()) - if (eCSSUnit_Null != arr->Item(5).GetUnit()) { - NS_FOR_CSS_SIDES(side) { - // an uninitialized parentCoord is ok because I'm not passing SETCOORD_INHERIT - if (!SetCoord(arr->Item(5 + side), coord, nsStyleCoord(), - SETCOORD_LENGTH, aContext, mPresContext, - canStoreInRuleTree)) { - NS_NOTREACHED("SetCoord for border-width replacement from border-image failed"); - } - if (coord.GetUnit() == eStyleUnit_Coord) { - border->SetBorderImageWidthOverride(side, coord.GetCoordValue()); - } else { - NS_WARNING("a border-width replacement from border-image " - "has a unit that's not eStyleUnit_Coord"); - border->SetBorderImageWidthOverride(side, 0); - } - } - border->mHaveBorderImageWidth = true; - } else { - border->mHaveBorderImageWidth = false; - } - - // stretch/round/repeat keywords - if (eCSSUnit_Null == arr->Item(9).GetUnit()) { - // default, both horizontal and vertical are stretch - border->mBorderImageHFill = NS_STYLE_BORDER_IMAGE_STRETCH; - border->mBorderImageVFill = NS_STYLE_BORDER_IMAGE_STRETCH; - } else { - // have horizontal value - border->mBorderImageHFill = arr->Item(9).GetIntValue(); - if (eCSSUnit_Null == arr->Item(10).GetUnit()) { - // vertical same as horizontal - border->mBorderImageVFill = border->mBorderImageHFill; - } else { - // have vertical value - border->mBorderImageVFill = arr->Item(10).GetIntValue(); - } - } - } else if (eCSSUnit_None == borderImageValue->GetUnit() || - eCSSUnit_Initial == borderImageValue->GetUnit()) { - border->mHaveBorderImageWidth = false; - border->SetBorderImage(nsnull); - } else if (eCSSUnit_Inherit == borderImageValue->GetUnit()) { - canStoreInRuleTree = false; - NS_FOR_CSS_SIDES(side) { - border->SetBorderImageWidthOverride(side, parentBorder->mBorderImageWidth.Side(side)); - } - border->mBorderImageSplit = parentBorder->mBorderImageSplit; - border->mBorderImageHFill = parentBorder->mBorderImageHFill; - border->mBorderImageVFill = parentBorder->mBorderImageVFill; - border->mHaveBorderImageWidth = parentBorder->mHaveBorderImageWidth; + // border-image-source + const nsCSSValue* borderImageSource = aRuleData->ValueForBorderImageSource(); + if (borderImageSource->GetUnit() == eCSSUnit_Image) { NS_SET_IMAGE_REQUEST(border->SetBorderImage, aContext, - parentBorder->GetBorderImage()) + borderImageSource->GetImageValue()); + } else if (borderImageSource->GetUnit() == eCSSUnit_Inherit) { + canStoreInRuleTree = false; + NS_SET_IMAGE_REQUEST(border->SetBorderImage, aContext, + parentBorder->GetBorderImage()); + } else if (borderImageSource->GetUnit() == eCSSUnit_Initial || + borderImageSource->GetUnit() == eCSSUnit_None) { + border->SetBorderImage(nsnull); } + nsCSSValue borderImageSliceValue; + nsCSSValue borderImageSliceFill; + SetBorderImageSlice(*aRuleData->ValueForBorderImageSlice(), + borderImageSliceValue, borderImageSliceFill); + + // border-image-slice: fill + SetDiscrete(borderImageSliceFill, + border->mBorderImageFill, + canStoreInRuleTree, SETDSC_ENUMERATED, + parentBorder->mBorderImageFill, + NS_STYLE_BORDER_IMAGE_SLICE_NOFILL, 0, 0, 0, 0); + + nsCSSRect borderImageSlice; + SetBorderImageRect(borderImageSliceValue, borderImageSlice); + + nsCSSRect borderImageWidth; + SetBorderImageRect(*aRuleData->ValueForBorderImageWidth(), + borderImageWidth); + + nsCSSRect borderImageOutset; + SetBorderImageRect(*aRuleData->ValueForBorderImageOutset(), + borderImageOutset); + + NS_FOR_CSS_SIDES (side) { + // border-image-slice + if (SetCoord(borderImageSlice.*(nsCSSRect::sides[side]), coord, + parentBorder->mBorderImageSlice.Get(side), + SETCOORD_FACTOR | SETCOORD_PERCENT | + SETCOORD_INHERIT | SETCOORD_INITIAL_HUNDRED_PCT, + aContext, mPresContext, canStoreInRuleTree)) { + border->mBorderImageSlice.Set(side, coord); + } + + // border-image-width + // 'auto' here means "same as slice" + if (SetCoord(borderImageWidth.*(nsCSSRect::sides[side]), coord, + parentBorder->mBorderImageWidth.Get(side), + SETCOORD_LPAH | SETCOORD_FACTOR | SETCOORD_INITIAL_FACTOR_ONE, + aContext, mPresContext, canStoreInRuleTree)) { + border->mBorderImageWidth.Set(side, coord); + } + + // border-image-outset + if (SetCoord(borderImageOutset.*(nsCSSRect::sides[side]), coord, + parentBorder->mBorderImageOutset.Get(side), + SETCOORD_LENGTH | SETCOORD_FACTOR | + SETCOORD_INHERIT | SETCOORD_INITIAL_FACTOR_ZERO, + aContext, mPresContext, canStoreInRuleTree)) { + border->mBorderImageOutset.Set(side, coord); + } + } + + // border-image-repeat + nsCSSValuePair borderImageRepeat; + SetBorderImagePair(*aRuleData->ValueForBorderImageRepeat(), + borderImageRepeat); + + SetDiscrete(borderImageRepeat.mXValue, + border->mBorderImageRepeatH, + canStoreInRuleTree, SETDSC_ENUMERATED, + parentBorder->mBorderImageRepeatH, + NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, 0, 0, 0, 0); + + SetDiscrete(borderImageRepeat.mYValue, + border->mBorderImageRepeatV, + canStoreInRuleTree, SETDSC_ENUMERATED, + parentBorder->mBorderImageRepeatV, + NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, 0, 0, 0, 0); + if (border->HasBorderImage()) border->TrackImage(aContext->PresContext()); diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 8aa5a85e3fe1..ffcb73cb19db 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -380,29 +380,35 @@ nsChangeHint nsStylePadding::MaxDifference() #endif nsStyleBorder::nsStyleBorder(nsPresContext* aPresContext) - : mHaveBorderImageWidth(false) + : mBorderColors(nsnull), + mBoxShadow(nsnull), #ifdef DEBUG - , mImageTracked(false) + mImageTracked(false), #endif - , mComputedBorder(0, 0, 0, 0) - , mBorderImage(nsnull) + mBorderImageSource(nsnull), + mBorderImageFill(NS_STYLE_BORDER_IMAGE_SLICE_NOFILL), + mBorderImageRepeatH(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH), + mBorderImageRepeatV(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH), + mFloatEdge(NS_STYLE_FLOAT_EDGE_CONTENT), + mComputedBorder(0, 0, 0, 0) { MOZ_COUNT_CTOR(nsStyleBorder); + + NS_FOR_CSS_HALF_CORNERS (corner) { + mBorderRadius.Set(corner, nsStyleCoord(0, nsStyleCoord::CoordConstructor)); + } + nscoord medium = (aPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]; NS_FOR_CSS_SIDES(side) { + mBorderImageSlice.Set(side, nsStyleCoord(1.0f, eStyleUnit_Percent)); + mBorderImageWidth.Set(side, nsStyleCoord(1.0f, eStyleUnit_Factor)); + mBorderImageOutset.Set(side, nsStyleCoord(0.0f, eStyleUnit_Factor)); + mBorder.Side(side) = medium; mBorderStyle[side] = NS_STYLE_BORDER_STYLE_NONE | BORDER_COLOR_FOREGROUND; mBorderColor[side] = NS_RGB(0, 0, 0); } - NS_FOR_CSS_HALF_CORNERS(corner) { - mBorderRadius.Set(corner, nsStyleCoord(0, nsStyleCoord::CoordConstructor)); - } - - mBorderColors = nsnull; - mBoxShadow = nsnull; - - mFloatEdge = NS_STYLE_FLOAT_EDGE_CONTENT; mTwipsPerPixel = aPresContext->DevPixelsToAppUnits(1); } @@ -424,18 +430,22 @@ nsBorderColors::Clone(bool aDeep) const } nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc) - : mBorderRadius(aSrc.mBorderRadius), - mBorderImageSplit(aSrc.mBorderImageSplit), - mFloatEdge(aSrc.mFloatEdge), - mBorderImageHFill(aSrc.mBorderImageHFill), - mBorderImageVFill(aSrc.mBorderImageVFill), - mBorderColors(nsnull), + : mBorderColors(nsnull), mBoxShadow(aSrc.mBoxShadow), - mHaveBorderImageWidth(aSrc.mHaveBorderImageWidth), +#ifdef DEBUG + mImageTracked(false), +#endif + mBorderImageSource(aSrc.mBorderImageSource), + mBorderRadius(aSrc.mBorderRadius), + mBorderImageSlice(aSrc.mBorderImageSlice), + mBorderImageFill(aSrc.mBorderImageFill), mBorderImageWidth(aSrc.mBorderImageWidth), + mBorderImageOutset(aSrc.mBorderImageOutset), + mBorderImageRepeatH(aSrc.mBorderImageRepeatH), + mBorderImageRepeatV(aSrc.mBorderImageRepeatV), + mFloatEdge(aSrc.mFloatEdge), mComputedBorder(aSrc.mComputedBorder), mBorder(aSrc.mBorder), - mBorderImage(aSrc.mBorderImage), mTwipsPerPixel(aSrc.mTwipsPerPixel) { MOZ_COUNT_CTOR(nsStyleBorder); @@ -452,9 +462,6 @@ nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc) mBorderStyle[side] = aSrc.mBorderStyle[side]; mBorderColor[side] = aSrc.mBorderColor[side]; } - NS_FOR_CSS_HALF_CORNERS(corner) { - mBorderRadius.Set(corner, aSrc.mBorderRadius.Get(corner)); - } } nsStyleBorder::~nsStyleBorder() @@ -469,23 +476,49 @@ nsStyleBorder::~nsStyleBorder() } } -void* +void* nsStyleBorder::operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { void* result = aContext->AllocateFromShell(sz); if (result) memset(result, 0, sz); return result; } - -void + +nsMargin +nsStyleBorder::GetImageOutset() const +{ + // We don't check whether there is a border-image (which is OK since + // the initial values yields 0 outset) so that we don't have to + // reflow to update overflow areas when an image loads. + nsMargin outset; + NS_FOR_CSS_SIDES(s) { + nsStyleCoord coord = mBorderImageOutset.Get(s); + nscoord value; + switch (coord.GetUnit()) { + case eStyleUnit_Coord: + value = coord.GetCoordValue(); + break; + case eStyleUnit_Factor: + value = coord.GetFactorValue() * mComputedBorder.Side(s); + break; + default: + NS_NOTREACHED("unexpected CSS unit for image outset"); + value = 0; + break; + } + outset.Side(s) = value; + } + return outset; +} + +void nsStyleBorder::Destroy(nsPresContext* aContext) { - if (mBorderImage) + if (mBorderImageSource) UntrackImage(aContext); this->~nsStyleBorder(); aContext->FreeToShell(sizeof(nsStyleBorder), this); } - nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const { nsChangeHint shadowDifference = @@ -496,8 +529,9 @@ nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const // XXXbz we should be able to return a more specific change hint for // at least GetActualBorder() differences... if (mTwipsPerPixel != aOther.mTwipsPerPixel || - GetActualBorder() != aOther.GetActualBorder() || + GetActualBorder() != aOther.GetActualBorder() || mFloatEdge != aOther.mFloatEdge || + mBorderImageOutset != aOther.mBorderImageOutset || (shadowDifference & nsChangeHint_ReflowFrame)) return NS_STYLE_HINT_REFLOW; @@ -517,13 +551,14 @@ nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const return NS_STYLE_HINT_VISUAL; if (IsBorderImageLoaded() || aOther.IsBorderImageLoaded()) { - if (mBorderImage != aOther.mBorderImage || - mBorderImageHFill != aOther.mBorderImageHFill || - mBorderImageVFill != aOther.mBorderImageVFill || - mBorderImageSplit != aOther.mBorderImageSplit) + if (mBorderImageSource != aOther.mBorderImageSource || + mBorderImageRepeatH != aOther.mBorderImageRepeatH || + mBorderImageRepeatV != aOther.mBorderImageRepeatV || + mBorderImageSlice != aOther.mBorderImageSlice || + mBorderImageFill != aOther.mBorderImageFill || + mBorderImageWidth != aOther.mBorderImageWidth || + mBorderImageOutset != aOther.mBorderImageOutset) return NS_STYLE_HINT_VISUAL; - // The call to GetActualBorder above already considered - // mBorderImageWidth and mHaveBorderImageWidth. } // Note that at this point if mBorderColors is non-null so is @@ -547,36 +582,17 @@ nsChangeHint nsStyleBorder::MaxDifference() } #endif -bool -nsStyleBorder::ImageBorderDiffers() const -{ - return mComputedBorder != - (mHaveBorderImageWidth ? mBorderImageWidth : mBorder); -} - -const nsMargin& -nsStyleBorder::GetActualBorder() const -{ - if (IsBorderImageLoaded()) - if (mHaveBorderImageWidth) - return mBorderImageWidth; - else - return mBorder; - else - return mComputedBorder; -} - void nsStyleBorder::TrackImage(nsPresContext* aContext) { // Sanity NS_ABORT_IF_FALSE(!mImageTracked, "Already tracking image!"); - NS_ABORT_IF_FALSE(mBorderImage, "Can't track null image!"); + NS_ABORT_IF_FALSE(mBorderImageSource, "Can't track null image!"); // Register the image with the document nsIDocument* doc = aContext->Document(); if (doc) - doc->AddImage(mBorderImage); + doc->AddImage(mBorderImageSource); // Mark state #ifdef DEBUG @@ -589,12 +605,12 @@ nsStyleBorder::UntrackImage(nsPresContext* aContext) { // Sanity NS_ABORT_IF_FALSE(mImageTracked, "Image not tracked!"); - NS_ABORT_IF_FALSE(mBorderImage, "Can't track null image!"); + NS_ABORT_IF_FALSE(mBorderImageSource, "Can't track null image!"); // Unregister the image with the document nsIDocument* doc = aContext->Document(); if (doc) - doc->RemoveImage(mBorderImage); + doc->RemoveImage(mBorderImageSource); // Mark state #ifdef DEBUG diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 4384ea26e1f3..c9f777061b8d 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -740,17 +740,6 @@ struct nsStyleBorder { static nsChangeHint MaxDifference(); #endif static bool ForceCompare() { return false; } - bool ImageBorderDiffers() const; - - nsStyleCorners mBorderRadius; // [reset] coord, percent, calc - nsStyleSides mBorderImageSplit; // [reset] integer, percent - PRUint8 mFloatEdge; // [reset] see nsStyleConsts.h - PRUint8 mBorderImageHFill; // [reset] - PRUint8 mBorderImageVFill; // [reset] - nsBorderColors** mBorderColors; // [reset] multiple levels of color for a border. - nsRefPtr mBoxShadow; // [reset] NULL for 'none' - bool mHaveBorderImageWidth; // [reset] - nsMargin mBorderImageWidth; // [reset] void EnsureBorderColors() { if (!mBorderColors) { @@ -788,19 +777,12 @@ struct nsStyleBorder { mComputedBorder.Side(aSide) = roundedWidth; } - void SetBorderImageWidthOverride(mozilla::css::Side aSide, nscoord aBorderWidth) + // Returns the computed border. + inline const nsMargin& GetActualBorder() const { - mBorderImageWidth.Side(aSide) = - NS_ROUND_BORDER_TO_PIXELS(aBorderWidth, mTwipsPerPixel); + return mComputedBorder; } - // Get the actual border, in twips. (If there is no border-image - // loaded, this is the same as GetComputedBorder. If there is a - // border-image loaded, it uses the border-image width overrides if - // present, and otherwise mBorder, which is GetComputedBorder without - // considering border-style: none.) - const nsMargin& GetActualBorder() const; - // Get the computed border (plus rounding). This does consider the // effects of 'border-style: none', but does not consider // 'border-image'. @@ -861,11 +843,13 @@ struct nsStyleBorder { inline void SetBorderImage(imgIRequest* aImage); inline imgIRequest* GetBorderImage() const; - bool HasBorderImage() {return !!mBorderImage;} + bool HasBorderImage() {return !!mBorderImageSource;} void TrackImage(nsPresContext* aContext); void UntrackImage(nsPresContext* aContext); + nsMargin GetImageOutset() const; + // These methods are used for the caller to caches the sub images created during // a border-image paint operation inline void SetSubImage(PRUint8 aIndex, imgIContainer* aSubImage) const; @@ -901,39 +885,55 @@ struct nsStyleBorder { mBorderStyle[aSide] |= BORDER_COLOR_FOREGROUND; } +public: + nsBorderColors** mBorderColors; // [reset] composite (stripe) colors + nsRefPtr mBoxShadow; // [reset] NULL for 'none' + #ifdef DEBUG bool mImageTracked; #endif protected: - // mComputedBorder holds the CSS2.1 computed border-width values. In - // particular, these widths take into account the border-style for the - // relevant side and the values are rounded to the nearest device - // pixel. They are also rounded (which is not part of the definition - // of computed values). However, they do *not* take into account the - // presence of border-image. See GetActualBorder above for how to - // really get the actual border. + nsCOMPtr mBorderImageSource; // [reset] + +public: + nsStyleCorners mBorderRadius; // [reset] coord, percent + nsStyleSides mBorderImageSlice; // [reset] factor, percent + PRUint8 mBorderImageFill; // [reset] + nsStyleSides mBorderImageWidth; // [reset] length, factor, percent, auto + nsStyleSides mBorderImageOutset; // [reset] length, factor + + PRUint8 mBorderImageRepeatH; // [reset] see nsStyleConsts.h + PRUint8 mBorderImageRepeatV; // [reset] + PRUint8 mFloatEdge; // [reset] + // 8 bits free here + +protected: + // mComputedBorder holds the CSS2.1 computed border-width values. + // In particular, these widths take into account the border-style + // for the relevant side, and the values are rounded to the nearest + // device pixel (which is not part of the definition of computed + // values). The presence or absence of a border-image does not + // affect border-width values. nsMargin mComputedBorder; - // mBorder holds the nscoord values for the border widths as they would be if - // all the border-style values were visible (not hidden or none). This - // member exists so that when we create structs using the copy - // constructor during style resolution the new structs will know what the - // specified values of the border were in case they have more specific rules - // setting the border style. Note that this isn't quite the CSS specified - // value, since this has had the enumerated border widths converted to - // lengths, and all lengths converted to twips. But it's not quite the - // computed value either. The values are rounded to the nearest device pixel - // We also use these values when we have a loaded border-image that - // does not have width overrides. + // mBorder holds the nscoord values for the border widths as they + // would be if all the border-style values were visible (not hidden + // or none). This member exists so that when we create structs + // using the copy constructor during style resolution the new + // structs will know what the specified values of the border were in + // case they have more specific rules setting the border style. + // + // Note that this isn't quite the CSS specified value, since this + // has had the enumerated border widths converted to lengths, and + // all lengths converted to twips. But it's not quite the computed + // value either. The values are rounded to the nearest device pixel. nsMargin mBorder; PRUint8 mBorderStyle[4]; // [reset] See nsStyleConsts.h - nscolor mBorderColor[4]; // [reset] the colors to use for a simple border. not used - // if -moz-border-colors is specified + nscolor mBorderColor[4]; // [reset] the colors to use for a simple + // border. not used for -moz-border-colors private: - nsCOMPtr mBorderImage; // [reset] - // Cache used by callers for border-image painting nsCOMArray mSubImages; diff --git a/layout/style/nsStyleStructInlines.h b/layout/style/nsStyleStructInlines.h index 15406e2ef84c..a4326c0b64ad 100644 --- a/layout/style/nsStyleStructInlines.h +++ b/layout/style/nsStyleStructInlines.h @@ -51,23 +51,23 @@ inline void nsStyleBorder::SetBorderImage(imgIRequest* aImage) { - mBorderImage = aImage; + mBorderImageSource = aImage; mSubImages.Clear(); } inline imgIRequest* nsStyleBorder::GetBorderImage() const { - NS_ABORT_IF_FALSE(!mBorderImage || mImageTracked, + NS_ABORT_IF_FALSE(!mBorderImageSource || mImageTracked, "Should be tracking any images we're going to use!"); - return mBorderImage; + return mBorderImageSource; } inline bool nsStyleBorder::IsBorderImageLoaded() const { PRUint32 status; - return mBorderImage && - NS_SUCCEEDED(mBorderImage->GetImageStatus(&status)) && + return mBorderImageSource && + NS_SUCCEEDED(mBorderImageSource->GetImageStatus(&status)) && (status & imgIRequest::STATUS_LOAD_COMPLETE) && !(status & imgIRequest::STATUS_ERROR); } From d4fa3e33b1954fa62f13123421c1aa5b71784d22 Mon Sep 17 00:00:00 2001 From: William Chen Date: Thu, 22 Dec 2011 18:34:53 -0500 Subject: [PATCH 22/26] Bug 497995: Part 2 - Update existing border-image tests. r=dbaron --HG-- extra : rebase_source : b9fb3987c8540457fa1f7b3e27420f208207a9c0 --- layout/base/tests/Makefile.in | 1 - layout/base/tests/test_bug445810.html | 138 ------------------ layout/reftests/border-image/470250-1.html | 4 +- layout/reftests/border-image/470250-2.html | 4 +- .../border-image-nofill-1-ref.html | 37 +++++ .../border-image/border-image-nofill-1.html | 20 +++ .../border-image-outset-1-ref.html | 15 ++ .../border-image/border-image-outset-1a.html | 21 +++ .../border-image/border-image-outset-1b.html | 22 +++ .../border-image/border-image-outset-1c.html | 21 +++ .../border-image-outset-move-1-ref.html | 15 ++ .../border-image-outset-move-1.html | 30 ++++ .../border-image-outset-resize-1-ref.html | 15 ++ .../border-image-outset-resize-1.html | 30 ++++ .../border-image-width-1-ref.html | 15 ++ .../border-image/border-image-width-1a.html | 20 +++ .../border-image/border-image-width-1b.html | 20 +++ .../border-image/border-image-width-1c.html | 21 +++ .../border-image/center-scaling-1.html | 3 +- .../border-image/center-scaling-2.html | 3 +- .../border-image/center-scaling-3.html | 3 +- .../border-image/center-scaling-4b.html | 3 +- .../border-image/center-scaling-4l.html | 3 +- .../border-image/center-scaling-4lr.html | 3 +- .../border-image/center-scaling-4r.html | 3 +- .../border-image/center-scaling-4t.html | 3 +- .../border-image/center-scaling-4tb.html | 3 +- .../border-image/different-h-v-1.html | 1 + .../border-image/different-h-v-2.html | 1 + .../border-image/multicolor-image-1.html | 8 +- .../border-image/multicolor-image-2.html | 60 ++++---- .../border-image/multicolor-image-3-ref.html | 32 ++-- .../border-image/multicolor-image-3.html | 25 ++-- .../border-image/multicolor-image-4.html | 60 ++++---- .../border-image/multicolor-image-5.html | 54 +++---- layout/reftests/border-image/reftest.list | 10 ++ .../reftests/border-image/repeat-image-1.html | 16 +- .../reftests/border-image/solid-image-1.html | 8 +- .../reftests/border-image/solid-image-1a.html | 19 +++ .../reftests/border-image/solid-image-2.html | 8 +- .../reftests/border-image/solid-image-2a.html | 8 +- layout/reftests/bugs/468473-1.xul | 2 +- .../pixel-rounding/border-image-width-0.html | 22 +-- .../pixel-rounding/border-image-width-10.html | 22 +-- .../pixel-rounding/border-image-width-4.html | 22 +-- .../pixel-rounding/border-image-width-9.html | 22 +-- .../svg/as-image/border-image-simple-1.html | 5 +- .../svg/as-image/border-image-simple-2.html | 5 +- layout/style/test/property_database.js | 66 ++++++++- .../test/test_shorthand_property_getters.html | 4 +- 50 files changed, 581 insertions(+), 375 deletions(-) delete mode 100644 layout/base/tests/test_bug445810.html create mode 100644 layout/reftests/border-image/border-image-nofill-1-ref.html create mode 100644 layout/reftests/border-image/border-image-nofill-1.html create mode 100644 layout/reftests/border-image/border-image-outset-1-ref.html create mode 100644 layout/reftests/border-image/border-image-outset-1a.html create mode 100644 layout/reftests/border-image/border-image-outset-1b.html create mode 100644 layout/reftests/border-image/border-image-outset-1c.html create mode 100644 layout/reftests/border-image/border-image-outset-move-1-ref.html create mode 100644 layout/reftests/border-image/border-image-outset-move-1.html create mode 100644 layout/reftests/border-image/border-image-outset-resize-1-ref.html create mode 100644 layout/reftests/border-image/border-image-outset-resize-1.html create mode 100644 layout/reftests/border-image/border-image-width-1-ref.html create mode 100644 layout/reftests/border-image/border-image-width-1a.html create mode 100644 layout/reftests/border-image/border-image-width-1b.html create mode 100644 layout/reftests/border-image/border-image-width-1c.html create mode 100644 layout/reftests/border-image/solid-image-1a.html diff --git a/layout/base/tests/Makefile.in b/layout/base/tests/Makefile.in index d4da2a0440e4..018a44b80525 100644 --- a/layout/base/tests/Makefile.in +++ b/layout/base/tests/Makefile.in @@ -89,7 +89,6 @@ _TEST_FILES = \ test_bug404209.xhtml \ test_bug416896.html \ test_bug423523.html \ - test_bug445810.html \ test_bug449781.html \ test_bug450930.xhtml \ test_bug458898.html \ diff --git a/layout/base/tests/test_bug445810.html b/layout/base/tests/test_bug445810.html deleted file mode 100644 index 090ea717a139..000000000000 --- a/layout/base/tests/test_bug445810.html +++ /dev/null @@ -1,138 +0,0 @@ - - - - - Test for Bug 445810 - - - - - -Mozilla Bug 445810 -

    -
    -
    -
    -
    - - - diff --git a/layout/reftests/border-image/470250-1.html b/layout/reftests/border-image/470250-1.html index 776971048556..c0635c21eb2d 100644 --- a/layout/reftests/border-image/470250-1.html +++ b/layout/reftests/border-image/470250-1.html @@ -5,7 +5,9 @@ div { width: 48px; height: 48px; - -moz-border-image: url(3x3green-1DD813.png) 0 / 0; + border-width: 0; + border-style: solid; + -moz-border-image: url(3x3green-1DD813.png) 0 fill; } diff --git a/layout/reftests/border-image/470250-2.html b/layout/reftests/border-image/470250-2.html index 9d4e79edf21c..ff1613d4ec49 100644 --- a/layout/reftests/border-image/470250-2.html +++ b/layout/reftests/border-image/470250-2.html @@ -6,7 +6,9 @@ table { width: 48px; height: 48px; border-collapse: separate; - -moz-border-image: url(3x3green-1DD813.png) 0 / 0; + border-width: 0; + border-style: solid; + -moz-border-image: url(3x3green-1DD813.png) 0 fill; } diff --git a/layout/reftests/border-image/border-image-nofill-1-ref.html b/layout/reftests/border-image/border-image-nofill-1-ref.html new file mode 100644 index 000000000000..40794cb37239 --- /dev/null +++ b/layout/reftests/border-image/border-image-nofill-1-ref.html @@ -0,0 +1,37 @@ + + + + -moz-border-image-slice without fill reference + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + diff --git a/layout/reftests/border-image/border-image-nofill-1.html b/layout/reftests/border-image/border-image-nofill-1.html new file mode 100644 index 000000000000..fdeab07d8d95 --- /dev/null +++ b/layout/reftests/border-image/border-image-nofill-1.html @@ -0,0 +1,20 @@ + + + + test of -moz-border-image-slice without fill + + + + + +
    + + diff --git a/layout/reftests/border-image/border-image-outset-1-ref.html b/layout/reftests/border-image/border-image-outset-1-ref.html new file mode 100644 index 000000000000..aa84c92689bd --- /dev/null +++ b/layout/reftests/border-image/border-image-outset-1-ref.html @@ -0,0 +1,15 @@ + + + + -moz-border-image-outset: 1em reference + + + + +
    +
    +border.png
    second longer longer longer longer longer longer line
    third longer longer longer longer longer longer line +
    +
    + + diff --git a/layout/reftests/border-image/border-image-outset-1a.html b/layout/reftests/border-image/border-image-outset-1a.html new file mode 100644 index 000000000000..d8fa94f93f87 --- /dev/null +++ b/layout/reftests/border-image/border-image-outset-1a.html @@ -0,0 +1,21 @@ + + + + -moz-border-outset-width: 1em + + + + + +
    +border.png
    second longer longer longer longer longer longer line
    third longer longer longer longer longer longer line +
    + + diff --git a/layout/reftests/border-image/border-image-outset-1b.html b/layout/reftests/border-image/border-image-outset-1b.html new file mode 100644 index 000000000000..0ff6980dc2fd --- /dev/null +++ b/layout/reftests/border-image/border-image-outset-1b.html @@ -0,0 +1,22 @@ + + + + -moz-border-outset-width: 1em + + + + + +
    +border.png
    second longer longer longer longer longer longer line
    third longer longer longer longer longer longer line +
    + + diff --git a/layout/reftests/border-image/border-image-outset-1c.html b/layout/reftests/border-image/border-image-outset-1c.html new file mode 100644 index 000000000000..12637c120506 --- /dev/null +++ b/layout/reftests/border-image/border-image-outset-1c.html @@ -0,0 +1,21 @@ + + + + -moz-border-outset-width: 1em + + + + + +
    +border.png
    second longer longer longer longer longer longer line
    third longer longer longer longer longer longer line +
    + + diff --git a/layout/reftests/border-image/border-image-outset-move-1-ref.html b/layout/reftests/border-image/border-image-outset-move-1-ref.html new file mode 100644 index 000000000000..396652ec180f --- /dev/null +++ b/layout/reftests/border-image/border-image-outset-move-1-ref.html @@ -0,0 +1,15 @@ + + + + -moz-border-image-outset move reference + + + + +
    +
    +Hello World! +
    +
    + + diff --git a/layout/reftests/border-image/border-image-outset-move-1.html b/layout/reftests/border-image/border-image-outset-move-1.html new file mode 100644 index 000000000000..963408278e16 --- /dev/null +++ b/layout/reftests/border-image/border-image-outset-move-1.html @@ -0,0 +1,30 @@ + + + + -moz-border-outset move + + + + + +
    +Hello World! +
    + + + diff --git a/layout/reftests/border-image/border-image-outset-resize-1-ref.html b/layout/reftests/border-image/border-image-outset-resize-1-ref.html new file mode 100644 index 000000000000..b83fddbf7671 --- /dev/null +++ b/layout/reftests/border-image/border-image-outset-resize-1-ref.html @@ -0,0 +1,15 @@ + + + + -moz-border-image-outset resize reference + + + + +
    +
    +Hello World! +
    +
    + + diff --git a/layout/reftests/border-image/border-image-outset-resize-1.html b/layout/reftests/border-image/border-image-outset-resize-1.html new file mode 100644 index 000000000000..cc92e8af20c1 --- /dev/null +++ b/layout/reftests/border-image/border-image-outset-resize-1.html @@ -0,0 +1,30 @@ + + + + -moz-border-outset resize + + + + + +
    +Hello World! +
    + + + diff --git a/layout/reftests/border-image/border-image-width-1-ref.html b/layout/reftests/border-image/border-image-width-1-ref.html new file mode 100644 index 000000000000..7d93d18c1eb1 --- /dev/null +++ b/layout/reftests/border-image/border-image-width-1-ref.html @@ -0,0 +1,15 @@ + + + + -moz-border-image-width: 1em reference + + + + +
    +
    +border.png
    second longer longer longer longer longer longer line
    third longer longer longer longer longer longer line +
    +
    + + diff --git a/layout/reftests/border-image/border-image-width-1a.html b/layout/reftests/border-image/border-image-width-1a.html new file mode 100644 index 000000000000..3f6ad64a3469 --- /dev/null +++ b/layout/reftests/border-image/border-image-width-1a.html @@ -0,0 +1,20 @@ + + + + -moz-border-image-width: 2em + + + + + +
    +border.png
    second longer longer longer longer longer longer line
    third longer longer longer longer longer longer line +
    + + diff --git a/layout/reftests/border-image/border-image-width-1b.html b/layout/reftests/border-image/border-image-width-1b.html new file mode 100644 index 000000000000..acc74379e035 --- /dev/null +++ b/layout/reftests/border-image/border-image-width-1b.html @@ -0,0 +1,20 @@ + + + + -moz-border-image-width: 2 + + + + + +
    +border.png
    second longer longer longer longer longer longer line
    third longer longer longer longer longer longer line +
    + + diff --git a/layout/reftests/border-image/border-image-width-1c.html b/layout/reftests/border-image/border-image-width-1c.html new file mode 100644 index 000000000000..9ef953a612c6 --- /dev/null +++ b/layout/reftests/border-image/border-image-width-1c.html @@ -0,0 +1,21 @@ + + + + -moz-border-image-width: 2 + + + + + +
    +border.png
    second longer longer longer longer longer longer line
    third longer longer longer longer longer longer line +
    + + diff --git a/layout/reftests/border-image/center-scaling-1.html b/layout/reftests/border-image/center-scaling-1.html index 3220b210fe6b..c0248f7c3d60 100644 --- a/layout/reftests/border-image/center-scaling-1.html +++ b/layout/reftests/border-image/center-scaling-1.html @@ -2,7 +2,8 @@ diff --git a/layout/reftests/border-image/multicolor-image-2.html b/layout/reftests/border-image/multicolor-image-2.html index 8212ae3daf30..6ef1fbd43d87 100644 --- a/layout/reftests/border-image/multicolor-image-2.html +++ b/layout/reftests/border-image/multicolor-image-2.html @@ -13,71 +13,71 @@ } div.one { - -moz-border-image: url(10x5multicolor.png) 2 2 1 3; - -khtml-border-image: url(10x5multicolor.png) 2 2 1 3; - border-image: url(10x5multicolor.png) 2 2 1 3; + -moz-border-image: url(10x5multicolor.png) 2 2 1 3 fill; + -khtml-border-image: url(10x5multicolor.png) 2 2 1 3 fill; + border-image: url(10x5multicolor.png) 2 2 1 3 fill; border-width: 4px 6px 8px 11px; + border-style: solid; width: 9px; height: 1px; } div.two { - -moz-border-image: url(10x5multicolor.png) 40% 20% 20% 30%; - -khtml-border-image: url(10x5multicolor.png) 40% 20% 20% 30%; - border-image: url(10x5multicolor.png) 40% 20% 20% 30%; + -moz-border-image: url(10x5multicolor.png) 40% 20% 20% 30% fill; + -khtml-border-image: url(10x5multicolor.png) 40% 20% 20% 30% fill; + border-image: url(10x5multicolor.png) 40% 20% 20% 30% fill; border-width: 3px 1px 0px 4px; + border-style: solid; width: 2px; height: 17px; } div.three { - -moz-border-image: url(10x5multicolor.png) 40% 2 1 30%; - -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30%; - border-image: url(10x5multicolor.png) 40% 2 1 30%; + -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% fill; + -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% fill; + border-image: url(10x5multicolor.png) 40% 2 1 30% fill; border-width: 10px 2px 5px 3px; + border-style: solid; width: 17px; height: 8px; } div.four { - -moz-border-image: url(10x5multicolor.png) 2 2 20% 30%; - -khtml-border-image: url(10x5multicolor.png) 2 2 20% 30%; - border-image: url(10x5multicolor.png) 2 2 20% 30%; + -moz-border-image: url(10x5multicolor.png) 2 2 20% 30% fill; + -khtml-border-image: url(10x5multicolor.png) 2 2 20% 30% fill; + border-image: url(10x5multicolor.png) 2 2 20% 30% fill; border-width: 5px 7px 1px 0; + border-style: solid; width: 8px; height: 5px; } div.five { - border-width: 5px 6px 7px 8px; /* ignored */ - border-width: 5px 6px 7px 8px ! important; /* ignored */ - -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 8px 10px 2px; - -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 8px 10px 2px; - border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 8px 10px 2px; - border-width: 5px 6px 7px 8px; /* ignored */ - border-width: 5px 6px 7px 8px ! important; /* ignored */ + border-width: 4px 8px 10px 2px; + -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% fill; + -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% fill; + border-image: url(10x5multicolor.png) 40% 2 1 30% fill; + border-style: solid; width: 0; height: 8px; } - div.five { - border-width: 5px 6px 7px 8px; /* ignored */ - border-width: 5px 6px 7px 8px ! important; /* ignored */ - } div.six { - border-width: 5px 6px 7px 8px; /* ignored */ - -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 0 10px 2px; - -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 0 10px 2px; - border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 0 10px 2px; + border-width: 4px 0 10px 2px; + -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% fill; + -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% fill; + border-image: url(10x5multicolor.png) 40% 2 1 30% fill; + border-style: solid; width: 17px; height: 0; } div.seven { - -moz-border-image: url(10x5multicolor.png) 40% 2 1 30%; - -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30%; - border-image: url(10x5multicolor.png) 40% 2 1 30%; + -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% fill; + -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% fill; + border-image: url(10x5multicolor.png) 40% 2 1 30% fill; border-width: 1px 3px 0 0; + border-style: solid; width: 17px; height: 0; } diff --git a/layout/reftests/border-image/multicolor-image-3-ref.html b/layout/reftests/border-image/multicolor-image-3-ref.html index a20c3bc83a1a..e35bd6937475 100644 --- a/layout/reftests/border-image/multicolor-image-3-ref.html +++ b/layout/reftests/border-image/multicolor-image-3-ref.html @@ -13,33 +13,41 @@ } div.one { - -moz-border-image: url(10x5multicolor.png) 2 2 2 2 / 4px 6px 8px 6px; - -khtml-border-image: url(10x5multicolor.png) 2 2 2 2 / 4px 6px 8px 6px; - border-image: url(10x5multicolor.png) 2 2 2 2 / 4px 6px 8px 6px; + border-width: 4px 6px 8px 6px; + border-style: solid; + -moz-border-image: url(10x5multicolor.png) 2 2 2 2; + -khtml-border-image: url(10x5multicolor.png) 2 2 2 2; + border-image: url(10x5multicolor.png) 2 2 2 2; width: 5px; height: 2px; } div.two { - -moz-border-image: url(10x5multicolor.png) 2 1 2 1/ 4px 4px 4px 4px; - -khtml-border-image: url(10x5multicolor.png) 2 1 2 1/ 4px 4px 4px 4px; - border-image: url(10x5multicolor.png) 2 1 2 1/ 4px 4px 4px 4px; + border-width: 4px 4px 4px 4px; + border-style: solid; + -moz-border-image: url(10x5multicolor.png) 2 1 2 1; + -khtml-border-image: url(10x5multicolor.png) 2 1 2 1; + border-image: url(10x5multicolor.png) 2 1 2 1; width: 5px; height: 2px; } div.three { - -moz-border-image: url(10x5multicolor.png) 2 3 1 3 / 4px 2px 4px 2px; - -khtml-border-image: url(10x5multicolor.png) 2 3 1 3 / 4px 2px 4px 2px; - border-image: url(10x5multicolor.png) 2 3 1 3 / 4px 2px 4px 2px; + border-width: 4px 2px 4px 2px; + border-style: solid; + -moz-border-image: url(10x5multicolor.png) 2 3 1 3; + -khtml-border-image: url(10x5multicolor.png) 2 3 1 3; + border-image: url(10x5multicolor.png) 2 3 1 3; width: 5px; height: 2px; } div.four { - -moz-border-image: url(10x5multicolor.png) 2 3 1 1 / 4px 3px 4px 3px; - -khtml-border-image: url(10x5multicolor.png) 2 3 1 1 / 4px 3px 4px 3px; - border-image: url(10x5multicolor.png) 2 3 1 1 / 4px 3px 4px 3px; + border-width: 4px 3px 4px 3px; + border-style: solid; + -moz-border-image: url(10x5multicolor.png) 2 3 1 1; + -khtml-border-image: url(10x5multicolor.png) 2 3 1 1; + border-image: url(10x5multicolor.png) 2 3 1 1; width: 5px; height: 2px; } diff --git a/layout/reftests/border-image/multicolor-image-3.html b/layout/reftests/border-image/multicolor-image-3.html index 43b52b69727e..de934bebe58f 100644 --- a/layout/reftests/border-image/multicolor-image-3.html +++ b/layout/reftests/border-image/multicolor-image-3.html @@ -13,17 +13,21 @@ } div.one { - -moz-border-image: url(10x5multicolor.png) 2 / 4px 6px 8px; - -khtml-border-image: url(10x5multicolor.png) 2 / 4px 6px 8px; - border-image: url(10x5multicolor.png) 2 / 4px 6px 8px; + border-width: 4px 6px 8px; + border-style: solid; + -moz-border-image: url(10x5multicolor.png) 2; + -khtml-border-image: url(10x5multicolor.png) 2; + border-image: url(10x5multicolor.png) 2; width: 5px; height: 2px; } div.two { - -moz-border-image: url(10x5multicolor.png) 2 1 / 4px; - -khtml-border-image: url(10x5multicolor.png) 2 1 / 4px; - border-image: url(10x5multicolor.png) 2 1 / 4px; + border-width: 4px; + border-style: solid; + -moz-border-image: url(10x5multicolor.png) 2 1; + -khtml-border-image: url(10x5multicolor.png) 2 1; + border-image: url(10x5multicolor.png) 2 1; width: 5px; height: 2px; } @@ -33,14 +37,17 @@ -khtml-border-image: url(10x5multicolor.png) 2 3 1; border-image: url(10x5multicolor.png) 2 3 1; border-width: 4px 2px; + border-style: solid; width: 5px; height: 2px; } div.four { - -moz-border-image: url(10x5multicolor.png) 2 3 1 1 / 4px 3px; - -khtml-border-image: url(10x5multicolor.png) 2 3 1 1 / 4px 3px; - border-image: url(10x5multicolor.png) 2 3 1 1 / 4px 3px; + border-width: 4px 3px; + border-style: solid; + -moz-border-image: url(10x5multicolor.png) 2 3 1 1; + -khtml-border-image: url(10x5multicolor.png) 2 3 1 1; + border-image: url(10x5multicolor.png) 2 3 1 1; width: 5px; height: 2px; } diff --git a/layout/reftests/border-image/multicolor-image-4.html b/layout/reftests/border-image/multicolor-image-4.html index fb597c72b73e..405debb7240c 100644 --- a/layout/reftests/border-image/multicolor-image-4.html +++ b/layout/reftests/border-image/multicolor-image-4.html @@ -30,71 +30,71 @@ } div.one { - -moz-border-image: url(10x5multicolor.png) 2 2 1 3 stretch; - -khtml-border-image: url(10x5multicolor.png) 2 2 1 3 stretch; - border-image: url(10x5multicolor.png) 2 2 1 3 stretch; + -moz-border-image: url(10x5multicolor.png) 2 2 1 3 fill stretch; + -khtml-border-image: url(10x5multicolor.png) 2 2 1 3 fill stretch; + border-image: url(10x5multicolor.png) 2 2 1 3 fill stretch; border-width: 4px 6px 8px 11px; + border-style: solid; width: 9px; height: 1px; } div.two { - -moz-border-image: url(10x5multicolor.png) 40% 20% 20% 30% repeat; - -khtml-border-image: url(10x5multicolor.png) 40% 20% 20% 30% repeat; - border-image: url(10x5multicolor.png) 40% 20% 20% 30% repeat; + -moz-border-image: url(10x5multicolor.png) 40% 20% 20% 30% fill repeat; + -khtml-border-image: url(10x5multicolor.png) 40% 20% 20% 30% fill repeat; + border-image: url(10x5multicolor.png) 40% 20% 20% 30% fill repeat; border-width: 3px 1px 0px 4px; + border-style: solid; width: 2px; height: 17px; } div.three { - -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% round; - -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% round; - border-image: url(10x5multicolor.png) 40% 2 1 30% round; + -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% fill round; + -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% fill round; + border-image: url(10x5multicolor.png) 40% 2 1 30% fill round; border-width: 10px 2px 5px 3px; + border-style: solid; width: 17px; height: 8px; } div.four { - -moz-border-image: url(10x5multicolor.png) 2 2 20% 30% stretch round; - -khtml-border-image: url(10x5multicolor.png) 2 2 20% 30% stretch round; - border-image: url(10x5multicolor.png) 2 2 20% 30% stretch round; + -moz-border-image: url(10x5multicolor.png) 2 2 20% 30% fill stretch round; + -khtml-border-image: url(10x5multicolor.png) 2 2 20% 30% fill stretch round; + border-image: url(10x5multicolor.png) 2 2 20% 30% fill stretch round; border-width: 5px 7px 1px 0; + border-style: solid; width: 8px; height: 5px; } div.five { - border-width: 5px 6px 7px 8px; /* ignored */ - border-width: 5px 6px 7px 8px ! important; /* ignored */ - -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 8px 10px 2px repeat stretch; - -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 8px 10px 2px repeat stretch; - border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 8px 10px 2px repeat stretch; - border-width: 5px 6px 7px 8px; /* ignored */ - border-width: 5px 6px 7px 8px ! important; /* ignored */ + border-width: 4px 8px 10px 2px; + border-style: solid; + -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% fill repeat stretch; + -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% fill repeat stretch; + border-image: url(10x5multicolor.png) 40% 2 1 30% fill repeat stretch; width: 0; height: 8px; } - div.five { - border-width: 5px 6px 7px 8px; /* ignored */ - border-width: 5px 6px 7px 8px ! important; /* ignored */ - } div.six { - border-width: 5px 6px 7px 8px; /* ignored */ - -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 0 10px 2px round repeat; - -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 0 10px 2px round repeat; - border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 0 10px 2px round repeat; + border-width: 4px 0 10px 2px; + border-style: solid; + -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% fill round repeat; + -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% fill round repeat; + border-image: url(10x5multicolor.png) 40% 2 1 30% fill round repeat; width: 17px; height: 0; } div.seven { - -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% stretch repeat; - -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% stretch repeat; - border-image: url(10x5multicolor.png) 40% 2 1 30% stretch repeat; + -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% fill stretch repeat; + -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% fill stretch repeat; + border-image: url(10x5multicolor.png) 40% 2 1 30% fill stretch repeat; border-width: 1px 3px 0 0; + border-style: solid; width: 17px; height: 0; } diff --git a/layout/reftests/border-image/multicolor-image-5.html b/layout/reftests/border-image/multicolor-image-5.html index b6608c682d53..3a034dc30440 100644 --- a/layout/reftests/border-image/multicolor-image-5.html +++ b/layout/reftests/border-image/multicolor-image-5.html @@ -31,62 +31,61 @@ } div.one { - -moz-border-image: url(10x5multicolor.png) 2 2 1 3 stretch; - -khtml-border-image: url(10x5multicolor.png) 2 2 1 3 stretch; - border-image: url(10x5multicolor.png) 2 2 1 3 stretch; + -moz-border-image: url(10x5multicolor.png) 2 2 1 3 fill stretch; + -khtml-border-image: url(10x5multicolor.png) 2 2 1 3 fill stretch; + border-image: url(10x5multicolor.png) 2 2 1 3 fill stretch; border-width: 4px 6px 8px 11px; + border-style: solid; width: 9px; height: 1px; } div.two { - -moz-border-image: url(10x5multicolor.png) 40% 20% 20% 30% repeat; - -khtml-border-image: url(10x5multicolor.png) 40% 20% 20% 30% repeat; - border-image: url(10x5multicolor.png) 40% 20% 20% 30% repeat; + -moz-border-image: url(10x5multicolor.png) 40% 20% 20% 30% fill repeat; + -khtml-border-image: url(10x5multicolor.png) 40% 20% 20% 30% fill repeat; + border-image: url(10x5multicolor.png) 40% 20% 20% 30% fill repeat; border-width: 3px 1px 0px 4px; + border-style: solid; width: 2px; height: 17px; } div.three { - -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% round; - -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% round; - border-image: url(10x5multicolor.png) 40% 2 1 30% round; + -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% fill round; + -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% fill round; + border-image: url(10x5multicolor.png) 40% 2 1 30% fill round; border-width: 10px 2px 5px 3px; + border-style: solid; width: 17px; height: 8px; } div.four { - -moz-border-image: url(10x5multicolor.png) 2 2 20% 30% stretch round; - -khtml-border-image: url(10x5multicolor.png) 2 2 20% 30% stretch round; - border-image: url(10x5multicolor.png) 2 2 20% 30% stretch round; + -moz-border-image: url(10x5multicolor.png) 2 2 20% 30% fill stretch round; + -khtml-border-image: url(10x5multicolor.png) 2 2 20% 30% fill stretch round; + border-image: url(10x5multicolor.png) 2 2 20% 30% fill stretch round; border-width: 5px 7px 1px 0; + border-style: solid; width: 8px; height: 5px; } div.five { - border-width: 5px 6px 7px 8px; /* ignored */ - border-width: 5px 6px 7px 8px ! important; /* ignored */ - -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 8px 10px 2px repeat stretch; - -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 8px 10px 2px repeat stretch; - border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 8px 10px 2px repeat stretch; - border-width: 5px 6px 7px 8px; /* ignored */ - border-width: 5px 6px 7px 8px ! important; /* ignored */ + border-width: 4px 8px 10px 2px; + border-style: solid; + -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% fill repeat stretch; + -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% fill repeat stretch; + border-image: url(10x5multicolor.png) 40% 2 1 30% fill repeat stretch; width: 0; height: 8px; } - div.five { - border-width: 5px 6px 7px 8px; /* ignored */ - border-width: 5px 6px 7px 8px ! important; /* ignored */ - } div.six { - border-width: 5px 6px 7px 8px; /* ignored */ - -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 0 10px 2px round repeat; - -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 0 10px 2px round repeat; - border-image: url(10x5multicolor.png) 40% 2 1 30% / 4px 0 10px 2px round repeat; + border-width: 4px 0 10px 2px; + border-style: solid; + -moz-border-image: url(10x5multicolor.png) 40% 2 1 30% round repeat; + -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% round repeat; + border-image: url(10x5multicolor.png) 40% 2 1 30% round repeat; width: 17px; height: 0; } @@ -96,6 +95,7 @@ -khtml-border-image: url(10x5multicolor.png) 40% 2 1 30% stretch repeat; border-image: url(10x5multicolor.png) 40% 2 1 30% stretch repeat; border-width: 1px 3px 0 0; + border-style: solid; width: 17px; height: 0; } diff --git a/layout/reftests/border-image/reftest.list b/layout/reftests/border-image/reftest.list index 61287e9d7010..f26a0bb02078 100644 --- a/layout/reftests/border-image/reftest.list +++ b/layout/reftests/border-image/reftest.list @@ -1,4 +1,5 @@ == solid-image-1.html solid-image-1-ref.html +== solid-image-1a.html solid-image-1-ref.html == solid-image-2.html solid-image-2-ref.html == solid-image-2a.html solid-image-2-ref.html == multicolor-image-1.html multicolor-image-1-ref.html @@ -24,3 +25,12 @@ == center-scaling-4lr.html center-scaling-4lr-ref.html == side-scaling-1h.html side-scaling-1h-ref.html == side-scaling-1v.html side-scaling-1v-ref.html +== border-image-width-1a.html border-image-width-1-ref.html +== border-image-width-1b.html border-image-width-1-ref.html +== border-image-width-1c.html border-image-width-1-ref.html +== border-image-outset-1a.html border-image-outset-1-ref.html +== border-image-outset-1b.html border-image-outset-1-ref.html +== border-image-outset-1c.html border-image-outset-1-ref.html +== border-image-nofill-1.html border-image-nofill-1-ref.html +== border-image-outset-resize-1.html border-image-outset-resize-1-ref.html +== border-image-outset-move-1.html border-image-outset-move-1-ref.html diff --git a/layout/reftests/border-image/repeat-image-1.html b/layout/reftests/border-image/repeat-image-1.html index 805e4742084d..907fb8c55fd6 100644 --- a/layout/reftests/border-image/repeat-image-1.html +++ b/layout/reftests/border-image/repeat-image-1.html @@ -9,17 +9,21 @@ div.p1 { background: red; /* fail if this shows through */ background-image: url('3x3multicolor.png'); /* fail if this shows through */ - -moz-border-image: url('4x4multicolor.png') 1 1 1 1 / 1px 3px repeat; - -khtml-border-image: url('4x4multicolor.png') 1 1 1 1 / 1px 3px repeat; - border-image: url('4x4multicolor.png') 1 1 1 1 / 1px 3px repeat; + border-width: 1px 3px; + border-style: solid; + -moz-border-image: url('4x4multicolor.png') 1 1 1 1 repeat; + -khtml-border-image: url('4x4multicolor.png') 1 1 1 1 repeat; + border-image: url('4x4multicolor.png') 1 1 1 1 repeat; } div.p2 { background: red; /* fail if this shows through */ background-image: url('3x3multicolor.png'); /* fail if this shows through */ - -moz-border-image: url('4x4multicolor.png') 1 1 1 1 / 1px 3px; - -khtml-border-image: url('4x4multicolor.png') 1 1 1 1 / 1px 3px; - border-image: url('4x4multicolor.png') 1 1 1 1 / 1px 3px; + border-width: 1px 3px; + border-style: solid; + -moz-border-image: url('4x4multicolor.png') 1 1 1 1; + -khtml-border-image: url('4x4multicolor.png') 1 1 1 1; + border-image: url('4x4multicolor.png') 1 1 1 1; } diff --git a/layout/reftests/border-image/solid-image-1.html b/layout/reftests/border-image/solid-image-1.html index 0e6b7d58af43..7a64a6659f87 100644 --- a/layout/reftests/border-image/solid-image-1.html +++ b/layout/reftests/border-image/solid-image-1.html @@ -6,9 +6,11 @@ diff --git a/layout/reftests/border-image/solid-image-1a.html b/layout/reftests/border-image/solid-image-1a.html new file mode 100644 index 000000000000..f043863f9fcb --- /dev/null +++ b/layout/reftests/border-image/solid-image-1a.html @@ -0,0 +1,19 @@ + + + + test of -moz-border-image + + + + + +
    border.png
    second longer longer longer longer longer longer line
    third longer longer longer longer longer longer line
    + + diff --git a/layout/reftests/border-image/solid-image-2.html b/layout/reftests/border-image/solid-image-2.html index 6496f4c40814..4a92b448b20a 100644 --- a/layout/reftests/border-image/solid-image-2.html +++ b/layout/reftests/border-image/solid-image-2.html @@ -7,9 +7,11 @@ -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    diff --git a/layout/reftests/pixel-rounding/border-image-width-10.html b/layout/reftests/pixel-rounding/border-image-width-10.html index a17a067b1f9e..74337f1b7c1a 100644 --- a/layout/reftests/pixel-rounding/border-image-width-10.html +++ b/layout/reftests/pixel-rounding/border-image-width-10.html @@ -5,50 +5,30 @@ -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    diff --git a/layout/reftests/pixel-rounding/border-image-width-4.html b/layout/reftests/pixel-rounding/border-image-width-4.html index a15076b80f32..28a5496dd736 100644 --- a/layout/reftests/pixel-rounding/border-image-width-4.html +++ b/layout/reftests/pixel-rounding/border-image-width-4.html @@ -5,50 +5,30 @@ -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    diff --git a/layout/reftests/pixel-rounding/border-image-width-9.html b/layout/reftests/pixel-rounding/border-image-width-9.html index 497924fb736a..1ea13e71ff69 100644 --- a/layout/reftests/pixel-rounding/border-image-width-9.html +++ b/layout/reftests/pixel-rounding/border-image-width-9.html @@ -5,50 +5,30 @@ -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    diff --git a/layout/reftests/svg/as-image/border-image-simple-1.html b/layout/reftests/svg/as-image/border-image-simple-1.html index 3665c1c30e43..f81dd8215a17 100644 --- a/layout/reftests/svg/as-image/border-image-simple-1.html +++ b/layout/reftests/svg/as-image/border-image-simple-1.html @@ -1,7 +1,6 @@ -
    +
    diff --git a/layout/reftests/svg/as-image/border-image-simple-2.html b/layout/reftests/svg/as-image/border-image-simple-2.html index 1783b0649ad2..1bd232763e5d 100644 --- a/layout/reftests/svg/as-image/border-image-simple-2.html +++ b/layout/reftests/svg/as-image/border-image-simple-2.html @@ -1,7 +1,6 @@ -
    +
    diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 7d7f231f5667..98962e486a70 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -233,19 +233,73 @@ var gCSSProperties = { "-moz-border-image": { domProp: "MozBorderImage", inherited: false, - type: CSS_TYPE_LONGHAND, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "-moz-border-image-source", "-moz-border-image-slice", "-moz-border-image-width", "-moz-border-image-outset", "-moz-border-image-repeat" ], initial_values: [ "none" ], other_values: [ "url('border.png') 27 27 27 27", "url('border.png') 27", + "stretch url('border.png')", + "url('border.png') 27 fill", "url('border.png') 27 27 27 27 repeat", + "repeat url('border.png') 27 27 27 27", + "url('border.png') repeat 27 27 27 27", + "url('border.png') fill 27 27 27 27 repeat", "url('border.png') 27 27 27 27 / 1em", + "27 27 27 27 / 1em url('border.png') ", + "url('border.png') 27 27 27 27 / 10 10 10 / 10 10 repeat", + "repeat 27 27 27 27 / 10 10 10 / 10 10 url('border.png')", + "url('border.png') 27 27 27 27 / / 10 10 1em", + "fill 27 27 27 27 / / 10 10 1em url('border.png')", + "url('border.png') 27 27 27 27 /", "url('border.png') 27 27 27 27 / 1em 1em 1em 1em repeat", "url('border.png') 27 27 27 27 / 1em 1em 1em 1em stretch round" ], - invalid_values: [ "url('border.png')", - "url('border.png') 27 27 27 27 27", + invalid_values: [ "url('border.png') 27 27 27 27 27", "url('border.png') 27 27 27 27 / 1em 1em 1em 1em 1em", - "url('border.png') / repeat", - "url('border.png') 27 27 27 27 /" ] + "url('border.png') fill", + "url('border.png') fill repeat", + "fill repeat", + "url('border.png') fill / 1em", + "url('border.png') / repeat" ] + }, + "-moz-border-image-source": { + domProp: "MozBorderImageSource", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "url('border.png')" ], + invalid_values: [ "url('border.png') url('border.png')" ] + }, + "-moz-border-image-slice": { + domProp: "MozBorderImageSlice", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "100%", "100% 100% 100% 100%" ], + other_values: [ "0%", "10", "10 100% 0 2", "0 0 0 0", "fill 10 10", "10 10 fill" ], + invalid_values: [ "-10%", "-10", "10 10 10 10 10", "10 10 10 10 -10", "10px", "-10px", "fill", "fill fill 10px", "10px fill fill" ] + }, + "-moz-border-image-width": { + domProp: "MozBorderImageWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "1", "1 1 1 1" ], + other_values: [ "0", "0%", "0px", "auto auto auto auto", "10 10% auto 15px", "10px 10px 10px 10px", "10", "10 10", "10 10 10" ], + invalid_values: [ "-10", "-10px", "-10%", "10 10 10 10 10", "10 10 10 10 auto", "auto auto auto auto auto" ] + }, + "-moz-border-image-outset": { + domProp: "MozBorderImageOutset", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0", "0 0 0 0" ], + other_values: [ "10px", "10", "10 10", "10 10 10", "10 10 10 10", "10px 10 10 10px" ], + invalid_values: [ "-10", "-10px", "-10%", "10%", "10 10 10 10 10" ] + }, + "-moz-border-image-repeat": { + domProp: "MozBorderImageRepeat", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "stretch", "stretch stretch" ], + other_values: [ "round", "repeat", "stretch round", "repeat round", "stretch repeat", "round round", "repeat repeat" ], + invalid_values: [ "none", "stretch stretch stretch", "0", "10", "0%", "0px" ] }, "-moz-border-left-colors": { domProp: "MozBorderLeftColors", @@ -1552,7 +1606,7 @@ var gCSSProperties = { domProp: "border", inherited: false, type: CSS_TYPE_TRUE_SHORTHAND, - subproperties: [ "border-bottom-color", "border-bottom-style", "border-bottom-width", "border-left-color", "border-left-style", "border-left-width", "border-right-color", "border-right-style", "border-right-width", "border-top-color", "border-top-style", "border-top-width", "-moz-border-top-colors", "-moz-border-right-colors", "-moz-border-bottom-colors", "-moz-border-left-colors", "-moz-border-image" ], + subproperties: [ "border-bottom-color", "border-bottom-style", "border-bottom-width", "border-left-color", "border-left-style", "border-left-width", "border-right-color", "border-right-style", "border-right-width", "border-top-color", "border-top-style", "border-top-width", "-moz-border-top-colors", "-moz-border-right-colors", "-moz-border-bottom-colors", "-moz-border-left-colors", "-moz-border-image-source", "-moz-border-image-slice", "-moz-border-image-width", "-moz-border-image-outset", "-moz-border-image-repeat" ], initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor", "-moz-calc(4px - 1px) none" ], other_values: [ "solid", "medium solid", "green solid", "10px solid", "thick solid", "-moz-calc(2px) solid blue" ], invalid_values: [ "5%" ] diff --git a/layout/style/test/test_shorthand_property_getters.html b/layout/style/test/test_shorthand_property_getters.html index ca287127e3df..fab8c2cf1a50 100644 --- a/layout/style/test/test_shorthand_property_getters.html +++ b/layout/style/test/test_shorthand_property_getters.html @@ -169,9 +169,9 @@ is(e.style.MozTransition, "", "should not have -moz-transition shorthand (lists // Check that the 'border' shorthand resets 'border-image' and // '-moz-border-*-colors', but that other 'border-*' shorthands don't // (bug 482692). -e.setAttribute("style", '-moz-border-image: url("foo.png") 5 5 5 5; border-left: medium solid green'); +e.setAttribute("style", '-moz-border-image: url("foo.png") 5 5 5 5 / 5 5 5 5 / 5 5 5 5 repeat repeat; border-left: medium solid green'); is(e.style.cssText, - '-moz-border-image: url("foo.png") 5 5 5 5; border-left: medium solid green;', + '-moz-border-image: url("foo.png") 5 5 5 5 / 5 5 5 5 / 5 5 5 5 repeat repeat; border-left: medium solid green;', "border-left does NOT reset -moz-border-image"); e.setAttribute("style", '-moz-border-image: url("foo.png") 5 5 5 5; border: medium solid green'); is(e.style.cssText, From 6040dbfcfa8cf0a1c8e8a3275fc70389d3222910 Mon Sep 17 00:00:00 2001 From: William Chen Date: Thu, 22 Dec 2011 18:35:02 -0500 Subject: [PATCH 23/26] Bug 497995: Part 3 - Cleanup nsCoord initial value handling. r=dbaron --HG-- extra : rebase_source : 9ed22aa9e24016566fe0e843e6019c4a583ca788 --- layout/style/nsRuleNode.cpp | 63 ++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index a8d4fbaefd2a..b5b14eafb8c8 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -615,7 +615,7 @@ static bool SetCoord(const nsCSSValue& aValue, nsStyleCoord& aCoord, nsPresContext* aPresContext, bool& aCanStoreInRuleTree) { - bool result = true; + bool result = true; if (aValue.GetUnit() == eCSSUnit_Null) { result = false; } @@ -669,43 +669,40 @@ static bool SetCoord(const nsCSSValue& aValue, nsStyleCoord& aCoord, (aValue.GetUnit() == eCSSUnit_Number)) { aCoord.SetFactorValue(aValue.GetFloatValue()); } - else if (((aMask & SETCOORD_INITIAL_AUTO) != 0) && - (aValue.GetUnit() == eCSSUnit_Initial)) { - aCoord.SetAutoValue(); - } - else if (((aMask & SETCOORD_INITIAL_ZERO) != 0) && - (aValue.GetUnit() == eCSSUnit_Initial)) { - aCoord.SetCoordValue(0); - } - else if (((aMask & SETCOORD_INITIAL_FACTOR_ZERO) != 0) && - (aValue.GetUnit() == eCSSUnit_Initial)) { - aCoord.SetFactorValue(0.0f); - } - else if (((aMask & SETCOORD_INITIAL_NONE) != 0) && - (aValue.GetUnit() == eCSSUnit_Initial)) { - aCoord.SetNoneValue(); - } - else if (((aMask & SETCOORD_INITIAL_NORMAL) != 0) && - (aValue.GetUnit() == eCSSUnit_Initial)) { - aCoord.SetNormalValue(); - } - else if (((aMask & SETCOORD_INITIAL_HALF) != 0) && - (aValue.GetUnit() == eCSSUnit_Initial)) { - aCoord.SetPercentValue(0.5f); - } - else if (((aMask & SETCOORD_INITIAL_HUNDRED_PCT) != 0) && - (aValue.GetUnit() == eCSSUnit_Initial)) { - aCoord.SetPercentValue(1.0f); - } - else if (((aMask & SETCOORD_INITIAL_FACTOR_ONE) != 0) && - (aValue.GetUnit() == eCSSUnit_Initial)) { - aCoord.SetFactorValue(1.0f); - } else if (((aMask & SETCOORD_STORE_CALC) != 0) && (aValue.IsCalcUnit())) { SpecifiedCalcToComputedCalc(aValue, aCoord, aStyleContext, aCanStoreInRuleTree); } + else if (aValue.GetUnit() == eCSSUnit_Initial) { + if ((aMask & SETCOORD_INITIAL_AUTO) != 0) { + aCoord.SetAutoValue(); + } + else if ((aMask & SETCOORD_INITIAL_ZERO) != 0) { + aCoord.SetCoordValue(0); + } + else if ((aMask & SETCOORD_INITIAL_FACTOR_ZERO) != 0) { + aCoord.SetFactorValue(0.0f); + } + else if ((aMask & SETCOORD_INITIAL_NONE) != 0) { + aCoord.SetNoneValue(); + } + else if ((aMask & SETCOORD_INITIAL_NORMAL) != 0) { + aCoord.SetNormalValue(); + } + else if ((aMask & SETCOORD_INITIAL_HALF) != 0) { + aCoord.SetPercentValue(0.5f); + } + else if ((aMask & SETCOORD_INITIAL_HUNDRED_PCT) != 0) { + aCoord.SetPercentValue(1.0f); + } + else if ((aMask & SETCOORD_INITIAL_FACTOR_ONE) != 0) { + aCoord.SetFactorValue(1.0f); + } + else { + result = false; // didn't set anything + } + } else { result = false; // didn't set anything } From 9e6b82159e4517e97dd10f2dc1147ba3e3db19a0 Mon Sep 17 00:00:00 2001 From: William Chen Date: Thu, 22 Dec 2011 18:35:10 -0500 Subject: [PATCH 24/26] Bug 497995: Part 4 - Tests for border-image using computed border width. r=dbaron --HG-- extra : rebase_source : 9ce07705ee02b6a92fc31d2a00f87779ac3c0756 --- .../border-image-style-none-auto-ref.html | 38 +++++++++++++++++++ .../border-image-style-none-auto.html | 21 ++++++++++ .../border-image-style-none-length-ref.html | 37 ++++++++++++++++++ .../border-image-style-none-length.html | 21 ++++++++++ .../border-image-style-none-ref.html | 32 ++++++++++++++++ .../border-image/border-image-style-none.html | 21 ++++++++++ layout/reftests/border-image/reftest.list | 3 ++ 7 files changed, 173 insertions(+) create mode 100644 layout/reftests/border-image/border-image-style-none-auto-ref.html create mode 100644 layout/reftests/border-image/border-image-style-none-auto.html create mode 100644 layout/reftests/border-image/border-image-style-none-length-ref.html create mode 100644 layout/reftests/border-image/border-image-style-none-length.html create mode 100644 layout/reftests/border-image/border-image-style-none-ref.html create mode 100644 layout/reftests/border-image/border-image-style-none.html diff --git a/layout/reftests/border-image/border-image-style-none-auto-ref.html b/layout/reftests/border-image/border-image-style-none-auto-ref.html new file mode 100644 index 000000000000..32e6488e6013 --- /dev/null +++ b/layout/reftests/border-image/border-image-style-none-auto-ref.html @@ -0,0 +1,38 @@ + + + + -moz-border-image-width: auto with border-bottom: none reference + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + diff --git a/layout/reftests/border-image/border-image-style-none-auto.html b/layout/reftests/border-image/border-image-style-none-auto.html new file mode 100644 index 000000000000..556195365da8 --- /dev/null +++ b/layout/reftests/border-image/border-image-style-none-auto.html @@ -0,0 +1,21 @@ + + + + test of -moz-border-image-width: auto with border-bottom: none + + + + + +
    + + diff --git a/layout/reftests/border-image/border-image-style-none-length-ref.html b/layout/reftests/border-image/border-image-style-none-length-ref.html new file mode 100644 index 000000000000..e9d2331b5710 --- /dev/null +++ b/layout/reftests/border-image/border-image-style-none-length-ref.html @@ -0,0 +1,37 @@ + + + + -moz-border-image-width: length with border-bottom: none reference + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + diff --git a/layout/reftests/border-image/border-image-style-none-length.html b/layout/reftests/border-image/border-image-style-none-length.html new file mode 100644 index 000000000000..9ec22014a54f --- /dev/null +++ b/layout/reftests/border-image/border-image-style-none-length.html @@ -0,0 +1,21 @@ + + + + test of -moz-border-image-width: length with border-bottom: none + + + + + +
    + + diff --git a/layout/reftests/border-image/border-image-style-none-ref.html b/layout/reftests/border-image/border-image-style-none-ref.html new file mode 100644 index 000000000000..48d4d0012089 --- /dev/null +++ b/layout/reftests/border-image/border-image-style-none-ref.html @@ -0,0 +1,32 @@ + + + + -moz-border-image with border-bottom: none reference + + + + + + + + + + + + + + + + + + + + +
    + + diff --git a/layout/reftests/border-image/border-image-style-none.html b/layout/reftests/border-image/border-image-style-none.html new file mode 100644 index 000000000000..38f2875aae9b --- /dev/null +++ b/layout/reftests/border-image/border-image-style-none.html @@ -0,0 +1,21 @@ + + + + test of -moz-border-image with border-bottom: none + + + + + +
    + + diff --git a/layout/reftests/border-image/reftest.list b/layout/reftests/border-image/reftest.list index f26a0bb02078..e661042fdac5 100644 --- a/layout/reftests/border-image/reftest.list +++ b/layout/reftests/border-image/reftest.list @@ -34,3 +34,6 @@ == border-image-nofill-1.html border-image-nofill-1-ref.html == border-image-outset-resize-1.html border-image-outset-resize-1-ref.html == border-image-outset-move-1.html border-image-outset-move-1-ref.html +== border-image-style-none.html border-image-style-none-ref.html +== border-image-style-none-length.html border-image-style-none-length-ref.html +== border-image-style-none-auto.html border-image-style-none-auto-ref.html From 236d6aa0275b0e5440d6eed5a63c88962e195084 Mon Sep 17 00:00:00 2001 From: William Chen Date: Thu, 22 Dec 2011 18:35:20 -0500 Subject: [PATCH 25/26] Bug 497995: Part 5 - Change existing border-image uses in the tree to match the new spec. r=dbaron --HG-- extra : rebase_source : 2adf368c0a144fe9de4958da626988694e914098 --- .../content/browser.css | 8 +++++-- browser/themes/gnomestripe/browser.css | 17 +++++++++---- browser/themes/pinstripe/browser.css | 24 ++++++++++++------- browser/themes/winstripe/browser.css | 12 +++++++--- .../themes/pinstripe/global/preferences.css | 4 ++-- 5 files changed, 44 insertions(+), 21 deletions(-) diff --git a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.css b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.css index f73098c22456..74b9e37f2674 100644 --- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.css +++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.css @@ -35,7 +35,9 @@ } .tail-up { - -moz-border-image: url(chrome://testpilot-os/skin/notification-tail-up.png) 26 56 22 18 / 26px 56px 22px 18px round stretch; + border-width: 26px 56px 22px 18px; + border-style: solid; + -moz-border-image: url(chrome://testpilot-os/skin/notification-tail-up.png) 26 56 22 18 fill round stretch; } /* tail-down uses the old styling; it doesn't look as good as the new styling, @@ -44,7 +46,9 @@ installed Test Pilot from AMO, they should get the new styling, similar to .tail-up! */ .tail-down { - -moz-border-image: url(chrome://testpilot/skin/notification-tail-down.png) 26 50 22 18 / 26px 50px 22px 18px repeat; + border-width: 26px 50px 22px 18px; + border-style: solid; + -moz-border-image: url(chrome://testpilot/skin/notification-tail-down.png) 26 50 22 18 fill repeat; color: white; } diff --git a/browser/themes/gnomestripe/browser.css b/browser/themes/gnomestripe/browser.css index 6c1d76384f37..35d3e8333e47 100644 --- a/browser/themes/gnomestripe/browser.css +++ b/browser/themes/gnomestripe/browser.css @@ -1239,8 +1239,9 @@ toolbar[iconsize="small"] #feed-button { background-clip: padding-box; padding-left: 4px; border-radius: 2.5px 0 0 2.5px; - -moz-border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 / 0 8px 0 0; - -moz-margin-end: -8px; + border-width: 0 8px 0 0; + border-style: solid; + -moz-border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 fill; -moz-margin-end: -8px; margin-top: -1px; margin-bottom: -1px; } @@ -1498,7 +1499,9 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- color: inherit; margin: 0; padding: 0; - -moz-border-image: url(tabbrowser/tab.png) 4 5 3 6 / 4px 5px 3px 6px repeat stretch; + border-width: 4px 5px 3px 6px; + border-style: solid; + -moz-border-image: url(tabbrowser/tab.png) 4 5 3 6 fill repeat stretch; border-radius: 10px 8px 0 0; min-height: 25px; /* reserve space for the sometimes hidden close button */ } @@ -1670,12 +1673,16 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(ltr), .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(rtl) { - -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 / 0 2px 0 0; + border-width: 0 2px 0 0; + border-style: solid; + -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill; } .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(ltr), .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(rtl) { - -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 / 0 0 0 2px; + border-width: 0 0 0 2px; + border-style: solid; + -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill; } #TabsToolbar .toolbarbutton-1 > .toolbarbutton-icon, diff --git a/browser/themes/pinstripe/browser.css b/browser/themes/pinstripe/browser.css index 54d9c174b022..e35d00cf2ffa 100644 --- a/browser/themes/pinstripe/browser.css +++ b/browser/themes/pinstripe/browser.css @@ -1856,30 +1856,30 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { @TABSONTOP_TAB_STACK@ > .tab-content, @TABSONTOP_NEWTAB_BUTTON@ > .toolbarbutton-icon { - -moz-border-image: url(chrome://browser/skin/tabbrowser/tab-top-normal-active.png) 0 11 repeat stretch; + -moz-border-image: url(chrome://browser/skin/tabbrowser/tab-top-normal-active.png) 0 11 fill repeat stretch; } @TABSONTOP_TAB@:hover > .tab-stack > .tab-content:not([selected="true"]), @TABSONTOP_NEWTAB_BUTTON@:hover > .toolbarbutton-icon { - -moz-border-image: url(chrome://browser/skin/tabbrowser/tab-top-hover-active.png) 0 11 repeat stretch; + -moz-border-image: url(chrome://browser/skin/tabbrowser/tab-top-hover-active.png) 0 11 fill repeat stretch; } @TABSONTOP_TAB_STACK@ > .tab-content[selected="true"] { - -moz-border-image: url(chrome://browser/skin/tabbrowser/tab-top-selected-active.png) 0 11 repeat stretch; + -moz-border-image: url(chrome://browser/skin/tabbrowser/tab-top-selected-active.png) 0 11 fill repeat stretch; } @TABSONBOTTOM_TAB_STACK@ > .tab-content, @TABSONBOTTOM_NEWTAB_BUTTON@ > .toolbarbutton-icon { - -moz-border-image: url(chrome://browser/skin/tabbrowser/tab-bottom-normal-active.png) 0 11 repeat stretch; + -moz-border-image: url(chrome://browser/skin/tabbrowser/tab-bottom-normal-active.png) 0 11 fill repeat stretch; } @TABSONBOTTOM_TAB@:hover > .tab-stack > .tab-content:not([selected="true"]), @TABSONBOTTOM_NEWTAB_BUTTON@:hover > .toolbarbutton-icon { - -moz-border-image: url(chrome://browser/skin/tabbrowser/tab-bottom-hover-active.png) 0 11 repeat stretch; + -moz-border-image: url(chrome://browser/skin/tabbrowser/tab-bottom-hover-active.png) 0 11 fill repeat stretch; } @TABSONBOTTOM_TAB_STACK@ > .tab-content[selected="true"] { - -moz-border-image: url(chrome://browser/skin/tabbrowser/tab-bottom-selected-active.png) 0 11 repeat stretch; + -moz-border-image: url(chrome://browser/skin/tabbrowser/tab-bottom-selected-active.png) 0 11 fill repeat stretch; } /* preloading hack */ @@ -2090,12 +2090,16 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(ltr), .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(rtl) { - -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 / 0 2px 0 0; + border-width: 0 2px 0 0; + border-style: solid; + -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill; } .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(ltr), .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(rtl) { - -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 / 0 0 0 2px; + border-width: 0 0 0 2px; + border-style: solid; + -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill; } /** @@ -2310,7 +2314,9 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { background-clip: padding-box; padding-left: 3px; border-radius: 2px 0 0 2px; - -moz-border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 / 0 8px 0 0; + border-width: 0 8px 0 0; + border-style: solid; + -moz-border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 fill; -moz-margin-end: -8px; } diff --git a/browser/themes/winstripe/browser.css b/browser/themes/winstripe/browser.css index 7c2d5126bef3..2cb0fb948ee0 100644 --- a/browser/themes/winstripe/browser.css +++ b/browser/themes/winstripe/browser.css @@ -1819,7 +1819,9 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- background-repeat: no-repeat; margin: 0; padding: 2px 0 4px; - -moz-border-image: url(tabbrowser/tab.png) 4 3 0 / 4px 3px 0 repeat stretch; + border-width: 4px 3px 0; + border-style: solid; + -moz-border-image: url(tabbrowser/tab.png) 4 3 0 fill repeat stretch; border-radius: 0; } @@ -2014,7 +2016,9 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]), .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]) { - -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 / 0 2px 0 0; + border-width: 0 2px 0 0; + border-style: solid; + -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill; } .tabs-newtab-button > .toolbarbutton-icon { @@ -2312,7 +2316,9 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] { background-clip: padding-box; padding-left: 3px; border-radius: 2.5px 0 0 2.5px; - -moz-border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 / 0 8px 0 0; + border-width: 0 8px 0 0; + border-style: solid; + -moz-border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 fill; -moz-margin-end: -8px; } diff --git a/toolkit/themes/pinstripe/global/preferences.css b/toolkit/themes/pinstripe/global/preferences.css index 35e7b66830eb..48f7f84c4b1a 100644 --- a/toolkit/themes/pinstripe/global/preferences.css +++ b/toolkit/themes/pinstripe/global/preferences.css @@ -85,11 +85,11 @@ radio[pane]:active:hover > .paneButtonIcon { } radio[pane][selected="true"] { - -moz-border-image: url("chrome://global/skin/icons/panebutton-active.png") 0 2 repeat stretch; + -moz-border-image: url("chrome://global/skin/icons/panebutton-active.png") 0 2 fill repeat stretch; } radio[pane][selected="true"]:-moz-window-inactive { - -moz-border-image: url("chrome://global/skin/icons/panebutton-inactive.png") 0 2 repeat stretch; + -moz-border-image: url("chrome://global/skin/icons/panebutton-inactive.png") 0 2 fill repeat stretch; } .paneButtonLabel { From e904a6e8a3581ae4fcbe908dd9aa8213283e58ee Mon Sep 17 00:00:00 2001 From: Joerg Sonnenberger Date: Thu, 22 Dec 2011 18:38:27 -0500 Subject: [PATCH 26/26] Bug 703878: Fix potential race condition in header.py. r=khuey --HG-- extra : rebase_source : 843bf4e7c427ce7d2a976af99d62a32b9dd142ee --- xpcom/idl-parser/header.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/xpcom/idl-parser/header.py b/xpcom/idl-parser/header.py index 5dd7734fd6c5..948c824d99be 100644 --- a/xpcom/idl-parser/header.py +++ b/xpcom/idl-parser/header.py @@ -527,6 +527,12 @@ if __name__ == '__main__': outfd.close() if options.depfile is not None: + dirname = os.path.dirname(options.depfile) + if dirname: + try: + os.makedirs(dirname) + except: + pass depfd = open(options.depfile, 'w') deps = [dep.replace('\\', '/') for dep in idl.deps]