From 9adc7a05fbf265df2d9053097551c10644651799 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Wed, 21 Aug 2013 09:58:25 +0100 Subject: [PATCH 01/54] Bug 907147 - Assertion failure: type->proto->hasNewType(&JSObject::class_, type), at jsobj.cpp:1423 r=terrence --- js/src/jsinfer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 269fef925e3..0c63691b4c6 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -6054,6 +6054,9 @@ ExclusiveContext::getNewType(Class *clasp, TaggedProto proto_, JSFunction *fun_) return type; } + JSContext *cx = asJSContext(); + SkipRoot skipHash(cx, &p); /* Prevent the hash from being poisoned. */ + Rooted proto(this, proto_); RootedFunction fun(this, fun_); @@ -6075,7 +6078,6 @@ ExclusiveContext::getNewType(Class *clasp, TaggedProto proto_, JSFunction *fun_) if (!typeInferenceEnabled()) return type; - JSContext *cx = asJSContext(); AutoEnterAnalysis enter(cx); /* From fe28daf9a9551019924278761ec750c663b9f0ff Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Wed, 21 Aug 2013 19:02:42 +0900 Subject: [PATCH 02/54] Bug 904956 - test_handlerService.js might not work on Windows Server 2012. r=rstrong --- .../tests/unit/test_handlerService.js | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/uriloader/exthandler/tests/unit/test_handlerService.js b/uriloader/exthandler/tests/unit/test_handlerService.js index 028540e20eb..8c7c8db9d34 100644 --- a/uriloader/exthandler/tests/unit/test_handlerService.js +++ b/uriloader/exthandler/tests/unit/test_handlerService.js @@ -26,15 +26,22 @@ function run_test() { const rootPrefBranch = prefSvc.getBranch(""); - let isWin7 = false; + let noMailto = false; let isWindows = ("@mozilla.org/windows-registry-key;1" in Components.classes); if (isWindows) { + // Check mailto handler from registry. + // If registry entry is nothing, no mailto handler + let regSvc = Cc["@mozilla.org/windows-registry-key;1"]. + createInstance(Ci.nsIWindowsRegKey); try { - let version = Cc["@mozilla.org/system-info;1"] - .getService(Ci.nsIPropertyBag2) - .getProperty("version"); - isWin7 = (parseFloat(version) == 6.1); - } catch (ex) { } + regSvc.open(regSvc.ROOT_KEY_CLASSES_ROOT, + "mailto", + regSvc.ACCESS_READ); + noMailto = false; + } catch (ex) { + noMailto = true; + } + regSvc.close(); } //**************************************************************************// @@ -150,8 +157,8 @@ function run_test() { else do_check_eq(0, protoInfo.possibleApplicationHandlers.length); - // Win7 doesn't have a default mailto: handler - if (isWin7) + // Win7+ might not have a default mailto: handler + if (noMailto) do_check_true(protoInfo.alwaysAskBeforeHandling); else do_check_false(protoInfo.alwaysAskBeforeHandling); @@ -161,12 +168,12 @@ function run_test() { protoInfo = protoSvc.getProtocolHandlerInfo("mailto"); if (haveDefaultHandlersVersion) { do_check_eq(2, protoInfo.possibleApplicationHandlers.length); - // Win7 doesn't have a default mailto: handler, but on other platforms + // Win7+ might not have a default mailto: handler, but on other platforms // alwaysAskBeforeHandling is expected to be false here, because although // the pref is true, the value in RDF is false. The injected mailto handler // carried over the default pref value, and so when we set the pref above // to true it's ignored. - if (isWin7) + if (noMailto) do_check_true(protoInfo.alwaysAskBeforeHandling); else do_check_false(protoInfo.alwaysAskBeforeHandling); From 68423ee72770f286224bb135f8c0f1445736e5a3 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Wed, 21 Aug 2013 11:11:23 +0100 Subject: [PATCH 03/54] Backed out changeset daaee7df86ae (bug 907147) for assertions --- js/src/jsinfer.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 0c63691b4c6..269fef925e3 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -6054,9 +6054,6 @@ ExclusiveContext::getNewType(Class *clasp, TaggedProto proto_, JSFunction *fun_) return type; } - JSContext *cx = asJSContext(); - SkipRoot skipHash(cx, &p); /* Prevent the hash from being poisoned. */ - Rooted proto(this, proto_); RootedFunction fun(this, fun_); @@ -6078,6 +6075,7 @@ ExclusiveContext::getNewType(Class *clasp, TaggedProto proto_, JSFunction *fun_) if (!typeInferenceEnabled()) return type; + JSContext *cx = asJSContext(); AutoEnterAnalysis enter(cx); /* From 9631abf6a7aa372410cbdfa2c62c691dbea28341 Mon Sep 17 00:00:00 2001 From: Ian Stakenvicius Date: Wed, 21 Aug 2013 08:42:49 -0400 Subject: [PATCH 04/54] Bug 517765 - Add 'make source-package' support into js/src. r=sstangl --- js/src/Makefile.in | 36 +++++++++- js/src/configure.in | 8 +++ js/src/make-source-package.sh | 123 ++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 js/src/make-source-package.sh diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 5a736a6109a..cddaaf59310 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -342,12 +342,17 @@ endif endif DIST_GARBAGE = config.cache config.log config.status* \ - config/autoconf.mk \ - unallmakefiles $(JS_CONFIG_NAME) js-config.h js-confdefs.h + config/autoconf.mk config/emptyvars.mk \ + unallmakefiles $(JS_CONFIG_NAME) js-config.h js-confdefs.h \ + backend.mk config/backend.mk devtools/backend.mk editline/backend.mk \ + gdb/backend.mk jsapi-tests/backend.mk shell/backend.mk tests/backend.mk \ + backend.RecursiveMakeBackend.built backend.RecursiveMakeBackend.built.pp \ + devtools/rootAnalysis/Makefile distclean:: cat unallmakefiles | $(XARGS) rm -f $(RM) $(DIST_GARBAGE) + rm -rf intl _virtualenv DEFINES += -DEXPORT_JS_API @@ -671,3 +676,30 @@ endif # # END kludges for the Nitro assembler ############################################### + +############################################### +# Generating source package tarballs +# (only possible when tar is found) +ifneq (,$(TAR)) + +source-package: + SRCDIR=$(srcdir) \ + DIST=$(DIST) \ + MAKE=$(MAKE) \ + MKDIR=$(MKDIR) \ + TAR=$(TAR) \ + MOZJS_MAJOR_VERSION=$(MOZJS_MAJOR_VERSION) \ + MOZJS_MINOR_VERSION=$(MOZJS_MINOR_VERSION) \ + MOZJS_PATCH_VERSION=$(MOZJS_PATCH_VERSION) \ + MOZJS_ALPHA=$(MOZJS_ALPHA) \ + $(srcdir)/make-source-package.sh + +clean:: + DIST=$(DIST) \ + MOZJS_MAJOR_VERSION=$(MOZJS_MAJOR_VERSION) \ + MOZJS_MINOR_VERSION=$(MOZJS_MINOR_VERSION) \ + MOZJS_PATCH_VERSION=$(MOZJS_PATCH_VERSION) \ + MOZJS_ALPHA=$(MOZJS_ALPHA) \ + $(srcdir)/make-source-package.sh clean + +endif diff --git a/js/src/configure.in b/js/src/configure.in index 245e84adbb8..072270b1e74 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -571,6 +571,14 @@ dnl ======================================================== AC_PROG_INSTALL AC_PROG_LN_S +AC_MSG_CHECKING([for tar archiver]) +AC_CHECK_PROGS(TAR, gnutar gtar tar, "") +if test -z "$TAR"; then + AC_MSG_WARN([no tar archiver found in \$PATH]) +fi +AC_MSG_RESULT([$TAR]) +AC_SUBST(TAR) + if test -z "$TINDERBOX_SKIP_PERL_VERSION_CHECK"; then AC_MSG_CHECKING([for minimum required perl version >= $PERL_VERSION]) _perl_version=`PERL_VERSION=$PERL_VERSION $PERL -e 'print "$]"; if ($] >= $ENV{PERL_VERSION}) { exit(0); } else { exit(1); }' 2>&5` diff --git a/js/src/make-source-package.sh b/js/src/make-source-package.sh new file mode 100644 index 00000000000..2db12e3dfd4 --- /dev/null +++ b/js/src/make-source-package.sh @@ -0,0 +1,123 @@ +#!/bin/sh +# need these environment vars: +echo "Environment:" +echo " MAKE = $MAKE" +echo " MKDIR = $MKDIR" +echo " TAR = $TAR" +echo " DIST = $DIST" +echo " SRCDIR = $SRCDIR" +echo " MOZJS_MAJOR_VERSION = $MOZJS_MAJOR_VERSION" +echo " MOZJS_MINOR_VERSION = $MOZJS_MINOR_VERSION" +echo " MOZJS_PATCH_VERSION = $MOZJS_PATCH_VERSION" +echo " MOZJS_ALPHA = $MOZJS_ALPHA" + +cmd=${1:-build} +pkg="mozjs-${MOZJS_MAJOR_VERSION}.${MOZJS_MINOR_VERSION}.${MOZJS_PATCH_VERSION:-${MOZJS_ALPHA:-0}}.tar.bz2" +pkgpath=${pkg%.tar*} +tgtpath=${DIST}/${pkgpath} +taropts="-jcf" + +case $cmd in +"clean") + echo "Cleaning ${pkg} and ${tgtpath} ..." + rm -rf ${pkg} ${tgtpath} + ;; +"build") + echo "Packaging source tarball ${pkg}..." + if [ -d ${tgtpath} ]; then + echo "WARNING - dist tree ${tgtpath} already exists!" + fi + ${MKDIR} -p ${tgtpath}/js/src + + # copy the embedded icu + ${MKDIR} -p ${tgtpath}/intl + cp -t ${tgtpath}/intl -dRp ${SRCDIR}/../../intl/icu + + # put in js itself + cp -t ${tgtpath} -dRp ${SRCDIR}/../../mfbt + cp -t ${tgtpath}/js -dRp ${SRCDIR}/../jsd ${SRCDIR}/../public + find ${SRCDIR} -mindepth 1 -maxdepth 1 -not -path ${DIST} -a -not -name ${pkg} \ + -exec cp -t ${tgtpath}/js/src -dRp {} + + + # distclean if necessary + if [ -e ${tgtpath}/js/src/Makefile ]; then + ${MAKE} -C ${tgtpath}/js/src distclean + fi + + # put in the virtualenv and supporting files if it doesnt already exist + if [ ! -e ${SRCDIR}/build/virtualenv ]; then + cp -t ${tgtpath}/js/src/build -dRp \ + ${SRCDIR}/../../build/virtualenv \ + ${SRCDIR}/../../build/buildconfig.py + fi + if [ ! -e ${SRCDIR}/python ]; then + cp -t ${tgtpath}/js/src -dRp \ + ${SRCDIR}/../../python + fi + if [ ! -e ${SRCDIR}/testing ]; then + ${MKDIR} -p ${tgtpath}/js/src/testing + cp -t ${tgtpath}/js/src/testing -dRp \ + ${SRCDIR}/../../testing/mozbase + fi + # end of virtualenv injection + + # remove *.pyc and *.pyo files if any + find ${tgtpath} -type f -name "*.pyc" -o -name "*.pyo" |xargs rm -f + + # copy or create INSTALL + if [ -e {DIST}/INSTALL ]; then + cp -t ${tgtpath} ${DIST}/INSTALL + else + cat <${tgtpath}/INSTALL +Full build documentation for SpiderMonkey is hosted on MDN: + https://developer.mozilla.org/en-US/docs/SpiderMonkey/Build_Documentation + +Note that the libraries produced by the build system include symbols, +causing the binaries to be extremely large. It is highly suggested that \`strip\` +be run over the binaries before deploying them. + +Building with default options may be performed as follows: + cd js/src + ./configure + make +INSTALL_EOF + fi + + # copy or create README + if [ -e ${DIST}/README ]; then + cp -t ${tgtpath} ${DIST}/README + else + cat <${tgtpath}/README +This directory contains SpiderMonkey ${MOZJS_MAJOR_VERSION}. + +This release is based on a revision of Mozilla ${MOZJS_MAJOR_VERSION}: + http://hg.mozilla.org/releases/ +The changes in the patches/ directory were applied. + +MDN hosts the latest SpiderMonkey ${MOZJS_MAJOR_VERSION} release notes: + https://developer.mozilla.org/en-US/docs/SpiderMonkey/${MOZJS_MAJOR_VERSION} +README_EOF + fi + + # copy LICENSE + if [ -e ${SRCDIR}/../../b2g/LICENSE ]; then + cp ${SRCDIR}/../../b2g/LICENSE ${tgtpath}/ + else + cp ${SRCDIR}/../../LICENSE ${tgtpath}/ + fi + + # copy patches dir, if it currently exists in DIST + if [ -d ${DIST}/patches ]; then + cp -t ${tgtpath} -dRp ${DIST}/patches + elif [ -d ${SRCDIR}/../../patches ]; then + cp -t ${tgtpath} -dRp ${SRCDIR}/../../patches + fi + + # Roll the tarball + ${TAR} $taropts ${DIST}/../${pkg} -C ${DIST} ${pkgpath} + echo "done." + ;; +*) + echo "Unrecognized command: $cmd" + ;; +esac From 5b561cf5dfd27839680a85472805334834694d92 Mon Sep 17 00:00:00 2001 From: Adrian Lungu Date: Wed, 21 Aug 2013 03:03:16 -0700 Subject: [PATCH 05/54] Bug 849204 - Linux geolocation: Init dbus_threads from the main thread. r=karlt --- toolkit/xre/nsNativeAppSupportUnix.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/toolkit/xre/nsNativeAppSupportUnix.cpp b/toolkit/xre/nsNativeAppSupportUnix.cpp index 4173c2371ba..544da48339f 100644 --- a/toolkit/xre/nsNativeAppSupportUnix.cpp +++ b/toolkit/xre/nsNativeAppSupportUnix.cpp @@ -36,10 +36,17 @@ #include #endif +#ifdef MOZ_ENABLE_DBUS +#include +#endif + #if (MOZ_PLATFORM_MAEMO == 5) struct DBusMessage; /* libosso.h references internals of dbus */ +#ifndef MOZ_ENABLE_DBUS #include +#endif + #include #include @@ -399,6 +406,13 @@ nsNativeAppSupportUnix::Start(bool *aRetVal) { NS_ASSERTION(gAppData, "gAppData must not be null."); +// The dbus library is used by both nsWifiScannerDBus and BluetoothDBusService, +// from diffrent threads. This could lead to race conditions if the dbus is not +// initialized before making any other library calls. +#ifdef MOZ_ENABLE_DBUS + dbus_threads_init_default(); +#endif + #if (MOZ_WIDGET_GTK == 2) if (gtk_major_version < MIN_GTK_MAJOR_VERSION || (gtk_major_version == MIN_GTK_MAJOR_VERSION && gtk_minor_version < MIN_GTK_MINOR_VERSION)) { From 7dd364f0de4998a7e0c5c1c80e549bdfe9cb0149 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Thu, 13 Jun 2013 14:24:31 +0200 Subject: [PATCH 06/54] Bug 884708 - Port GTK2 to GTK3, build fixes. r=karlt --- toolkit/components/downloads/nsDownloadManager.cpp | 10 +++++----- widget/gtk2/nsDeviceContextSpecG.cpp | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/toolkit/components/downloads/nsDownloadManager.cpp b/toolkit/components/downloads/nsDownloadManager.cpp index c194a00f1d3..13afab1d21f 100644 --- a/toolkit/components/downloads/nsDownloadManager.cpp +++ b/toolkit/components/downloads/nsDownloadManager.cpp @@ -53,7 +53,7 @@ #include "AndroidBridge.h" #endif -#ifdef MOZ_WIDGET_GTK2 +#ifdef MOZ_WIDGET_GTK #include #endif @@ -103,7 +103,7 @@ nsDownloadManager::GetSingleton() gDownloadManagerService = new nsDownloadManager(); if (gDownloadManagerService) { -#if defined(MOZ_WIDGET_GTK2) +#if defined(MOZ_WIDGET_GTK) g_type_init(); #endif NS_ADDREF(gDownloadManagerService); @@ -2717,7 +2717,7 @@ nsDownload::SetState(DownloadState aState) } } -#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GTK2) +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GTK) nsCOMPtr fileURL = do_QueryInterface(mTarget); nsCOMPtr file; nsAutoString path; @@ -2727,7 +2727,7 @@ nsDownload::SetState(DownloadState aState) file && NS_SUCCEEDED(file->GetPath(path))) { -#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK2) +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) // On Windows and Gtk, add the download to the system's "recent documents" // list, with a pref to disable. { @@ -2738,7 +2738,7 @@ nsDownload::SetState(DownloadState aState) if (addToRecentDocs && !mPrivate) { #ifdef XP_WIN ::SHAddToRecentDocs(SHARD_PATHW, path.get()); -#elif defined(MOZ_WIDGET_GTK2) +#elif defined(MOZ_WIDGET_GTK) GtkRecentManager* manager = gtk_recent_manager_get_default(); gchar* uri = g_filename_to_uri(NS_ConvertUTF16toUTF8(path).get(), diff --git a/widget/gtk2/nsDeviceContextSpecG.cpp b/widget/gtk2/nsDeviceContextSpecG.cpp index 88c8edd4b41..c7c60a736ca 100644 --- a/widget/gtk2/nsDeviceContextSpecG.cpp +++ b/widget/gtk2/nsDeviceContextSpecG.cpp @@ -540,7 +540,11 @@ nsresult nsDeviceContextSpecGTK::GetPrintMethod(const char *aPrinter, PrintMetho } static void +#if (MOZ_WIDGET_GTK == 3) +print_callback(GtkPrintJob *aJob, gpointer aData, const GError *aError) { +#else print_callback(GtkPrintJob *aJob, gpointer aData, GError *aError) { +#endif g_object_unref(aJob); ((nsIFile*) aData)->Remove(false); } From d111da916d607dcc929d6c20db7cb11a33d5c4ef Mon Sep 17 00:00:00 2001 From: Michael Harrison Date: Wed, 21 Aug 2013 08:44:14 -0400 Subject: [PATCH 07/54] Bug 897491 - Update the display when the alt attribute of an image is added or removed. r=bz --- content/html/content/src/HTMLImageElement.cpp | 6 ++++++ layout/reftests/bugs/897491-1-ref.html | 7 +++++++ layout/reftests/bugs/897491-1.html | 15 +++++++++++++++ layout/reftests/bugs/897491-2-ref.html | 7 +++++++ layout/reftests/bugs/897491-2.html | 15 +++++++++++++++ layout/reftests/bugs/reftest.list | 2 ++ 6 files changed, 52 insertions(+) create mode 100644 layout/reftests/bugs/897491-1-ref.html create mode 100644 layout/reftests/bugs/897491-1.html create mode 100644 layout/reftests/bugs/897491-2-ref.html create mode 100644 layout/reftests/bugs/897491-2.html diff --git a/content/html/content/src/HTMLImageElement.cpp b/content/html/content/src/HTMLImageElement.cpp index c426fd8838e..e933f62b05c 100644 --- a/content/html/content/src/HTMLImageElement.cpp +++ b/content/html/content/src/HTMLImageElement.cpp @@ -13,6 +13,7 @@ #include "nsMappedAttributes.h" #include "nsSize.h" #include "nsIDocument.h" +#include "nsIDOMMutationEvent.h" #include "nsIScriptContext.h" #include "nsIURL.h" #include "nsIIOService.h" @@ -251,6 +252,11 @@ HTMLImageElement::GetAttributeChangeHint(const nsIAtom* aAttribute, if (aAttribute == nsGkAtoms::usemap || aAttribute == nsGkAtoms::ismap) { NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE); + } else if (aAttribute == nsGkAtoms::alt) { + if (aModType == nsIDOMMutationEvent::ADDITION || + aModType == nsIDOMMutationEvent::REMOVAL) { + NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE); + } } return retval; } diff --git a/layout/reftests/bugs/897491-1-ref.html b/layout/reftests/bugs/897491-1-ref.html new file mode 100644 index 00000000000..e8d89d4426f --- /dev/null +++ b/layout/reftests/bugs/897491-1-ref.html @@ -0,0 +1,7 @@ + + + +alt1 + + + diff --git a/layout/reftests/bugs/897491-1.html b/layout/reftests/bugs/897491-1.html new file mode 100644 index 00000000000..8aae62967a7 --- /dev/null +++ b/layout/reftests/bugs/897491-1.html @@ -0,0 +1,15 @@ + + + + +alt2 + + + diff --git a/layout/reftests/bugs/897491-2-ref.html b/layout/reftests/bugs/897491-2-ref.html new file mode 100644 index 00000000000..921733e6804 --- /dev/null +++ b/layout/reftests/bugs/897491-2-ref.html @@ -0,0 +1,7 @@ + + + +alt1 + + + diff --git a/layout/reftests/bugs/897491-2.html b/layout/reftests/bugs/897491-2.html new file mode 100644 index 00000000000..9b1402abc92 --- /dev/null +++ b/layout/reftests/bugs/897491-2.html @@ -0,0 +1,15 @@ + + + + +alt2 + + + diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 7cf9e9b396a..e1e4d597331 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1772,3 +1772,5 @@ test-pref(layout.css.flexbox.enabled,true) == 849407-1.html 849407-1-ref.html == 883987-1f.html 883987-1-ref.html == 890495-1.html 890495-1-ref.html == 894931-1.html 894931-1-ref.html +== 897491-1.html 897491-1-ref.html +== 897491-2.html 897491-2-ref.html From decaefa3d30a2ed431676c5b9cd5f0c7c58b0dfc Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Thu, 8 Aug 2013 15:56:09 -0400 Subject: [PATCH 08/54] Bug 898580 - Add nsIDOMWindowUtils.getViewId(element). r=smaug --- dom/base/nsDOMWindowUtils.cpp | 10 ++++++++++ dom/interfaces/base/nsIDOMWindowUtils.idl | 8 +++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index e8170d7a2d2..b5fe80ff25b 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -1694,6 +1694,16 @@ nsDOMWindowUtils::FindElementWithViewId(nsViewID aID, return content ? CallQueryInterface(content, aResult) : NS_OK; } +NS_IMETHODIMP +nsDOMWindowUtils::GetViewId(nsIDOMElement* aElement, nsViewID* aResult) +{ + nsCOMPtr content = do_QueryInterface(aElement); + if (content && nsLayoutUtils::FindIDFor(content, aResult)) { + return NS_OK; + } + return NS_ERROR_NOT_AVAILABLE; +} + NS_IMETHODIMP nsDOMWindowUtils::GetScreenPixelsPerCSSPixel(float* aScreenPixels) { diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 5f397fda534..dffb0f9dfd9 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -42,7 +42,7 @@ interface nsIURI; interface nsIDOMEventTarget; interface nsIRunnable; -[scriptable, uuid(ff1cec22-b183-40d3-8b42-b81a2f0ba4e6)] +[scriptable, uuid(d6e733ef-492b-4e67-b723-28571c2959f0)] interface nsIDOMWindowUtils : nsISupports { /** @@ -1177,6 +1177,12 @@ interface nsIDOMWindowUtils : nsISupports { */ nsIDOMElement findElementWithViewId(in nsViewID aId); + /** + * Find the view ID for a given element. This is the reverse of + * findElementWithViewId(). + */ + nsViewID getViewId(in nsIDOMElement aElement); + /** * Checks the layer tree for this window and returns true * if all layers have transforms that are translations by integers, From 928f2bf87a401d288dd5f4baa3aba9331ebd1e1c Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Mon, 12 Aug 2013 16:31:49 -0400 Subject: [PATCH 09/54] Bug 898580 - Metro mechanism for content to inform APZC about scroll events. Handles multi-APZC. r=bbondy, r=kats --- browser/metro/base/content/apzc.js | 15 ++++++- .../metro/base/content/bindings/browser.js | 40 ++++++++++++----- .../metro/base/content/bindings/browser.xml | 4 +- widget/windows/winrt/MetroWidget.cpp | 43 +++++++++++++++++++ widget/windows/winrt/MetroWidget.h | 10 +++-- 5 files changed, 96 insertions(+), 16 deletions(-) diff --git a/browser/metro/base/content/apzc.js b/browser/metro/base/content/apzc.js index 9729ca299b9..0509c2950ce 100644 --- a/browser/metro/base/content/apzc.js +++ b/browser/metro/base/content/apzc.js @@ -46,11 +46,13 @@ var APZCObserver = { case 'TabOpen': { let browser = aEvent.originalTarget.linkedBrowser; browser.addEventListener("pageshow", this, true); + browser.messageManager.addMessageListener("scroll", this); break; } case 'TabClose': { let browser = aEvent.originalTarget.linkedBrowser; - browser.removeEventListener("pageshow", this); + browser.removeEventListener("pageshow", this, true); + browser.messageManager.removeMessageListener("scroll", this); break; } } @@ -113,5 +115,16 @@ var APZCObserver = { } else if (aTopic == "apzc-handle-pan-end") { Util.dumpLn("APZC pan-end"); } + }, + + receiveMessage: function(aMessage) { + let json = aMessage.json; + switch (aMessage.name) { + case "scroll": { + let data = json.viewId + " " + json.presShellId + " (" + json.scrollOffset.x + ", " + json.scrollOffset.y + ")"; + Services.obs.notifyObservers(null, "scroll-offset-changed", data); + break; + } + } } }; diff --git a/browser/metro/base/content/bindings/browser.js b/browser/metro/base/content/bindings/browser.js index 8dbff2d248f..39045349c2f 100644 --- a/browser/metro/base/content/bindings/browser.js +++ b/browser/metro/base/content/bindings/browser.js @@ -652,11 +652,7 @@ let ContentScroll = { break; case "scroll": { - let doc = aEvent.target; - if (doc != content.document) - break; - - this.sendScroll(); + this.sendScroll(aEvent.target); break; } @@ -683,13 +679,35 @@ let ContentScroll = { } }, - sendScroll: function sendScroll() { - let scrollOffset = this.getScrollOffset(content); - if (this._scrollOffset.x == scrollOffset.x && this._scrollOffset.y == scrollOffset.y) - return; + sendScroll: function sendScroll(target) { + let isRoot = false; + if (target instanceof Ci.nsIDOMDocument) { + var window = target.defaultView; + var scrollOffset = this.getScrollOffset(window); + var element = target.documentElement; - this._scrollOffset = scrollOffset; - sendAsyncMessage("scroll", scrollOffset); + if (target == content.document) { + if (this._scrollOffset.x == scrollOffset.x && this._scrollOffset.y == scrollOffset.y) { + return; + } + this._scrollOffset = scrollOffset; + isRoot = true; + } + } else { + var window = target.currentDoc.defaultView; + var scrollOffset = this.getScrollOffsetForElement(target); + var element = target; + } + + let utils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + let presShellId = {}; + utils.getPresShellId(presShellId); + let viewId = utils.getViewId(element); + + sendAsyncMessage("scroll", { presShellId: presShellId.value, + viewId: viewId, + scrollOffset: scrollOffset, + isRoot: isRoot }); } }; diff --git a/browser/metro/base/content/bindings/browser.xml b/browser/metro/base/content/bindings/browser.xml index f7340589a75..67db5babe3e 100644 --- a/browser/metro/base/content/bindings/browser.xml +++ b/browser/metro/base/content/bindings/browser.xml @@ -859,9 +859,11 @@ switch (aMessage.name) { case "scroll": + if (!json.isRoot) + return; if (!self.scrollSync) return; - this.doScroll(json.x, json.y, 0); + this.doScroll(json.scrollOffset.x, json.scrollOffset.y, 0); break; } }, diff --git a/widget/windows/winrt/MetroWidget.cpp b/widget/windows/winrt/MetroWidget.cpp index 854430232f0..5d49fb94ff5 100644 --- a/widget/windows/winrt/MetroWidget.cpp +++ b/widget/windows/winrt/MetroWidget.cpp @@ -163,6 +163,7 @@ MetroWidget::MetroWidget() : mWnd(NULL), mMetroWndProc(NULL), mTempBasicLayerInUse(false), + mRootLayerTreeId(), nsWindowBase() { // Global initialization @@ -271,6 +272,14 @@ MetroWidget::Destroy() nsCOMPtr kungFuDeathGrip(this); + if (ShouldUseAPZC()) { + nsresult rv; + nsCOMPtr observerService = do_GetService("@mozilla.org/observer-service;1", &rv); + if (NS_SUCCEEDED(rv)) { + observerService->RemoveObserver(this, "scroll-offset-changed"); + } + } + RemoveSubclass(); NotifyWindowDestroyed(); @@ -829,6 +838,13 @@ CompositorParent* MetroWidget::NewCompositorParent(int aSurfaceWidth, int aSurfa if (ShouldUseAPZC()) { CompositorParent::SetControllerForLayerTree(compositor->RootLayerTreeId(), this); MetroWidget::sAPZC = CompositorParent::GetAPZCTreeManager(compositor->RootLayerTreeId()); + mRootLayerTreeId = compositor->RootLayerTreeId(); + + nsresult rv; + nsCOMPtr observerService = do_GetService("@mozilla.org/observer-service;1", &rv); + if (NS_SUCCEEDED(rv)) { + observerService->AddObserver(this, "scroll-offset-changed", false); + } } return compositor; @@ -1446,3 +1462,30 @@ MetroWidget::HandlePanEnd() LogFunction(); MetroUtils::FireObserver("apzc-handle-pan-end", L""); } + +NS_IMETHODIMP +MetroWidget::Observe(nsISupports *subject, const char *topic, const PRUnichar *data) +{ + NS_ENSURE_ARG_POINTER(topic); + if (!strcmp(topic, "scroll-offset-changed")) { + uint64_t scrollId; + int32_t presShellId; + CSSIntPoint scrollOffset; + int matched = sscanf(NS_LossyConvertUTF16toASCII(data).get(), + "%llu %d (%d, %d)", + &scrollId, + &presShellId, + &scrollOffset.x, + &scrollOffset.y); + if (matched != 4) { + NS_WARNING("Malformed scroll-offset-changed message"); + return NS_ERROR_UNEXPECTED; + } + if (MetroWidget::sAPZC) { + MetroWidget::sAPZC->UpdateScrollOffset( + ScrollableLayerGuid(mRootLayerTreeId, presShellId, scrollId), + scrollOffset); + } + } + return NS_OK; +} \ No newline at end of file diff --git a/widget/windows/winrt/MetroWidget.h b/widget/windows/winrt/MetroWidget.h index 0a049bd604d..5d9aef8c6cd 100644 --- a/widget/windows/winrt/MetroWidget.h +++ b/widget/windows/winrt/MetroWidget.h @@ -46,7 +46,8 @@ class FrameworkView; } } } class MetroWidget : public nsWindowBase, - public mozilla::layers::GeckoContentController + public mozilla::layers::GeckoContentController, + public nsIObserver { typedef mozilla::widget::WindowHook WindowHook; typedef mozilla::widget::TaskbarWindowPreview TaskbarWindowPreview; @@ -55,6 +56,7 @@ class MetroWidget : public nsWindowBase, typedef ABI::Windows::UI::Core::IKeyEventArgs IKeyEventArgs; typedef ABI::Windows::UI::Core::ICharacterReceivedEventArgs ICharacterReceivedEventArgs; typedef mozilla::widget::winrt::FrameworkView FrameworkView; + typedef mozilla::layers::FrameMetrics FrameMetrics; static LRESULT CALLBACK StaticWindowProcedure(HWND aWnd, UINT aMsg, WPARAM aWParan, LPARAM aLParam); @@ -65,6 +67,7 @@ public: virtual ~MetroWidget(); NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIOBSERVER static HWND GetICoreWindowHWND() { return sICoreHwnd; } @@ -194,11 +197,11 @@ public: void RequestContentRepaintImplMainThread(); // GeckoContentController interface impl - virtual void RequestContentRepaint(const mozilla::layers::FrameMetrics& aFrameMetrics); + virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics); virtual void HandleDoubleTap(const mozilla::CSSIntPoint& aPoint); virtual void HandleSingleTap(const mozilla::CSSIntPoint& aPoint); virtual void HandleLongTap(const mozilla::CSSIntPoint& aPoint); - virtual void SendAsyncScrollDOMEvent(mozilla::layers::FrameMetrics::ViewID aScrollId, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize); + virtual void SendAsyncScrollDOMEvent(FrameMetrics::ViewID aScrollId, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize); virtual void PostDelayedTask(Task* aTask, int aDelayMs); virtual void HandlePanBegin(); virtual void HandlePanEnd(); @@ -243,6 +246,7 @@ protected: bool mTempBasicLayerInUse; Microsoft::WRL::ComPtr mMetroInput; mozilla::layers::FrameMetrics mFrameMetrics; + uint64_t mRootLayerTreeId; public: static nsRefPtr sAPZC; From fe4f40489808f9e4d7651c785dca2eb2915d6411 Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Thu, 15 Aug 2013 11:30:27 -0400 Subject: [PATCH 10/54] Bug 898580 - Remove updating of scroll offset in AsyncPanZoomController::NotifyLayersUpdated. It is now no longer needed by any platform. r=bbondy, r=kats --- gfx/layers/ipc/AsyncPanZoomController.cpp | 24 ----------------------- 1 file changed, 24 deletions(-) diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp index c5d590771ad..e6475d8f91c 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -1146,30 +1146,6 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri bool isDefault = mFrameMetrics.IsDefault(); mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners; - // TODO: Once a mechanism for calling UpdateScrollOffset() when content does - // a scrollTo() is implemented for metro (bug 898580), this block can be removed. -#ifdef MOZ_METRO - if (!mPaintThrottler.IsOutstanding()) { - // No paint was requested, but we got one anyways. One possible cause of this - // is that content could have fired a scrollTo(). In this case, we should take - // the new scroll offset. Document/viewport changes are handled elsewhere. - // Also note that, since NotifyLayersUpdated() is called whenever there's a - // layers update, we didn't necessarily get a new scroll offset, but we're - // updating our local copy of it anyways just in case. - switch (mState) { - case NOTHING: - case FLING: - case TOUCHING: - case WAITING_LISTENERS: - mFrameMetrics.mScrollOffset = aLayerMetrics.mScrollOffset; - break; - // Don't clobber if we're in other states. - default: - break; - } - } -#endif - mPaintThrottler.TaskComplete(GetFrameTime()); bool needContentRepaint = false; if (aLayerMetrics.mCompositionBounds.width == mFrameMetrics.mCompositionBounds.width && From d9e550ecf05283f46bc9417ebd6bfc70c7274e61 Mon Sep 17 00:00:00 2001 From: Mihnea Dobrescu-Balaur Date: Tue, 20 Aug 2013 16:07:33 -0700 Subject: [PATCH 11/54] Bug 905782 - Make the XPCShell harness treat running a single file as a sequential run. r=ted --- testing/xpcshell/mach_commands.py | 2 +- testing/xpcshell/runxpcshelltests.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/testing/xpcshell/mach_commands.py b/testing/xpcshell/mach_commands.py index 11c214640cf..1f38190ff13 100644 --- a/testing/xpcshell/mach_commands.py +++ b/testing/xpcshell/mach_commands.py @@ -160,7 +160,7 @@ class XPCShellRunner(MozbuildObject): self.log_manager.disable_unstructured() - if not result and not sequential: + if not result and not xpcshell.sequential: print("Tests were run in parallel. Try running with --sequential " "to make sure the failures were not caused by this.") return int(not result) diff --git a/testing/xpcshell/runxpcshelltests.py b/testing/xpcshell/runxpcshelltests.py index 3abd61a0653..a7edcc5aff4 100644 --- a/testing/xpcshell/runxpcshelltests.py +++ b/testing/xpcshell/runxpcshelltests.py @@ -1122,6 +1122,8 @@ class XPCShellTests(object): pStdout, pStderr = self.getPipes() self.buildTestList() + if self.singleFile: + self.sequential = True if shuffle: random.shuffle(self.alltests) @@ -1185,7 +1187,7 @@ class XPCShellTests(object): else: tests_queue.append(test) - if sequential: + if self.sequential: self.log.info("INFO | Running tests sequentially.") else: self.log.info("INFO | Using at most %d threads." % NUM_THREADS) From 9e5556bcfde4f3d60a1e39bb0f24bd0bf192e218 Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Tue, 20 Aug 2013 11:03:24 +0200 Subject: [PATCH 12/54] Bug 666399 - New Harmony syntax for generators. r=Waldo Add a GeneratorKind enumeration, and use it in the parser and runtime to indicate whether a function is a non-generator, a legacy generator, or a star generator. Always parse "yield" as TOK_YIELD, regardless of the version. Add TokenStream::currentName() to retrieve the current token's name, which works for TOK_NAME or TOK_YIELD. The parser should check the validity of "yield" as a name, if needed, using checkYieldNameValidity(). Parse "function*" as the StarGenerator GeneratorKind, and allow star generators to be lazily parsed. Separate parsing of return statements from yield expressions. --- js/src/frontend/BytecodeCompiler.cpp | 11 +- js/src/frontend/ParseNode.cpp | 6 +- js/src/frontend/Parser.cpp | 441 ++++++++++++------ js/src/frontend/Parser.h | 36 +- js/src/frontend/SharedContext.h | 28 +- js/src/frontend/TokenStream.cpp | 8 +- js/src/frontend/TokenStream.h | 7 + .../jit-test/tests/generators/es6-syntax.js | 38 ++ js/src/jit/AsmJS.cpp | 2 +- js/src/js.msg | 1 + js/src/jsanalyze.cpp | 2 +- js/src/jsatom.cpp | 1 - js/src/jsatom.h | 1 - js/src/jsfun.cpp | 17 +- js/src/jsfun.h | 12 + js/src/jsiter.cpp | 7 + js/src/jsreflect.cpp | 1 + js/src/jsscript.cpp | 26 +- js/src/jsscript.h | 66 ++- js/src/tests/ecma_6/Generators/shell.js | 0 js/src/tests/ecma_6/Generators/syntax.js | 97 ++++ js/src/vm/CommonPropertyNames.h | 1 + js/src/vm/Keywords.h | 8 +- 23 files changed, 605 insertions(+), 212 deletions(-) create mode 100644 js/src/jit-test/tests/generators/es6-syntax.js create mode 100644 js/src/tests/ecma_6/Generators/shell.js create mode 100644 js/src/tests/ecma_6/Generators/syntax.js diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 5b78aab371e..27ffbb04406 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -69,7 +69,7 @@ CheckArgumentsWithinEval(JSContext *cx, Parser &parser, Handle } // It's an error to use |arguments| in a legacy generator expression. - if (script->isGeneratorExp && script->isLegacyGenerator) { + if (script->isGeneratorExp && script->isLegacyGenerator()) { parser.report(ParseError, false, NULL, JSMSG_BAD_GENEXP_BODY, js_arguments_str); return false; } @@ -294,7 +294,8 @@ frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject sco */ JSFunction *fun = evalCaller->functionOrCallerFunction(); Directives directives(/* strict = */ fun->strict()); - ObjectBox *funbox = parser.newFunctionBox(/* fn = */ NULL, fun, pc.addr(), directives); + ObjectBox *funbox = parser.newFunctionBox(/* fn = */ NULL, fun, pc.addr(), + directives, fun->generatorKind()); if (!funbox) return NULL; bce.objectList.add(funbox); @@ -414,7 +415,9 @@ frontend::CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *cha uint32_t staticLevel = lazy->staticLevel(cx); Rooted fun(cx, lazy->function()); - ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict()); + JS_ASSERT(!lazy->isLegacyGenerator()); + ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict(), + lazy->generatorKind()); if (!pn) return false; @@ -518,7 +521,7 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO ParseNode *fn; while (true) { Directives newDirectives = directives; - fn = parser.standaloneFunctionBody(fun, formals, directives, &newDirectives); + fn = parser.standaloneFunctionBody(fun, formals, NotGenerator, directives, &newDirectives); if (fn) break; diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 59a9dc37dd5..55a2c172aa7 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -384,9 +384,9 @@ Parser::cloneParseTree(ParseNode *opn) if (pn->getKind() == PNK_MODULE) { MOZ_ASSUME_UNREACHABLE("module nodes cannot be cloned"); } - NULLCHECK(pn->pn_funbox = - newFunctionBox(pn, opn->pn_funbox->function(), pc, - Directives(/* strict = */ opn->pn_funbox->strict))); + NULLCHECK(pn->pn_funbox = newFunctionBox(pn, opn->pn_funbox->function(), pc, + Directives(/* strict = */ opn->pn_funbox->strict), + opn->pn_funbox->generatorKind())); NULLCHECK(pn->pn_body = cloneParseTree(opn->pn_body)); pn->pn_cookie = opn->pn_cookie; pn->pn_dflags = opn->pn_dflags; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 35dba78666c..856249dbcff 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -56,7 +56,6 @@ namespace frontend { typedef Rooted RootedStaticBlockObject; typedef Handle HandleStaticBlockObject; -typedef MutableHandle MutableHandlePropertyName; /* * Insist that the next token be of type tt, or report errno and return null. @@ -466,13 +465,14 @@ Parser::newObjectBox(JSObject *obj) template FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun, ParseContext *outerpc, Directives directives, - bool extraWarnings) + bool extraWarnings, GeneratorKind generatorKind) : ObjectBox(fun, traceListHead), SharedContext(cx, directives, extraWarnings), bindings(), bufStart(0), bufEnd(0), ndefaults(0), + generatorKindBits_(GeneratorKindAsBits(generatorKind)), inWith(false), // initialized below inGenexpLambda(false), hasDestructuringArgs(false), @@ -533,7 +533,7 @@ FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunct template FunctionBox * Parser::newFunctionBox(Node fn, JSFunction *fun, ParseContext *outerpc, - Directives inheritedDirectives) + Directives inheritedDirectives, GeneratorKind generatorKind) { JS_ASSERT(fun && !IsPoisonedPtr(fun)); @@ -546,7 +546,8 @@ Parser::newFunctionBox(Node fn, JSFunction *fun, ParseContext(context, traceListHead, fun, outerpc, - inheritedDirectives, options().extraWarningsOption); + inheritedDirectives, options().extraWarningsOption, + generatorKind); if (!funbox) { js_ReportOutOfMemory(context); return NULL; @@ -863,6 +864,7 @@ Parser::checkStrictBinding(PropertyName *name, Node pn) template <> ParseNode * Parser::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals, + GeneratorKind generatorKind, Directives inheritedDirectives, Directives *newDirectives) { @@ -877,7 +879,8 @@ Parser::standaloneFunctionBody(HandleFunction fun, const AutoN argsbody->makeEmpty(); fn->pn_body = argsbody; - FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ NULL, inheritedDirectives); + FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ NULL, inheritedDirectives, + generatorKind); if (!funbox) return null(); handler.setFunctionBox(fn, funbox); @@ -1062,7 +1065,9 @@ Parser::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ JS_ASSERT(pc->sc->isFunctionBox()); JS_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid); +#ifdef DEBUG uint32_t startYieldOffset = pc->lastYieldOffset; +#endif Node pn; if (type == StatementListBody) { @@ -1082,8 +1087,14 @@ Parser::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ return null(); } - if (pc->lastYieldOffset != startYieldOffset) { - JS_ASSERT(pc->isLegacyGenerator()); + switch (pc->generatorKind()) { + case NotGenerator: + JS_ASSERT(pc->lastYieldOffset == startYieldOffset); + break; + + case LegacyGenerator: + // FIXME: Catch these errors eagerly, in yieldExpression(). + JS_ASSERT(pc->lastYieldOffset != startYieldOffset); if (kind == Arrow) { reportWithOffset(ParseError, false, pc->lastYieldOffset, JSMSG_YIELD_IN_ARROW, js_yield_str); @@ -1095,9 +1106,12 @@ Parser::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ JSMSG_BAD_ANON_GENERATOR_RETURN); return null(); } - pc->sc->asFunctionBox()->setIsLegacyGenerator(); - } else { - JS_ASSERT(!pc->isLegacyGenerator()); + break; + + case StarGenerator: + JS_ASSERT(kind != Arrow); + JS_ASSERT(type == StatementListBody); + break; } /* Check for falling off the end of a function that returns a value. */ @@ -1627,6 +1641,11 @@ Parser::functionArguments(FunctionSyntaxKind kind, Node *listp, No } #endif /* JS_HAS_DESTRUCTURING */ + case TOK_YIELD: + if (!checkYieldNameValidity(JSMSG_MISSING_FORMAL)) + return false; + goto TOK_NAME; + case TOK_TRIPLEDOT: { hasRest = true; @@ -1636,15 +1655,16 @@ Parser::functionArguments(FunctionSyntaxKind kind, Node *listp, No report(ParseError, false, null(), JSMSG_NO_REST_NAME); return false; } - /* Fall through */ + goto TOK_NAME; } + TOK_NAME: case TOK_NAME: { if (parenFreeArrow) funbox->setStart(tokenStream); - RootedPropertyName name(context, tokenStream.currentToken().name()); + RootedPropertyName name(context, tokenStream.currentName()); bool disallowDuplicateArgs = funbox->hasDestructuringArgs || hasDefaults; if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg)) return false; @@ -1695,6 +1715,20 @@ Parser::functionArguments(FunctionSyntaxKind kind, Node *listp, No return true; } +template +bool +Parser::checkFunctionName(HandlePropertyName funName) +{ + if (pc->isStarGenerator() && funName == context->names().yield) { + // The name of a named function expression is specified to be bound in + // the outer context as if via "let". In an ES6 generator, "yield" is + // not a valid name for a let-bound variable. + report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield"); + return false; + } + return true; +} + template <> bool Parser::checkFunctionDefinition(HandlePropertyName funName, @@ -1707,6 +1741,9 @@ Parser::checkFunctionDefinition(HandlePropertyName funName, /* Function statements add a binding to the enclosing scope. */ bool bodyLevel = pc->atBodyLevel(); + if (!checkFunctionName(funName)) + return false; + if (kind == Statement) { /* * Handle redeclaration and optimize cases where we can statically bind the @@ -1820,7 +1857,9 @@ Parser::checkFunctionDefinition(HandlePropertyName funName, // so we can skip over them after accounting for their free variables. if (LazyScript *lazyOuter = handler.lazyOuterFunction()) { JSFunction *fun = handler.nextLazyInnerFunction(); - FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false)); + JS_ASSERT(!fun->isLegacyGenerator()); + FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false), + fun->generatorKind()); if (!funbox) return false; @@ -1896,6 +1935,9 @@ Parser::checkFunctionDefinition(HandlePropertyName funName, /* Function statements add a binding to the enclosing scope. */ bool bodyLevel = pc->atBodyLevel(); + if (!checkFunctionName(funName)) + return false; + if (kind == Statement) { /* * Handle redeclaration and optimize cases where we can statically bind the @@ -1934,7 +1976,8 @@ Parser::checkFunctionDefinition(HandlePropertyName funName, template typename ParseHandler::Node Parser::functionDef(HandlePropertyName funName, const TokenStream::Position &start, - FunctionType type, FunctionSyntaxKind kind) + FunctionType type, FunctionSyntaxKind kind, + GeneratorKind generatorKind) { JS_ASSERT_IF(kind == Statement, funName); @@ -1962,7 +2005,7 @@ Parser::functionDef(HandlePropertyName funName, const TokenStream: Directives newDirectives = directives; while (true) { - if (functionArgsAndBody(pn, fun, type, kind, directives, &newDirectives)) + if (functionArgsAndBody(pn, fun, type, kind, generatorKind, directives, &newDirectives)) break; if (tokenStream.hadError() || directives == newDirectives) return null(); @@ -2066,6 +2109,7 @@ Parser::finishFunctionDefinition(Node pn, FunctionBox *funbo if (pc->sc->strict) lazy->setStrict(); + lazy->setGeneratorKind(funbox->generatorKind()); if (funbox->usesArguments && funbox->usesApply) lazy->setUsesArgumentsAndApply(); PropagateTransitiveParseFlags(funbox, lazy); @@ -2078,13 +2122,14 @@ template <> bool Parser::functionArgsAndBody(ParseNode *pn, HandleFunction fun, FunctionType type, FunctionSyntaxKind kind, + GeneratorKind generatorKind, Directives inheritedDirectives, Directives *newDirectives) { ParseContext *outerpc = pc; // Create box for fun->object early to protect against last-ditch GC. - FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives); + FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind); if (!funbox) return false; @@ -2160,13 +2205,14 @@ template <> bool Parser::functionArgsAndBody(Node pn, HandleFunction fun, FunctionType type, FunctionSyntaxKind kind, + GeneratorKind generatorKind, Directives inheritedDirectives, Directives *newDirectives) { ParseContext *outerpc = pc; // Create box for fun->object early to protect against last-ditch GC. - FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives); + FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind); if (!funbox) return false; @@ -2192,14 +2238,15 @@ Parser::functionArgsAndBody(Node pn, HandleFunction fun, template <> ParseNode * Parser::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, - bool strict) + bool strict, GeneratorKind generatorKind) { Node pn = handler.newFunctionDefinition(); if (!pn) return null(); Directives directives(/* strict = */ strict); - FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ NULL, directives); + FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ NULL, directives, + generatorKind); if (!funbox) return null(); @@ -2273,6 +2320,10 @@ Parser::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, Fu // Parse the function body. FunctionBodyType bodyType = StatementListBody; if (tokenStream.getToken(TokenStream::Operand) != TOK_LC) { + if (funbox->isStarGenerator()) { + report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY); + return false; + } tokenStream.ungetToken(); bodyType = ExpressionBody; fun->setIsExprClosure(); @@ -2310,7 +2361,7 @@ template <> ParseNode * Parser::moduleDecl() { - JS_ASSERT(tokenStream.currentToken().name() == context->names().module); + JS_ASSERT(tokenStream.currentName() == context->names().module); if (!((pc->sc->isGlobalSharedContext() || pc->sc->isModuleBox()) && pc->atBodyLevel())) { report(ParseError, false, NULL, JSMSG_MODULE_STATEMENT); @@ -2352,18 +2403,44 @@ Parser::moduleDecl() return SyntaxParseHandler::NodeFailure; } +template +bool +Parser::checkYieldNameValidity(unsigned errorNumber) +{ + // In star generators and in JS >= 1.7, yield is a keyword. + if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7) { + report(ParseError, false, null(), errorNumber); + return false; + } + // Otherwise in strict mode, yield is a future reserved word. + if (pc->sc->strict) { + report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield"); + return false; + } + return true; +} + template typename ParseHandler::Node Parser::functionStmt() { - JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION); + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); TokenStream::Position start(keepAtoms); tokenStream.tell(&start); RootedPropertyName name(context); - if (tokenStream.getToken(TokenStream::KeywordIsName) == TOK_NAME) { - name = tokenStream.currentToken().name(); + GeneratorKind generatorKind = NotGenerator; + TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName); + + if (tt == TOK_MUL) { + tokenStream.tell(&start); + tt = tokenStream.getToken(TokenStream::KeywordIsName); + generatorKind = StarGenerator; + } + + if (tt == TOK_NAME) { + name = tokenStream.currentName(); } else { /* Unnamed function expressions are forbidden in statement context. */ report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT); @@ -2375,22 +2452,34 @@ Parser::functionStmt() !report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT)) return null(); - return functionDef(name, start, Normal, Statement); + return functionDef(name, start, Normal, Statement, generatorKind); } template typename ParseHandler::Node Parser::functionExpr() { - RootedPropertyName name(context); - JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION); + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); + TokenStream::Position start(keepAtoms); tokenStream.tell(&start); - if (tokenStream.getToken(TokenStream::KeywordIsName) == TOK_NAME) - name = tokenStream.currentToken().name(); + + RootedPropertyName name(context); + GeneratorKind generatorKind = NotGenerator; + TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName); + + if (tt == TOK_MUL) { + tokenStream.tell(&start); + tt = tokenStream.getToken(TokenStream::KeywordIsName); + generatorKind = StarGenerator; + } + + if (tt == TOK_NAME) + name = tokenStream.currentName(); else tokenStream.ungetToken(); - return functionDef(name, start, Normal, Expression); + + return functionDef(name, start, Normal, Expression, generatorKind); } /* @@ -2524,7 +2613,7 @@ Parser::maybeParseDirective(Node list, Node pn, bool *cont) } } } else if (directive == context->names().useAsm) { - if (pc->sc->isFunctionBox()) + if (pc->sc->isFunctionBox() && !pc->isGenerator()) return asmJS(list); return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL); } @@ -2606,15 +2695,21 @@ Parser::condition() return pn; } -static bool -MatchLabel(TokenStream &ts, MutableHandlePropertyName label) +template +bool +Parser::matchLabel(MutableHandle label) { - TokenKind tt = ts.peekTokenSameLine(TokenStream::Operand); + TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand); if (tt == TOK_ERROR) return false; if (tt == TOK_NAME) { - (void) ts.getToken(); - label.set(ts.currentToken().name()); + tokenStream.consumeKnownToken(TOK_NAME); + label.set(tokenStream.currentName()); + } else if (tt == TOK_YIELD) { + tokenStream.consumeKnownToken(TOK_YIELD); + if (!checkYieldNameValidity()) + return false; + label.set(tokenStream.currentName()); } else { label.set(NULL); } @@ -3240,7 +3335,7 @@ template typename ParseHandler::Node Parser::letBlock(LetContext letContext) { - JS_ASSERT(tokenStream.currentToken().type == TOK_LET); + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LET)); RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context)); if (!blockObj) @@ -3330,7 +3425,7 @@ template typename ParseHandler::Node Parser::blockStatement() { - JS_ASSERT(tokenStream.currentToken().type == TOK_LC); + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); StmtInfoPC stmtInfo(context); if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, pc)) @@ -3372,7 +3467,6 @@ Parser::newBindingNode(PropertyName *name, bool functionScope, Var } /* Make a new node for this declarator name (or destructuring pattern). */ - JS_ASSERT(tokenStream.currentToken().type == TOK_NAME); return newName(name); } @@ -3461,12 +3555,17 @@ Parser::variables(ParseNodeKind kind, bool *psimple, #endif /* JS_HAS_DESTRUCTURING */ if (tt != TOK_NAME) { - if (tt != TOK_ERROR) - report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME); - return null(); + if (tt == TOK_YIELD) { + if (!checkYieldNameValidity(JSMSG_NO_VARIABLE_NAME)) + return null(); + } else { + if (tt != TOK_ERROR) + report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME); + return null(); + } } - RootedPropertyName name(context, tokenStream.currentToken().name()); + RootedPropertyName name(context, tokenStream.currentName()); pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext); if (!pn2) return null(); @@ -4123,9 +4222,14 @@ Parser::forStatement() PushStatementPC(pc, &forStmt, STMT_FOR_LOOP); /* Don't parse 'for each' loops. */ - if (allowsForEachIn() && tokenStream.peekToken() == TOK_NAME) { - JS_ALWAYS_FALSE(abortIfSyntaxParser()); - return null(); + if (allowsForEachIn()) { + TokenKind tt = tokenStream.peekToken(); + // Not all "yield" tokens are names, but the ones that aren't names are + // invalid in this context anyway. + if (tt == TOK_NAME || tt == TOK_YIELD) { + JS_ALWAYS_FALSE(abortIfSyntaxParser()); + return null(); + } } MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); @@ -4327,7 +4431,7 @@ Parser::continueStatement() uint32_t begin = pos().begin; RootedPropertyName label(context); - if (!MatchLabel(tokenStream, &label)) + if (!matchLabel(&label)) return null(); StmtInfoPC *stmt = pc->topStmt; @@ -4374,7 +4478,7 @@ Parser::breakStatement() uint32_t begin = pos().begin; RootedPropertyName label(context); - if (!MatchLabel(tokenStream, &label)) + if (!matchLabel(&label)) return null(); StmtInfoPC *stmt = pc->topStmt; if (label) { @@ -4405,98 +4509,45 @@ Parser::breakStatement() template typename ParseHandler::Node -Parser::returnStatementOrYieldExpression() +Parser::returnStatement() { - JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN) || - tokenStream.isCurrentTokenType(TOK_YIELD)); - bool isYield = tokenStream.isCurrentTokenType(TOK_YIELD); + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN)); uint32_t begin = pos().begin; if (!pc->sc->isFunctionBox()) { - report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, - isYield ? js_yield_str : js_return_str); + report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str); return null(); } - // Legacy generators are identified by the presence of "yield" in their - // bodies. We only see "yield" as TOK_YIELD in JS 1.7+. - if (isYield) { - JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7); - if (!abortIfSyntaxParser()) - return null(); - - if (pc->isLegacyGenerator()) { - // We are in a legacy generator: a function that has already seen a - // yield. - JS_ASSERT(pc->sc->isFunctionBox()); - JS_ASSERT(pc->lastYieldOffset != ParseContext::NoYieldOffset); - } else { - // We are in a code that has not seen a yield, and in JS 1.8 so - // "yield" parsed as TOK_YIELD. Try to transition to being a legacy - // generator. - JS_ASSERT(tokenStream.currentToken().type == TOK_YIELD); - JS_ASSERT(pc->lastYieldOffset == ParseContext::NoYieldOffset); - - if (!pc->sc->isFunctionBox()) { - report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str); - return null(); - } - - pc->generatorParseMode = ParseContext::LegacyGenerator; - } - - pc->lastYieldOffset = begin; - } - // Parse an optional operand. // - // Checking whether yield has an operand is especially wonky since - // there is not a mandatory semicolon. - // - // ES6 does not permit yield without an operand. We will have to sunset - // this extension in order to conform to the ES6 syntax, which treats - // "yield \n expr;" as a single ExpressionStatement. + // This is ugly, but we don't want to require a semicolon. Node exprNode; - TokenKind next = tokenStream.peekTokenSameLine(TokenStream::Operand); - if (next == TOK_ERROR) + switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) { + case TOK_ERROR: return null(); - if (next == TOK_EOF || next == TOK_EOL || next == TOK_SEMI || next == TOK_RC || - (isYield && (next == TOK_RB || next == TOK_RP || next == TOK_COLON || next == TOK_COMMA))) - { - if (isYield) { - if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND)) - return null(); - } - + case TOK_EOF: + case TOK_EOL: + case TOK_SEMI: + case TOK_RC: exprNode = null(); - if (!isYield) - pc->funHasReturnVoid = true; - } else { - exprNode = isYield ? assignExpr() : expr(); + pc->funHasReturnVoid = true; + break; + default: { + exprNode = expr(); if (!exprNode) return null(); - if (!isYield) - pc->funHasReturnExpr = true; + pc->funHasReturnExpr = true; + } } - if (!isYield) { - if (!MatchOrInsertSemicolon(tokenStream)) - return null(); - } + if (!MatchOrInsertSemicolon(tokenStream)) + return null(); - Node pn = isYield - ? handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode) - : handler.newReturnStatement(exprNode, TokenPos(begin, pos().end)); + Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end)); if (!pn) return null(); - if (pc->funHasReturnExpr && pc->isLegacyGenerator()) { - /* As in Python (see PEP-255), disallow return v; in generators. */ - reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN, - JSMSG_BAD_ANON_GENERATOR_RETURN); - return null(); - } - if (options().extraWarningsOption && pc->funHasReturnExpr && pc->funHasReturnVoid && !reportBadReturn(pn, ParseExtraWarning, JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE)) @@ -4504,9 +4555,108 @@ Parser::returnStatementOrYieldExpression() return null(); } + if (pc->isLegacyGenerator() && exprNode) { + /* Disallow "return v;" in legacy generators. */ + reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN, + JSMSG_BAD_ANON_GENERATOR_RETURN); + return null(); + } + return pn; } +template +typename ParseHandler::Node +Parser::yieldExpression() +{ + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD)); + uint32_t begin = pos().begin; + + switch (pc->generatorKind()) { + case StarGenerator: + { + JS_ASSERT(pc->sc->isFunctionBox()); + + pc->lastYieldOffset = begin; + + bool isDelegatingYield = tokenStream.matchToken(TOK_MUL); + + // ES6 generators require a value. + Node exprNode = assignExpr(); + if (!exprNode) + return null(); + + // FIXME: Plumb isDelegatingYield appropriately. + (void) isDelegatingYield; + return handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode); + } + + case NotGenerator: + // We are in code that has not seen a yield, but we are in JS 1.7 or + // later. Try to transition to being a legacy generator. + JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7); + JS_ASSERT(pc->lastYieldOffset == ParseContext::NoYieldOffset); + + if (!abortIfSyntaxParser()) + return null(); + + if (!pc->sc->isFunctionBox()) { + report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str); + return null(); + } + + pc->sc->asFunctionBox()->setGeneratorKind(LegacyGenerator); + + if (pc->funHasReturnExpr) { + /* As in Python (see PEP-255), disallow return v; in generators. */ + reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN, + JSMSG_BAD_ANON_GENERATOR_RETURN); + return null(); + } + // Fall through. + + case LegacyGenerator: + { + // We are in a legacy generator: a function that has already seen a + // yield, or in a legacy generator comprehension. + JS_ASSERT(pc->sc->isFunctionBox()); + + pc->lastYieldOffset = begin; + + // Legacy generators do not require a value. + Node exprNode; + switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) { + case TOK_ERROR: + return null(); + case TOK_EOF: + case TOK_EOL: + case TOK_SEMI: + case TOK_RC: + case TOK_RB: + case TOK_RP: + case TOK_COLON: + case TOK_COMMA: + // No value. + exprNode = null(); + // ES6 does not permit yield without an operand. We should + // encourage users of yield expressions of this kind to pass an + // operand, to bring users closer to standard syntax. + if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND)) + return null(); + break; + default: + exprNode = assignExpr(); + if (!exprNode) + return null(); + } + + return handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode); + } + } + + MOZ_ASSUME_UNREACHABLE("yieldExpr"); +} + template <> ParseNode * Parser::withStatement() @@ -4576,7 +4726,7 @@ typename ParseHandler::Node Parser::labeledStatement() { uint32_t begin = pos().begin; - RootedPropertyName label(context, tokenStream.currentToken().name()); + RootedPropertyName label(context, tokenStream.currentName()); for (StmtInfoPC *stmt = pc->topStmt; stmt; stmt = stmt->down) { if (stmt->type == STMT_LABEL && stmt->label == label) { report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL); @@ -4717,9 +4867,13 @@ Parser::tryStatement() break; #endif + case TOK_YIELD: + if (!checkYieldNameValidity(JSMSG_CATCH_IDENTIFIER)) + return null(); + // Fall through. case TOK_NAME: { - RootedPropertyName label(context, tokenStream.currentToken().name()); + RootedPropertyName label(context, tokenStream.currentName()); catchName = newBindingNode(label, false); if (!catchName) return null(); @@ -4855,7 +5009,7 @@ Parser::statement(bool canHaveDirectives) case TOK_BREAK: return breakStatement(); case TOK_RETURN: - return returnStatementOrYieldExpression(); + return returnStatement(); case TOK_WITH: return withStatement(); case TOK_THROW: @@ -4886,10 +5040,18 @@ Parser::statement(bool canHaveDirectives) } return expressionStatement(); + case TOK_YIELD: + if (tokenStream.peekToken() == TOK_COLON) { + if (!checkYieldNameValidity()) + return null(); + return labeledStatement(); + } + return expressionStatement(); + case TOK_NAME: if (tokenStream.peekToken() == TOK_COLON) return labeledStatement(); - if (tokenStream.currentToken().name() == context->names().module + if (tokenStream.currentName() == context->names().module && tokenStream.peekTokenSameLine() == TOK_STRING) { return moduleDecl(); @@ -5202,8 +5364,8 @@ Parser::assignExpr() if (tt == TOK_STRING && tokenStream.nextTokenEndsExpr()) return stringLiteral(); - if (tt == TOK_YIELD) - return returnStatementOrYieldExpression(); + if (tt == TOK_YIELD && (versionNumber() >= JSVERSION_1_7 || pc->isGenerator())) + return yieldExpression(); tokenStream.ungetToken(); @@ -5241,7 +5403,7 @@ Parser::assignExpr() return null(); tokenStream.ungetToken(); - return functionDef(NullPtr(), start, Normal, Arrow); + return functionDef(NullPtr(), start, Normal, Arrow, NotGenerator); } default: @@ -5662,7 +5824,7 @@ Parser::comprehensionTail(ParseNode *kid, unsigned blockid, bo BindData data(context); TokenKind tt; - JS_ASSERT(tokenStream.currentToken().type == TOK_FOR); + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR)); if (kind == PNK_SEMI) { /* @@ -5747,7 +5909,7 @@ Parser::comprehensionTail(ParseNode *kid, unsigned blockid, bo #endif case TOK_NAME: - name = tokenStream.currentToken().name(); + name = tokenStream.currentName(); /* * Create a name node with pn_op JSOP_NAME. We can't set pn_op to @@ -5955,7 +6117,8 @@ Parser::generatorExpr(ParseNode *kid) /* Create box for fun->object early to protect against last-ditch GC. */ Directives directives(/* strict = */ outerpc->sc->strict); - FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives); + FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives, + LegacyGenerator); if (!genFunbox) return null(); @@ -5975,7 +6138,7 @@ Parser::generatorExpr(ParseNode *kid) if (outerpc->sc->isFunctionBox()) genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags; - genFunbox->setIsLegacyGenerator(); + JS_ASSERT(genFunbox->isLegacyGenerator()); genFunbox->inGenexpLambda = true; genfn->pn_blockid = genpc.bodyid; @@ -6116,7 +6279,7 @@ Parser::memberExpr(TokenKind tt, bool allowCallSyntax) if (tt == TOK_ERROR) return null(); if (tt == TOK_NAME) { - PropertyName *field = tokenStream.currentToken().name(); + PropertyName *field = tokenStream.currentName(); nextMember = handler.newPropertyAccess(lhs, field, pos().end); if (!nextMember) return null(); @@ -6191,9 +6354,7 @@ template typename ParseHandler::Node Parser::identifierName() { - JS_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME)); - - RootedPropertyName name(context, tokenStream.currentToken().name()); + RootedPropertyName name(context, tokenStream.currentName()); Node pn = newName(name); if (!pn) return null(); @@ -6395,7 +6556,7 @@ Parser::objectLiteral() break; case TOK_NAME: { - atom = tokenStream.currentToken().name(); + atom = tokenStream.currentName(); if (atom == context->names().get) { op = JSOP_INITPROP_GETTER; } else if (atom == context->names().set) { @@ -6411,7 +6572,7 @@ Parser::objectLiteral() // name next. TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName); if (tt == TOK_NAME) { - atom = tokenStream.currentToken().name(); + atom = tokenStream.currentName(); propname = newName(atom->asPropertyName()); if (!propname) return null(); @@ -6527,7 +6688,7 @@ Parser::objectLiteral() TokenStream::Position start(keepAtoms); tokenStream.tell(&start); Node accessor = functionDef(funName, start, op == JSOP_INITPROP_GETTER ? Getter : Setter, - Expression); + Expression, NotGenerator); if (!accessor) return null(); if (!handler.addAccessorPropertyDefinition(literal, propname, accessor, op)) @@ -6628,6 +6789,10 @@ Parser::primaryExpr(TokenKind tt) case TOK_STRING: return stringLiteral(); + case TOK_YIELD: + if (!checkYieldNameValidity()) + return null(); + // Fall through. case TOK_NAME: return identifierName(); @@ -6689,7 +6854,7 @@ template typename ParseHandler::Node Parser::parenExpr(bool *genexp) { - JS_ASSERT(tokenStream.currentToken().type == TOK_LP); + JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP)); uint32_t begin = pos().begin; if (genexp) diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 726eaac8cd3..9b933f7377b 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -104,18 +104,20 @@ struct ParseContext : public GenericParseContext const unsigned staticLevel; /* static compilation unit nesting level */ - // Functions start off being parsed as NotGenerator. - // NotGenerator transitions to LegacyGenerator on parsing "yield" in JS 1.7. - enum GeneratorParseMode { NotGenerator, LegacyGenerator }; - GeneratorParseMode generatorParseMode; - // lastYieldOffset stores the offset of the last yield that was parsed. // NoYieldOffset is its initial value. static const uint32_t NoYieldOffset = UINT32_MAX; uint32_t lastYieldOffset; - bool isGenerator() const { return generatorParseMode != NotGenerator; } - bool isLegacyGenerator() const { return generatorParseMode == LegacyGenerator; } + // Most functions start off being parsed as non-generators. + // Non-generators transition to LegacyGenerator on parsing "yield" in JS 1.7. + // An ES6 generator is marked as a "star generator" before its body is parsed. + GeneratorKind generatorKind() const { + return sc->isFunctionBox() ? sc->asFunctionBox()->generatorKind() : NotGenerator; + } + bool isGenerator() const { return generatorKind() != NotGenerator; } + bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; } + bool isStarGenerator() const { return generatorKind() == StarGenerator; } Node blockNode; /* parse node for a block with let declarations (block with its own lexical scope) */ @@ -249,7 +251,6 @@ struct ParseContext : public GenericParseContext blockChain(prs->context), maybeFunction(maybeFunction), staticLevel(staticLevel), - generatorParseMode(NotGenerator), lastYieldOffset(NoYieldOffset), blockNode(ParseHandler::null()), decls_(prs->context, prs->alloc), @@ -400,7 +401,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter ObjectBox *newObjectBox(JSObject *obj); ModuleBox *newModuleBox(Module *module, ParseContext *pc); FunctionBox *newFunctionBox(Node fn, JSFunction *fun, ParseContext *pc, - Directives directives); + Directives directives, GeneratorKind generatorKind); /* * Create a new function object given parse context (pc) and a name (which @@ -433,13 +434,16 @@ class Parser : private AutoGCRooter, public StrictModeGetter Node statement(bool canHaveDirectives = false); bool maybeParseDirective(Node list, Node pn, bool *cont); - // Parse a function, given only its body. Used for the Function constructor. + // Parse a function, given only its body. Used for the Function and + // Generator constructors. Node standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals, + GeneratorKind generatorKind, Directives inheritedDirectives, Directives *newDirectives); // Parse a function, given only its arguments and body. Used for lazily // parsed functions. - Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict); + Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict, + GeneratorKind generatorKind); /* * Parse a function body. Pass StatementListBody if the body is a list of @@ -487,7 +491,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter Node switchStatement(); Node continueStatement(); Node breakStatement(); - Node returnStatementOrYieldExpression(); + Node returnStatement(); Node withStatement(); Node labeledStatement(); Node throwStatement(); @@ -504,6 +508,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter Node expr(); Node assignExpr(); Node assignExprWithoutYield(unsigned err); + Node yieldExpression(); Node condExpr1(); Node orExpr1(); Node unaryExpr(); @@ -517,9 +522,10 @@ class Parser : private AutoGCRooter, public StrictModeGetter bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest); Node functionDef(HandlePropertyName name, const TokenStream::Position &start, - FunctionType type, FunctionSyntaxKind kind); + FunctionType type, FunctionSyntaxKind kind, GeneratorKind generatorKind); bool functionArgsAndBody(Node pn, HandleFunction fun, FunctionType type, FunctionSyntaxKind kind, + GeneratorKind generatorKind, Directives inheritedDirectives, Directives *newDirectives); Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin); @@ -536,6 +542,9 @@ class Parser : private AutoGCRooter, public StrictModeGetter Node identifierName(); + bool matchLabel(MutableHandle label); + bool checkYieldNameValidity(unsigned errorNumber = JSMSG_SYNTAX_ERROR); + bool allowsForEachIn() { #if !JS_HAS_FOR_EACH_IN return false; @@ -556,6 +565,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter bool checkFunctionArguments(); bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom); + bool checkFunctionName(HandlePropertyName funName); bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind, bool *pbodyProcessed); bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body); diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 4c01bd97bef..9e36f162933 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -72,10 +72,6 @@ class FunctionContextFlags // This class's data is all private and so only visible to these friends. friend class FunctionBox; - // We parsed a yield statement in the function, which can happen in JS1.7+ - // mode. - bool isLegacyGenerator:1; - // The function or a function that encloses it may define new local names // at runtime through means other than calling eval. bool mightAliasLocals:1; @@ -129,8 +125,7 @@ class FunctionContextFlags public: FunctionContextFlags() - : isLegacyGenerator(false), - mightAliasLocals(false), + : mightAliasLocals(false), hasExtensibleScope(false), needsDeclEnvObject(false), argumentsHasLocalBinding(false), @@ -264,6 +259,8 @@ class FunctionBox : public ObjectBox, public SharedContext uint32_t startLine; uint32_t startColumn; uint16_t ndefaults; + + uint8_t generatorKindBits_; /* The GeneratorKind of this function. */ bool inWith:1; /* some enclosing scope is a with-statement */ bool inGenexpLambda:1; /* lambda from generator expression */ bool hasDestructuringArgs:1; /* arguments list contains destructuring expression */ @@ -279,21 +276,30 @@ class FunctionBox : public ObjectBox, public SharedContext template FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun, ParseContext *pc, Directives directives, - bool extraWarnings); + bool extraWarnings, GeneratorKind generatorKind); ObjectBox *toObjectBox() { return this; } JSFunction *function() const { return &object->as(); } - // In the future, isGenerator will also return true for ES6 generators. - bool isGenerator() const { return isLegacyGenerator(); } - bool isLegacyGenerator() const { return funCxFlags.isLegacyGenerator; } + GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); } + bool isGenerator() const { return generatorKind() != NotGenerator; } + bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; } + bool isStarGenerator() const { return generatorKind() == StarGenerator; } + + void setGeneratorKind(GeneratorKind kind) { + // A generator kind can be set at initialization, or when "yield" is + // first seen. In both cases the transition can only happen from + // NotGenerator. + JS_ASSERT(!isGenerator()); + generatorKindBits_ = GeneratorKindAsBits(kind); + } + bool mightAliasLocals() const { return funCxFlags.mightAliasLocals; } bool hasExtensibleScope() const { return funCxFlags.hasExtensibleScope; } bool needsDeclEnvObject() const { return funCxFlags.needsDeclEnvObject; } bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; } bool definitelyNeedsArgsObj() const { return funCxFlags.definitelyNeedsArgsObj; } - void setIsLegacyGenerator() { funCxFlags.isLegacyGenerator = true; } void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; } void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; } void setNeedsDeclEnvObject() { funCxFlags.needsDeclEnvObject = true; } diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 104328a5903..07b29b0b654 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -937,10 +937,10 @@ TokenStream::checkForKeyword(const jschar *s, size_t length, TokenKind *ttp) return reportError(JSMSG_RESERVED_ID, kw->chars); } - // The keyword is not in this version. Treat it as an identifier, - // unless it is let or yield which we treat as TOK_STRICT_RESERVED by - // falling through to the code below (ES5 forbids them in strict mode). - if (kw->tokentype != TOK_LET && kw->tokentype != TOK_YIELD) + // The keyword is not in this version. Treat it as an identifier, unless + // it is let which we treat as TOK_STRICT_RESERVED by falling through to + // the code below (ES5 forbids it in strict mode). + if (kw->tokentype != TOK_LET) return true; } diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 48a1632a215..ab73b665254 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -407,6 +407,13 @@ class MOZ_STACK_CLASS TokenStream JSVersion versionNumber() const { return VersionNumber(options().version); } JSVersion versionWithFlags() const { return options().version; } + PropertyName *currentName() const { + if (isCurrentTokenType(TOK_YIELD)) + return cx->names().yield; + JS_ASSERT(isCurrentTokenType(TOK_NAME)); + return currentToken().name(); + } + bool isCurrentTokenAssignment() const { return TokenKindIsAssignment(currentToken().type); } diff --git a/js/src/jit-test/tests/generators/es6-syntax.js b/js/src/jit-test/tests/generators/es6-syntax.js new file mode 100644 index 00000000000..79b9109cead --- /dev/null +++ b/js/src/jit-test/tests/generators/es6-syntax.js @@ -0,0 +1,38 @@ +// Test interactions between ES6 generators and not-yet-standard +// features. + +function assertSyntaxError(str) { + var msg; + var evil = eval; + try { + // Non-direct eval. + evil(str); + } catch (exc) { + if (exc instanceof SyntaxError) + return; + msg = "Assertion failed: expected SyntaxError, got " + exc; + } + if (msg === undefined) + msg = "Assertion failed: expected SyntaxError, but no exception thrown"; + throw new Error(msg + " - " + str); +} + +// Destructuring binding. +assertSyntaxError("function* f(x = yield) {}"); +assertSyntaxError("function* f(x = yield 17) {}"); +assertSyntaxError("function* f([yield]) {}"); +assertSyntaxError("function* f({ yield }) {}"); +assertSyntaxError("function* f(...yield) {}"); + +// For each. +assertSyntaxError("for yield"); +assertSyntaxError("for yield (;;) {}"); +assertSyntaxError("for yield (x of y) {}"); +assertSyntaxError("for yield (var i in o) {}"); + +// Expression bodies. +assertSyntaxError("function* f() yield 7"); + +// Asm.js. +load(libdir + "asm.js"); +assertAsmDirectiveFail("function* f() { 'use asm'; function g() { return 0; } return g; })()") diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp index 7f577f199ce..2a17102b3bd 100644 --- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -4607,7 +4607,7 @@ ParseFunction(ModuleCompiler &m, ParseNode **fnOut) AsmJSParseContext *outerpc = m.parser().pc; Directives directives(outerpc); - FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives); + FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives, NotGenerator); if (!funbox) return false; diff --git a/js/src/js.msg b/js/src/js.msg index 3f665d8d548..cf71af5f763 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -409,3 +409,4 @@ MSG_DEF(JSMSG_BINARYDATA_NOT_BINARYSTRUCT, 355, 1, JSEXN_TYPEERR, "{0} is not MSG_DEF(JSMSG_BINARYDATA_SUBARRAY_INTEGER_ARG, 356, 1, JSEXN_ERR, "argument {0} must be an integer") MSG_DEF(JSMSG_BINARYDATA_STRUCTTYPE_EMPTY_DESCRIPTOR, 357, 0, JSEXN_ERR, "field descriptor cannot be empty") MSG_DEF(JSMSG_BINARYDATA_STRUCTTYPE_BAD_FIELD, 358, 1, JSEXN_ERR, "field {0} is not a valid BinaryData Type descriptor") +MSG_DEF(JSMSG_ES6_UNIMPLEMENTED, 359, 0, JSEXN_ERR, "ES6 functionality not yet implemented") diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index d6d2658613f..dd32d81b641 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -1749,7 +1749,7 @@ ScriptAnalysis::needsArgsObj(JSContext *cx) * * FIXME: Don't build arguments for ES6 generator expressions. */ - if (cx->compartment()->debugMode() || script_->isGenerator) + if (cx->compartment()->debugMode() || script_->isGenerator()) return true; /* diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index 7ed4381cdd2..d99380d218b 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -104,7 +104,6 @@ const char js_typeof_str[] = "typeof"; const char js_void_str[] = "void"; const char js_while_str[] = "while"; const char js_with_str[] = "with"; -const char js_yield_str[] = "yield"; /* * For a browser build from 2007-08-09 after the browser starts up there are diff --git a/js/src/jsatom.h b/js/src/jsatom.h index 1769065c100..9cd9272f0a3 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -153,7 +153,6 @@ extern const char js_typeof_str[]; extern const char js_void_str[]; extern const char js_while_str[]; extern const char js_with_str[]; -extern const char js_yield_str[]; namespace js { diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index bce514701de..4b0ff495095 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -526,6 +526,7 @@ FindBody(JSContext *cx, HandleFunction fun, StableCharPtr chars, size_t length, do { switch (ts.getToken()) { case TOK_NAME: + case TOK_YIELD: if (nest == 0) onward = false; break; @@ -1228,14 +1229,7 @@ fun_isGenerator(JSContext *cx, unsigned argc, Value *vp) return true; } - bool result = false; - if (fun->hasScript()) { - JSScript *script = fun->nonLazyScript(); - JS_ASSERT(script->length != 0); - result = script->isGenerator; - } - - JS_SET_RVAL(cx, vp, BooleanValue(result)); + JS_SET_RVAL(cx, vp, BooleanValue(fun->isGenerator())); return true; } @@ -1456,10 +1450,15 @@ js::Function(JSContext *cx, unsigned argc, Value *vp) return false; } + if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7) + tt = TOK_NAME; + if (tt != TOK_NAME) { if (tt == TOK_TRIPLEDOT) { hasRest = true; tt = ts.getToken(); + if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7) + tt = TOK_NAME; if (tt != TOK_NAME) { if (tt != TOK_ERROR) ts.reportError(JSMSG_NO_REST_NAME); @@ -1470,7 +1469,7 @@ js::Function(JSContext *cx, unsigned argc, Value *vp) } } - if (!formals.append(ts.currentToken().name())) + if (!formals.append(ts.currentName())) return false; /* diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 445c96d8048..1dd31b496e3 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -284,6 +284,18 @@ class JSFunction : public JSObject return u.i.s.lazy_; } + js::GeneratorKind generatorKind() const { + if (!isInterpreted()) + return js::NotGenerator; + return hasScript() ? nonLazyScript()->generatorKind() : lazyScript()->generatorKind(); + } + + bool isGenerator() const { return generatorKind() != js::NotGenerator; } + + bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; } + + bool isStarGenerator() const { return generatorKind() == js::StarGenerator; } + inline void setScript(JSScript *script_); inline void initScript(JSScript *script_); void initLazyScript(js::LazyScript *lazy) { diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 23ff9e8e3c0..f4f8ec349ab 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -1475,6 +1475,13 @@ js_NewGenerator(JSContext *cx, const FrameRegs &stackRegs) JS_ASSERT(stackRegs.stackDepth() == 0); StackFrame *stackfp = stackRegs.fp(); + JS_ASSERT(stackfp->script()->isGenerator()); + + if (stackfp->script()->isStarGenerator()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ES6_UNIMPLEMENTED); + return NULL; + } + Rooted global(cx, &stackfp->global()); RootedObject obj(cx); { diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index 613fc03c7ff..dc33ff76f84 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -2787,6 +2787,7 @@ ASTSerializer::function(ParseNode *pn, ASTType type, MutableHandleValue dst) { RootedFunction func(cx, pn->pn_funbox->function()); + // FIXME: Provide more information (legacy generator vs star generator). bool isGenerator = pn->pn_funbox->isGenerator(); bool isExpression = diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 2db9b1584d7..fae864ede0a 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -408,9 +408,9 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc FunHasAnyAliasedFormal, ArgumentsHasVarBinding, NeedsArgsObj, - IsGenerator, IsGeneratorExp, IsLegacyGenerator, + IsStarGenerator, OwnSource, ExplicitUseStrict, SelfHosted @@ -497,12 +497,12 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc scriptBits |= (1 << NeedsArgsObj); if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource()) scriptBits |= (1 << OwnSource); - if (script->isGenerator) - scriptBits |= (1 << IsGenerator); if (script->isGeneratorExp) scriptBits |= (1 << IsGeneratorExp); - if (script->isLegacyGenerator) + if (script->isLegacyGenerator()) scriptBits |= (1 << IsLegacyGenerator); + if (script->isStarGenerator()) + scriptBits |= (1 << IsStarGenerator); JS_ASSERT(!script->compileAndGo); JS_ASSERT(!script->hasSingletons); @@ -597,12 +597,14 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc script->setArgumentsHasVarBinding(); if (scriptBits & (1 << NeedsArgsObj)) script->setNeedsArgsObj(true); - if (scriptBits & (1 << IsGenerator)) - script->isGenerator = true; if (scriptBits & (1 << IsGeneratorExp)) script->isGeneratorExp = true; - if (scriptBits & (1 << IsLegacyGenerator)) - script->isLegacyGenerator = true; + + if (scriptBits & (1 << IsLegacyGenerator)) { + JS_ASSERT(!(scriptBits & (1 << IsStarGenerator))); + script->setGeneratorKind(LegacyGenerator); + } else if (scriptBits & (1 << IsStarGenerator)) + script->setGeneratorKind(StarGenerator); } JS_STATIC_ASSERT(sizeof(jsbytecode) == 1); @@ -1974,9 +1976,8 @@ JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, Byteco RootedFunction fun(cx, NULL); if (funbox) { JS_ASSERT(!bce->script->noScriptRval); - script->isGenerator = funbox->isGenerator(); script->isGeneratorExp = funbox->inGenexpLambda; - script->isLegacyGenerator = funbox->isLegacyGenerator(); + script->setGeneratorKind(funbox->generatorKind()); script->setFunction(funbox->function()); } @@ -2477,8 +2478,8 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, dst->funHasAnyAliasedFormal = src->funHasAnyAliasedFormal; dst->hasSingletons = src->hasSingletons; dst->treatAsRunOnce = src->treatAsRunOnce; - dst->isGenerator = src->isGenerator; dst->isGeneratorExp = src->isGeneratorExp; + dst->setGeneratorKind(src->generatorKind()); /* Copy over hints. */ dst->shouldInline = src->shouldInline; @@ -2919,7 +2920,7 @@ JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script) if (script->needsArgsObj()) return true; - JS_ASSERT(!script->isGenerator); + JS_ASSERT(!script->isGenerator()); script->needsArgsObj_ = true; @@ -3011,6 +3012,7 @@ LazyScript::LazyScript(JSFunction *fun, void *table, uint32_t numFreeVariables, version_(version), numFreeVariables_(numFreeVariables), numInnerFunctions_(numInnerFunctions), + generatorKindBits_(GeneratorKindAsBits(NotGenerator)), strict_(false), bindingsAccessedDynamically_(false), hasDebuggerStatement_(false), diff --git a/js/src/jsscript.h b/js/src/jsscript.h index f6cd11efb69..ee20caf4239 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -414,6 +414,19 @@ class ScriptSourceObject : public JSObject static const uint32_t SOURCE_SLOT = 0; }; +enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator }; + +static inline unsigned +GeneratorKindAsBits(GeneratorKind generatorKind) { + return static_cast(generatorKind); +} + +static inline GeneratorKind +GeneratorKindFromBits(unsigned val) { + JS_ASSERT(val <= StarGenerator); + return static_cast(val); +} + } /* namespace js */ class JSScript : public js::gc::Cell @@ -519,7 +532,7 @@ class JSScript : public js::gc::Cell uint16_t nslots; /* vars plus maximum stack depth */ uint16_t staticLevel;/* static level for display maintenance */ - // 8-bit fields. + // 4-bit fields. public: // The kinds of the optional arrays. @@ -528,15 +541,16 @@ class JSScript : public js::gc::Cell OBJECTS, REGEXPS, TRYNOTES, - LIMIT + ARRAY_KIND_BITS }; - typedef uint8_t ArrayBitsT; - private: // The bits in this field indicate the presence/non-presence of several // optional arrays in |data|. See the comments above Create() for details. - ArrayBitsT hasArrayBits; + uint8_t hasArrayBits:4; + + // The GeneratorKind of the script. + uint8_t generatorKindBits_:4; // 1-bit fields. @@ -589,14 +603,9 @@ class JSScript : public js::gc::Cell #endif bool invalidatedIdempotentCache:1; /* idempotent cache has triggered invalidation */ - // All generators have isGenerator set to true. - bool isGenerator:1; // If the generator was created implicitly via a generator expression, // isGeneratorExp will be true. bool isGeneratorExp:1; - // Generators are either legacy-style (JS 1.7+ starless generators with - // StopIteration), or ES6-style (function* with boxed return values). - bool isLegacyGenerator:1; bool hasScriptCounts:1;/* script has an entry in JSCompartment::scriptCountsMap */ @@ -647,6 +656,19 @@ class JSScript : public js::gc::Cell jsbytecode *argumentsBytecode() const { JS_ASSERT(code[0] == JSOP_ARGUMENTS); return code; } void setArgumentsHasVarBinding(); + js::GeneratorKind generatorKind() const { + return js::GeneratorKindFromBits(generatorKindBits_); + } + bool isGenerator() const { return generatorKind() != js::NotGenerator; } + bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; } + bool isStarGenerator() const { return generatorKind() == js::StarGenerator; } + void setGeneratorKind(js::GeneratorKind kind) { + // A script only gets its generator kind set as part of initialization, + // so it can only transition from not being a generator. + JS_ASSERT(!isGenerator()); + generatorKindBits_ = GeneratorKindAsBits(kind); + } + /* * As an optimization, even when argsHasLocalBinding, the function prologue * may not need to create an arguments object. This is determined by @@ -1038,7 +1060,8 @@ class JSScript : public js::gc::Cell void markChildren(JSTracer *trc); }; -JS_STATIC_ASSERT(sizeof(JSScript::ArrayBitsT) * 8 >= JSScript::LIMIT); +/* The array kind flags are stored in a 4-bit field; make sure they fit. */ +JS_STATIC_ASSERT(JSScript::ARRAY_KIND_BITS <= 4); /* If this fails, add/remove padding within JSScript. */ JS_STATIC_ASSERT(sizeof(JSScript) % js::gc::CellSize == 0); @@ -1145,7 +1168,9 @@ class LazyScript : public js::gc::Cell uint32_t version_ : 8; uint32_t numFreeVariables_ : 24; - uint32_t numInnerFunctions_ : 26; + uint32_t numInnerFunctions_ : 24; + + uint32_t generatorKindBits_:2; // N.B. These are booleans but need to be uint32_t to pack correctly on MSVC. uint32_t strict_ : 1; @@ -1211,6 +1236,23 @@ class LazyScript : public js::gc::Cell return (HeapPtrFunction *)&freeVariables()[numFreeVariables()]; } + GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); } + + bool isGenerator() const { return generatorKind() != NotGenerator; } + + bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; } + + bool isStarGenerator() const { return generatorKind() == StarGenerator; } + + void setGeneratorKind(GeneratorKind kind) { + // A script only gets its generator kind set as part of initialization, + // so it can only transition from NotGenerator. + JS_ASSERT(!isGenerator()); + // Legacy generators cannot currently be lazy. + JS_ASSERT(kind != LegacyGenerator); + generatorKindBits_ = GeneratorKindAsBits(kind); + } + bool strict() const { return strict_; } diff --git a/js/src/tests/ecma_6/Generators/shell.js b/js/src/tests/ecma_6/Generators/shell.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/js/src/tests/ecma_6/Generators/syntax.js b/js/src/tests/ecma_6/Generators/syntax.js new file mode 100644 index 00000000000..cf0ddb2e104 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/syntax.js @@ -0,0 +1,97 @@ +// This file was written by Andy Wingo and originally +// contributed to V8 as generators-parsing.js, available here: +// +// http://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/harmony/generators-parsing.js + +function assertSyntaxError(str) { + var msg; + var evil = eval; + try { + // Non-direct eval. + evil(str); + } catch (exc) { + if (exc instanceof SyntaxError) + return; + msg = "Assertion failed: expected SyntaxError, got " + exc; + } + if (msg === undefined) + msg = "Assertion failed: expected SyntaxError, but no exception thrown"; + throw new Error(msg + " - " + str); +} + +// Yield statements. +function* g() { yield 3; yield 4; } + +// Yield expressions. +function* g() { (yield 3) + (yield 4); } + +// You can have a generator in strict mode. +function* g() { "use strict"; yield 3; yield 4; } + +// Generators can have return statements also, which internally parse to a kind +// of yield expression. +function* g() { yield 1; return; } +function* g() { yield 1; return 2; } +function* g() { yield 1; return 2; yield "dead"; } + +// Generator expression. +(function* () { yield 3; }); + +// Named generator expression. +(function* g() { yield 3; }); + +// Generators do not have to contain yield expressions. +function* g() { } + +// YieldExpressions can occur in the RHS of a YieldExpression. +function* g() { yield yield 1; } +function* g() { yield 3 + (yield 4); } + +// Generator definitions with a name of "yield" are not specifically ruled out +// by the spec, as the `yield' name is outside the generator itself. However, +// in strict-mode, "yield" is an invalid identifier. +function* yield() { (yield 3) + (yield 4); } +assertSyntaxError("function* yield() { 'use strict'; (yield 3) + (yield 4); }"); + +// In classic mode, yield is a normal identifier, outside of generators. +function yield(yield) { yield: yield (yield + yield (0)); } + +// Yield is always valid as a key in an object literal. +({ yield: 1 }); +function* g() { yield ({ yield: 1 }) } +function* g() { yield ({ get yield() { return 1; }}) } + +// Yield is a valid property name. +function* g(obj) { yield obj.yield; } + +// Checks that yield is a valid label in classic mode, but not valid in a strict +// mode or in generators. +function f() { yield: 1 } +assertSyntaxError("function f() { 'use strict'; yield: 1 }") +assertSyntaxError("function* g() { yield: 1 }") + +// Yield is only a keyword in the body of the generator, not in nested +// functions. +function* g() { function f(yield) { yield (yield + yield (0)); } } + +// Yield needs a RHS. +assertSyntaxError("function* g() { yield; }"); + +// Yield in a generator is not an identifier. +assertSyntaxError("function* g() { yield = 10; }"); + +// Yield binds very loosely, so this parses as "yield (3 + yield 4)", which is +// invalid. +assertSyntaxError("function* g() { yield 3 + yield 4; }"); + +// Yield is still a future-reserved-word in strict mode +assertSyntaxError("function f() { 'use strict'; var yield = 13; }"); + +// The name of the NFE is let-bound in G, so is invalid. +assertSyntaxError("function* g() { yield (function yield() {}); }"); + +// In generators, yield is invalid as a formal argument name. +assertSyntaxError("function* g(yield) { yield (10); }"); + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 38fdef7236c..c3f7cf6ec99 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -152,6 +152,7 @@ macro(void0, void0, "(void 0)") \ macro(watch, watch, "watch") \ macro(writable, writable, "writable") \ + macro(yield, yield, "yield") \ /* Type names must be contiguous and ordered; see js::TypeName. */ \ macro(undefined, undefined, "undefined") \ macro(object, object, "object") \ diff --git a/js/src/vm/Keywords.h b/js/src/vm/Keywords.h index aa250b80a89..8e6a93ece2f 100644 --- a/js/src/vm/Keywords.h +++ b/js/src/vm/Keywords.h @@ -72,8 +72,12 @@ macro(protected, protected_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \ macro(public, public_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \ macro(static, static_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \ - /* ES5 future reserved keyword in strict mode, keyword in JS1.7 even when not strict. */ \ - macro(yield, yield, TOK_YIELD, JSVERSION_1_7) \ + /* \ + * ES5 future reserved keyword in strict mode, keyword in JS1.7 even when \ + * not strict, keyword inside function* in all versions. Punt logic to \ + * parser. \ + */ \ + macro(yield, yield, TOK_YIELD, JSVERSION_DEFAULT) \ /* Various conditional keywords. */ \ FOR_CONST_KEYWORD(macro) \ FOR_LET_KEYWORD(macro) From b8fca425b6cc244988ff133063d2150fc63434bb Mon Sep 17 00:00:00 2001 From: Jason Smith Date: Tue, 20 Aug 2013 23:40:29 -0700 Subject: [PATCH 13/54] Bug 899878 - Mochitest for recording media with no timeslice for media recording API. r=roc --- content/media/test/Makefile.in | 1 + ...est_mediarecorder_record_no_timeslice.html | 117 ++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 content/media/test/test_mediarecorder_record_no_timeslice.html diff --git a/content/media/test/Makefile.in b/content/media/test/Makefile.in index 7237d7c54bd..752e441ddf5 100644 --- a/content/media/test/Makefile.in +++ b/content/media/test/Makefile.in @@ -125,6 +125,7 @@ MOCHITEST_FILES = \ test_bug448534.html \ test_bug463162.xhtml \ test_decoder_disable.html \ + test_mediarecorder_record_no_timeslice.html \ test_mediarecorder_reload_crash.html \ test_media_selection.html \ test_playback.html \ diff --git a/content/media/test/test_mediarecorder_record_no_timeslice.html b/content/media/test/test_mediarecorder_record_no_timeslice.html new file mode 100644 index 00000000000..0c587b661e4 --- /dev/null +++ b/content/media/test/test_mediarecorder_record_no_timeslice.html @@ -0,0 +1,117 @@ + + + + Test MediaRecorder Record No Timeslice + + + + + +
+
+
+ + From 37a5d033a01f84f8c39f65340a206ef42519fb5e Mon Sep 17 00:00:00 2001 From: t_mrc-ct Date: Wed, 21 Aug 2013 22:40:57 +0900 Subject: [PATCH 14/54] Bug 906698 - nsUTF16ToUnicode is mis-converting on big-endian processor. r=smontagu CLOSED TREE --- intl/uconv/ucvlatin/nsUTF16ToUnicode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intl/uconv/ucvlatin/nsUTF16ToUnicode.cpp b/intl/uconv/ucvlatin/nsUTF16ToUnicode.cpp index c77d74d7bad..4e1097e051f 100644 --- a/intl/uconv/ucvlatin/nsUTF16ToUnicode.cpp +++ b/intl/uconv/ucvlatin/nsUTF16ToUnicode.cpp @@ -78,7 +78,7 @@ nsUTF16ToUnicodeBase::UTF16ConvertToUnicode(const char * aSrc, // previous run while the 2nd byte has to come from |*src|. mState = STATE_NORMAL; #ifdef IS_BIG_ENDIAN - u = (mOddByte << 8) | *src++; // safe, we know we have at least one byte. + u = (mOddByte << 8) | uint8_t(*src++); // safe, we know we have at least one byte. #else u = (*src++ << 8) | mOddByte; // safe, we know we have at least one byte. #endif From 26cfbdcee92312d5082d3b2adf3da14bcc5b398e Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Tue, 20 Aug 2013 18:14:44 -0400 Subject: [PATCH 15/54] Bug 906119 - Enable incremental linking with Visual C++; r=glandium --HG-- extra : rebase_source : 936287f63c95baa535a4991bc1106a3f9f59f36f --- configure.in | 5 ++++- js/src/configure.in | 5 ++++- toolkit/library/Makefile.in | 13 ------------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/configure.in b/configure.in index c869852f8de..7853e229226 100644 --- a/configure.in +++ b/configure.in @@ -2076,7 +2076,10 @@ ia64*-hpux*) XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xul.lib $(LIBXUL_DIST)/lib/mozalloc.lib' LIBXUL_LIBS='$(LIBXUL_DIST)/lib/xul.lib $(LIBXUL_DIST)/lib/mozalloc.lib' MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)' - LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT -RELEASE" + LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT" + if test -z "$DEVELOPER_OPTIONS"; then + LDFLAGS="$LDFLAGS -RELEASE" + fi dnl For profile-guided optimization PROFILE_GEN_CFLAGS="-GL" PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT" diff --git a/js/src/configure.in b/js/src/configure.in index 072270b1e74..5f17b3a9ca0 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -1637,7 +1637,10 @@ ia64*-hpux*) XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/mozalloc.lib' LIBXUL_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xul.lib $(LIBXUL_DIST)/lib/mozalloc.lib' MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)' - LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT -RELEASE" + LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT" + if test -z "$DEVELOPER_OPTIONS"; then + LDFLAGS="$LDFLAGS -RELEASE" + fi dnl For profile-guided optimization PROFILE_GEN_CFLAGS="-GL" PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT" diff --git a/toolkit/library/Makefile.in b/toolkit/library/Makefile.in index 1e245635e48..578e5d7f565 100644 --- a/toolkit/library/Makefile.in +++ b/toolkit/library/Makefile.in @@ -491,19 +491,6 @@ else SDK_LIBRARY = $(SHARED_LIBRARY) endif -# See bug 653662 - some builders are hitting an internal size limit on -# incremental builds. Disable incremental linking for debug builds on VC8 or -# on 32-bit hosts. -# See: http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/c34d5c37-ca4a-4580-9c7c-4379a8c76d1f/ -ifeq ($(OS_ARCH),WINNT) -IS_32BIT_HOST := $(if $(filter AMD64,$(PROCESSOR_ARCHITECTURE) $(PROCESSOR_ARCHITEW6432)),,yes) -ifneq (,$(or $(filter 1400,$(_MSC_VER)),$(IS_32BIT_HOST))) -ifdef MOZ_DEBUG -EXTRA_DSO_LDOPTS += -INCREMENTAL:NO -endif -endif -endif - EXTRA_DSO_LDOPTS += $(LIBS_DIR) EXTRA_DSO_LDOPTS += $(NSPR_LIBS) $(MOZALLOC_LIB) From d57def1e9fd00c823445c2dd654d196cb0e3371c Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Wed, 21 Aug 2013 11:07:03 -0400 Subject: [PATCH 16/54] Bug 681152 - Disable bug512295-1.html on all platforms because of intermittent failures Landed on a CLOSED TREE with RyanVM's blessing --- layout/base/tests/test_reftests_with_caret.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/base/tests/test_reftests_with_caret.html b/layout/base/tests/test_reftests_with_caret.html index 1ab34fca242..b763a5b6b32 100644 --- a/layout/base/tests/test_reftests_with_caret.html +++ b/layout/base/tests/test_reftests_with_caret.html @@ -118,7 +118,6 @@ if (!isWindows) { tests.push([ 'bug240933-2.html' , 'bug240933-1-ref.html' ]); // bug 681162 tests.push([ 'bug389321-1.html' , 'bug389321-1-ref.html' ]); // bug 683163 tests.push([ 'bug482484.html' , 'bug482484-ref.html' ]); // bug 688575 - tests.push([ 'bug512295-1.html' , 'bug512295-1-ref.html' ]); // bug 681152 tests.push([ 'bug512295-2.html' , 'bug512295-2-ref.html' ]); // bug 681331 tests.push([ 'bug597519-1.html' , 'bug597519-1-ref.html' ]); // bug 680579 tests.push([ 'bug602141-1.html' , 'bug602141-1-ref.html' ]); // bug 681334 @@ -142,6 +141,7 @@ if (!isWindows) { if (!isWindows && !isOSXMtnLion) { tests.push([ 'bug106855-1.html' , 'bug106855-1-ref.html' ]); // bug 682837 tests.push([ 'bug106855-2.html' , 'bug106855-1-ref.html?' ]); // bug 681138 + tests.push([ 'bug512295-1.html' , 'bug512295-1-ref.html' ]); // bug 681152 } */ From c25e61d121db1f70c19e0ff1f228bd76575d06e6 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Fri, 16 Aug 2013 15:01:04 -0400 Subject: [PATCH 17/54] Bug 906150 - initialize an empty RangeData at each point of use in nsSelection.cpp; r=smaug --- layout/generic/nsSelection.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index ca9af245b9f..d98acd16967 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -129,8 +129,6 @@ struct CachedOffsetForFrame { bool mCanCacheFrameOffset; // cached frame offset is valid? }; -static RangeData sEmptyData(nullptr); - // Stack-class to turn on/off selection batching for table selection class MOZ_STACK_CLASS nsSelectionBatcher MOZ_FINAL { @@ -4525,7 +4523,8 @@ Selection::GetRangeCount(int32_t* aRangeCount) NS_IMETHODIMP Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn) { - *aReturn = mRanges.SafeElementAt(aIndex, sEmptyData).mRange; + RangeData empty(nullptr); + *aReturn = mRanges.SafeElementAt(aIndex, empty).mRange; if (!*aReturn) { return NS_ERROR_DOM_INDEX_SIZE_ERR; } @@ -4538,7 +4537,8 @@ Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn) nsRange* Selection::GetRangeAt(int32_t aIndex) { - return mRanges.SafeElementAt(aIndex, sEmptyData).mRange; + RangeData empty(nullptr); + return mRanges.SafeElementAt(aIndex, empty).mRange; } /* From a94c411a496e40f5039fa8696555496117c198a4 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Fri, 16 Aug 2013 15:13:08 -0400 Subject: [PATCH 18/54] Bug 906152 - use StaticRefPtr in DataChannel.cpp to avoid a static constructor; r=jduell --- netwerk/sctp/datachannel/DataChannel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp index d6b55c47225..aec39b7dc07 100644 --- a/netwerk/sctp/datachannel/DataChannel.cpp +++ b/netwerk/sctp/datachannel/DataChannel.cpp @@ -38,6 +38,7 @@ #include "nsThreadUtils.h" #include "nsAutoPtr.h" #include "nsNetUtil.h" +#include "mozilla/StaticPtr.h" #ifdef MOZ_PEERCONNECTION #include "mtransport/runnable_utils.h" #endif @@ -78,7 +79,7 @@ static bool sctp_initialized; namespace mozilla { class DataChannelShutdown; -nsRefPtr gDataChannelShutdown; +StaticRefPtr gDataChannelShutdown; class DataChannelShutdown : public nsIObserver { From 1d8d55cbc7b7e143736fd8ef2a6f2899f584a71d Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Fri, 16 Aug 2013 15:23:28 -0400 Subject: [PATCH 19/54] Bug 906149 - make no-arg TimeDuration constructor constexpr to avoid a static constructor; r=ehsan --- xpcom/ds/TimeStamp.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xpcom/ds/TimeStamp.h b/xpcom/ds/TimeStamp.h index feb13b9a037..0fa9d454d92 100644 --- a/xpcom/ds/TimeStamp.h +++ b/xpcom/ds/TimeStamp.h @@ -8,6 +8,7 @@ #define mozilla_TimeStamp_h #include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" #include "prinrval.h" #include "nsDebug.h" @@ -44,7 +45,7 @@ class TimeDuration { public: // The default duration is 0. - TimeDuration() : mValue(0) {} + MOZ_CONSTEXPR TimeDuration() : mValue(0) {} // Allow construction using '0' as the initial value, for readability, // but no other numbers (so we don't have any implicit unit conversions). struct _SomethingVeryRandomHere; From 9f2201c2945f76e526b445d49bbb2aa695cda1d7 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Tue, 20 Aug 2013 11:21:31 -0400 Subject: [PATCH 20/54] Bug 884281 - use mozilla::Atomic in xpcom/; r=bsmedberg --- xpcom/base/AvailableMemoryTracker.cpp | 14 +++++------ xpcom/base/nsDebugImpl.cpp | 7 +++--- xpcom/base/nsExceptionService.cpp | 14 +++++------ xpcom/base/nsExceptionService.h | 3 ++- xpcom/base/nsMemoryImpl.cpp | 6 ++--- xpcom/base/nsMemoryImpl.h | 4 +++- xpcom/base/nsMemoryInfoDumper.cpp | 6 ++--- xpcom/string/src/nsSubstring.cpp | 34 ++++++++++++++------------- xpcom/threads/TimerThread.cpp | 2 +- xpcom/threads/TimerThread.h | 3 ++- xpcom/threads/nsTimerImpl.cpp | 14 ++++++----- 11 files changed, 58 insertions(+), 49 deletions(-) diff --git a/xpcom/base/AvailableMemoryTracker.cpp b/xpcom/base/AvailableMemoryTracker.cpp index 75f31aea9b9..b1985db99c4 100644 --- a/xpcom/base/AvailableMemoryTracker.cpp +++ b/xpcom/base/AvailableMemoryTracker.cpp @@ -7,7 +7,6 @@ #include "mozilla/AvailableMemoryTracker.h" #include "prinrval.h" -#include "pratom.h" #include "prenv.h" #include "nsIMemoryReporter.h" @@ -19,6 +18,7 @@ #include "nsPrintfCString.h" #include "nsThread.h" +#include "mozilla/Atomics.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" @@ -128,9 +128,9 @@ uint32_t sLowCommitSpaceThreshold = 0; uint32_t sLowPhysicalMemoryThreshold = 0; uint32_t sLowMemoryNotificationIntervalMS = 0; -uint32_t sNumLowVirtualMemEvents = 0; -uint32_t sNumLowCommitSpaceEvents = 0; -uint32_t sNumLowPhysicalMemEvents = 0; +Atomic sNumLowVirtualMemEvents; +Atomic sNumLowCommitSpaceEvents; +Atomic sNumLowPhysicalMemEvents; WindowsDllInterceptor sKernel32Intercept; WindowsDllInterceptor sGdi32Intercept; @@ -212,19 +212,19 @@ void CheckMemAvailable() // notification. We'll probably crash if we run out of virtual memory, // so don't worry about firing this notification too often. LOG("Detected low virtual memory."); - PR_ATOMIC_INCREMENT(&sNumLowVirtualMemEvents); + ++sNumLowVirtualMemEvents; NS_DispatchEventualMemoryPressure(MemPressure_New); } else if (stat.ullAvailPageFile < sLowCommitSpaceThreshold * 1024 * 1024) { LOG("Detected low available page file space."); if (MaybeScheduleMemoryPressureEvent()) { - PR_ATOMIC_INCREMENT(&sNumLowCommitSpaceEvents); + ++sNumLowCommitSpaceEvents; } } else if (stat.ullAvailPhys < sLowPhysicalMemoryThreshold * 1024 * 1024) { LOG("Detected low physical memory."); if (MaybeScheduleMemoryPressureEvent()) { - PR_ATOMIC_INCREMENT(&sNumLowPhysicalMemEvents); + ++sNumLowPhysicalMemEvents; } } } diff --git a/xpcom/base/nsDebugImpl.cpp b/xpcom/base/nsDebugImpl.cpp index e751d993b0d..16f243aa64f 100644 --- a/xpcom/base/nsDebugImpl.cpp +++ b/xpcom/base/nsDebugImpl.cpp @@ -6,6 +6,8 @@ // Chromium headers must come before Mozilla headers. #include "base/process_util.h" +#include "mozilla/Atomics.h" + #include "nsDebugImpl.h" #include "nsDebug.h" #ifdef MOZ_CRASHREPORTER @@ -20,7 +22,6 @@ #include "prerror.h" #include "prerr.h" #include "prenv.h" -#include "pratom.h" #ifdef ANDROID #include @@ -83,7 +84,7 @@ using namespace mozilla; static bool sIsMultiprocess = false; static const char *sMultiprocessDescription = NULL; -static int32_t gAssertionCount = 0; +static Atomic gAssertionCount; NS_IMPL_QUERY_INTERFACE2(nsDebugImpl, nsIDebug, nsIDebug2) @@ -390,7 +391,7 @@ NS_DebugBreak(uint32_t aSeverity, const char *aStr, const char *aExpr, } // Now we deal with assertions - PR_ATOMIC_INCREMENT(&gAssertionCount); + gAssertionCount++; switch (GetAssertBehavior()) { case NS_ASSERT_WARN: diff --git a/xpcom/base/nsExceptionService.cpp b/xpcom/base/nsExceptionService.cpp index ab3cf4d66b8..922ff5321ec 100644 --- a/xpcom/base/nsExceptionService.cpp +++ b/xpcom/base/nsExceptionService.cpp @@ -52,7 +52,7 @@ public: nsExceptionManager *mNextThread; // not ref-counted. nsExceptionService *mService; // not ref-counted #ifdef DEBUG - static int32_t totalInstances; + static Atomic totalInstances; #endif private: @@ -61,7 +61,7 @@ private: #ifdef DEBUG -int32_t nsExceptionManager::totalInstances = 0; +Atomic nsExceptionManager::totalInstances; #endif // Note this object is single threaded - the service itself ensures @@ -76,7 +76,7 @@ nsExceptionManager::nsExceptionManager(nsExceptionService *svc) : { /* member initializers and constructor code */ #ifdef DEBUG - PR_ATOMIC_INCREMENT(&totalInstances); + ++totalInstances; #endif } @@ -84,7 +84,7 @@ nsExceptionManager::~nsExceptionManager() { /* destructor code */ #ifdef DEBUG - PR_ATOMIC_DECREMENT(&totalInstances); + --totalInstances; #endif // DEBUG } @@ -120,7 +120,7 @@ Mutex *nsExceptionService::sLock = nullptr; nsExceptionManager *nsExceptionService::firstThread = nullptr; #ifdef DEBUG -int32_t nsExceptionService::totalInstances = 0; +Atomic nsExceptionService::totalInstances; #endif NS_IMPL_ISUPPORTS3(nsExceptionService, @@ -132,7 +132,7 @@ nsExceptionService::nsExceptionService() : mProviders(4, true) /* small, thread-safe hashtable */ { #ifdef DEBUG - if (PR_ATOMIC_INCREMENT(&totalInstances)!=1) { + if (++totalInstances != 1) { NS_ERROR("The nsExceptionService is a singleton!"); } #endif @@ -157,7 +157,7 @@ nsExceptionService::~nsExceptionService() Shutdown(); /* destructor code */ #ifdef DEBUG - PR_ATOMIC_DECREMENT(&totalInstances); + --totalInstances; #endif } diff --git a/xpcom/base/nsExceptionService.h b/xpcom/base/nsExceptionService.h index ee86f9a4285..e98e2b5b356 100644 --- a/xpcom/base/nsExceptionService.h +++ b/xpcom/base/nsExceptionService.h @@ -6,6 +6,7 @@ #ifndef nsExceptionService_h__ #define nsExceptionService_h__ +#include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/Mutex.h" @@ -52,7 +53,7 @@ public: static unsigned tlsIndex; static void ThreadDestruct( void *data ); #ifdef DEBUG - static int32_t totalInstances; + static mozilla::Atomic totalInstances; #endif private: diff --git a/xpcom/base/nsMemoryImpl.cpp b/xpcom/base/nsMemoryImpl.cpp index a67372a6d2c..d09a9136af8 100644 --- a/xpcom/base/nsMemoryImpl.cpp +++ b/xpcom/base/nsMemoryImpl.cpp @@ -114,7 +114,7 @@ nsMemoryImpl::FlushMemory(const PRUnichar* aReason, bool aImmediate) } } - int32_t lastVal = PR_ATOMIC_SET(&sIsFlushing, 1); + int32_t lastVal = sIsFlushing.exchange(1); if (lastVal) return NS_OK; @@ -183,8 +183,8 @@ nsMemoryImpl::FlushEvent::Run() return NS_OK; } -int32_t -nsMemoryImpl::sIsFlushing = 0; +mozilla::Atomic +nsMemoryImpl::sIsFlushing; PRIntervalTime nsMemoryImpl::sLastFlushTime = 0; diff --git a/xpcom/base/nsMemoryImpl.h b/xpcom/base/nsMemoryImpl.h index c73b8e2e6ee..ab27d01c199 100644 --- a/xpcom/base/nsMemoryImpl.h +++ b/xpcom/base/nsMemoryImpl.h @@ -6,6 +6,8 @@ #ifndef nsMemoryImpl_h__ #define nsMemoryImpl_h__ +#include "mozilla/Atomics.h" + #include "nsIMemory.h" #include "nsIRunnable.h" #include "prtime.h" @@ -37,7 +39,7 @@ protected: const PRUnichar* mReason; }; - static int32_t sIsFlushing; + static mozilla::Atomic sIsFlushing; static FlushEvent sFlushEvent; static PRIntervalTime sLastFlushTime; }; diff --git a/xpcom/base/nsMemoryInfoDumper.cpp b/xpcom/base/nsMemoryInfoDumper.cpp index 417e28b05ab..728edf18aff 100644 --- a/xpcom/base/nsMemoryInfoDumper.cpp +++ b/xpcom/base/nsMemoryInfoDumper.cpp @@ -6,6 +6,7 @@ #include "mozilla/nsMemoryInfoDumper.h" +#include "mozilla/Atomics.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/FileUtils.h" #include "mozilla/Preferences.h" @@ -142,7 +143,7 @@ static int sGCAndCCDumpSignum; // SIGRTMIN + 2 // This is the write-end of a pipe that we use to notice when a // dump-about-memory signal occurs. -static int sDumpAboutMemoryPipeWriteFd = -1; +static Atomic sDumpAboutMemoryPipeWriteFd(-1); void DumpAboutMemorySignalHandler(int aSignum) @@ -329,8 +330,7 @@ public: // 2) open a new fd with the same number as sDumpAboutMemoryPipeWriteFd // had. // 3) receive a signal, then write to the fd. - int pipeWriteFd = sDumpAboutMemoryPipeWriteFd; - PR_ATOMIC_SET(&sDumpAboutMemoryPipeWriteFd, -1); + int pipeWriteFd = sDumpAboutMemoryPipeWriteFd.exchange(-1); close(pipeWriteFd); FdWatcher::StopWatching(); diff --git a/xpcom/string/src/nsSubstring.cpp b/xpcom/string/src/nsSubstring.cpp index 1a9a730cd20..7f8943a2695 100644 --- a/xpcom/string/src/nsSubstring.cpp +++ b/xpcom/string/src/nsSubstring.cpp @@ -8,6 +8,7 @@ #define ENABLE_STRING_STATS #endif +#include "mozilla/Atomics.h" #include "mozilla/MemoryReporting.h" #ifdef ENABLE_STRING_STATS @@ -20,11 +21,12 @@ #include "nsStringBuffer.h" #include "nsDependentString.h" #include "nsMemory.h" -#include "pratom.h" #include "prprf.h" #include "nsStaticAtom.h" #include "nsCOMPtr.h" +using mozilla::Atomic; + // --------------------------------------------------------------------------- static PRUnichar gNullChar = 0; @@ -50,31 +52,31 @@ class nsStringStats return; printf("nsStringStats\n"); - printf(" => mAllocCount: % 10d\n", mAllocCount); - printf(" => mReallocCount: % 10d\n", mReallocCount); - printf(" => mFreeCount: % 10d", mFreeCount); + printf(" => mAllocCount: % 10d\n", (int)mAllocCount); + printf(" => mReallocCount: % 10d\n", (int)mReallocCount); + printf(" => mFreeCount: % 10d", (int)mFreeCount); if (mAllocCount > mFreeCount) printf(" -- LEAKED %d !!!\n", mAllocCount - mFreeCount); else printf("\n"); - printf(" => mShareCount: % 10d\n", mShareCount); - printf(" => mAdoptCount: % 10d\n", mAdoptCount); - printf(" => mAdoptFreeCount: % 10d", mAdoptFreeCount); + printf(" => mShareCount: % 10d\n", (int)mShareCount); + printf(" => mAdoptCount: % 10d\n", (int)mAdoptCount); + printf(" => mAdoptFreeCount: % 10d", (int)mAdoptFreeCount); if (mAdoptCount > mAdoptFreeCount) printf(" -- LEAKED %d !!!\n", mAdoptCount - mAdoptFreeCount); else printf("\n"); } - int32_t mAllocCount; - int32_t mReallocCount; - int32_t mFreeCount; - int32_t mShareCount; - int32_t mAdoptCount; - int32_t mAdoptFreeCount; + Atomic mAllocCount; + Atomic mReallocCount; + Atomic mFreeCount; + Atomic mShareCount; + Atomic mAdoptCount; + Atomic mAdoptFreeCount; }; static nsStringStats gStringStats; -#define STRING_STAT_INCREMENT(_s) PR_ATOMIC_INCREMENT(&gStringStats.m ## _s ## Count) +#define STRING_STAT_INCREMENT(_s) (gStringStats.m ## _s ## Count)++ #else #define STRING_STAT_INCREMENT(_s) #endif @@ -148,7 +150,7 @@ class nsACStringAccessor : public nsACString void nsStringBuffer::AddRef() { - PR_ATOMIC_INCREMENT(&mRefCount); + ++mRefCount; STRING_STAT_INCREMENT(Share); NS_LOG_ADDREF(this, mRefCount, "nsStringBuffer", sizeof(*this)); } @@ -156,7 +158,7 @@ nsStringBuffer::AddRef() void nsStringBuffer::Release() { - int32_t count = PR_ATOMIC_DECREMENT(&mRefCount); + int32_t count = --mRefCount; NS_LOG_RELEASE(this, count, "nsStringBuffer"); if (count == 0) { diff --git a/xpcom/threads/TimerThread.cpp b/xpcom/threads/TimerThread.cpp index 9053f8f4118..68803aeeb94 100644 --- a/xpcom/threads/TimerThread.cpp +++ b/xpcom/threads/TimerThread.cpp @@ -84,7 +84,7 @@ nsresult TimerThread::Init() return NS_OK; } - if (PR_ATOMIC_SET(&mInitInProgress, 1) == 0) { + if (mInitInProgress.exchange(1) == 0) { // We hold on to mThread to keep the thread alive. nsresult rv = NS_NewThread(getter_AddRefs(mThread), this); if (NS_FAILED(rv)) { diff --git a/xpcom/threads/TimerThread.h b/xpcom/threads/TimerThread.h index a9699c29f3c..aba1e4ba8ed 100644 --- a/xpcom/threads/TimerThread.h +++ b/xpcom/threads/TimerThread.h @@ -15,6 +15,7 @@ #include "nsTArray.h" +#include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/Monitor.h" #include "mozilla/TimeStamp.h" @@ -52,7 +53,7 @@ public: private: ~TimerThread(); - int32_t mInitInProgress; + mozilla::Atomic mInitInProgress; bool mInitialized; // These two internal helper methods must be called while mLock is held. diff --git a/xpcom/threads/nsTimerImpl.cpp b/xpcom/threads/nsTimerImpl.cpp index 634b5fc8dbd..a59ea4b4172 100644 --- a/xpcom/threads/nsTimerImpl.cpp +++ b/xpcom/threads/nsTimerImpl.cpp @@ -12,11 +12,13 @@ #include "plarena.h" #include "pratom.h" #include "GeckoProfiler.h" +#include "mozilla/Atomics.h" +using mozilla::Atomic; using mozilla::TimeDuration; using mozilla::TimeStamp; -static int32_t gGenerator = 0; +static Atomic gGenerator; static TimerThread* gThread = nullptr; #ifdef DEBUG_TIMERS @@ -114,7 +116,7 @@ public: MOZ_ASSERT(gThread->IsOnTimerThread(), "nsTimer must always be allocated on the timer thread"); - PR_ATOMIC_INCREMENT(&sAllocatorUsers); + sAllocatorUsers++; } #ifdef DEBUG_TIMERS @@ -140,19 +142,19 @@ private: MOZ_ASSERT(!sCanDeleteAllocator || sAllocatorUsers > 0, "This will result in us attempting to deallocate the nsTimerEvent allocator twice"); - PR_ATOMIC_DECREMENT(&sAllocatorUsers); + sAllocatorUsers--; } nsRefPtr mTimer; int32_t mGeneration; static TimerEventAllocator* sAllocator; - static int32_t sAllocatorUsers; + static Atomic sAllocatorUsers; static bool sCanDeleteAllocator; }; TimerEventAllocator* nsTimerEvent::sAllocator = nullptr; -int32_t nsTimerEvent::sAllocatorUsers = 0; +Atomic nsTimerEvent::sAllocatorUsers; bool nsTimerEvent::sCanDeleteAllocator = false; namespace { @@ -342,7 +344,7 @@ nsresult nsTimerImpl::InitCommon(uint32_t aType, uint32_t aDelay) gThread->RemoveTimer(this); mCanceled = false; mTimeout = TimeStamp(); - mGeneration = PR_ATOMIC_INCREMENT(&gGenerator); + mGeneration = gGenerator++; mType = (uint8_t)aType; SetDelayInternal(aDelay); From 7e84fb44e4a00483e77fc7539f9c876e367af7ca Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Tue, 20 Aug 2013 15:22:27 -0400 Subject: [PATCH 21/54] Bug 907355 - fix warning about set-but-not-used variable in nsTimerImpl.cpp; r=ehsan --- xpcom/threads/nsTimerImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xpcom/threads/nsTimerImpl.cpp b/xpcom/threads/nsTimerImpl.cpp index a59ea4b4172..bb59fc98bf1 100644 --- a/xpcom/threads/nsTimerImpl.cpp +++ b/xpcom/threads/nsTimerImpl.cpp @@ -499,8 +499,8 @@ void nsTimerImpl::Fire() PROFILER_LABEL("Timer", "Fire"); - TimeStamp now = TimeStamp::Now(); #ifdef DEBUG_TIMERS + TimeStamp now = TimeStamp::Now(); if (PR_LOG_TEST(GetTimerLog(), PR_LOG_DEBUG)) { TimeDuration a = now - mStart; // actual delay in intervals TimeDuration b = TimeDuration::FromMilliseconds(mDelay); // expected delay in intervals From 7e4027ab576e49cacfd477ddc35ed5b954594f9e Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Tue, 20 Aug 2013 16:32:54 -0400 Subject: [PATCH 22/54] Bug 907728 - use function overloading instead of defaulted arguments for nsTSubstring::Assign; r=jlebar --- xpcom/string/public/nsTSubstring.h | 4 ++-- xpcom/string/src/nsTSubstring.cpp | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/xpcom/string/public/nsTSubstring.h b/xpcom/string/public/nsTSubstring.h index 972195ec380..f73ce8b20b5 100644 --- a/xpcom/string/public/nsTSubstring.h +++ b/xpcom/string/public/nsTSubstring.h @@ -342,8 +342,8 @@ class nsTSubstring_CharT void NS_FASTCALL Assign( char_type c ); bool NS_FASTCALL Assign( char_type c, const fallible_t& ) NS_WARN_UNUSED_RESULT; - void NS_FASTCALL - Assign( const char_type* data, size_type length = size_type(-1) ); + void NS_FASTCALL Assign( const char_type* data ); + void NS_FASTCALL Assign( const char_type* data, size_type length ); bool NS_FASTCALL Assign( const char_type* data, size_type length, const fallible_t& ) NS_WARN_UNUSED_RESULT; void NS_FASTCALL Assign( const self_type& ); diff --git a/xpcom/string/src/nsTSubstring.cpp b/xpcom/string/src/nsTSubstring.cpp index 4acfb87b9a8..eb8adda10cb 100644 --- a/xpcom/string/src/nsTSubstring.cpp +++ b/xpcom/string/src/nsTSubstring.cpp @@ -285,6 +285,13 @@ nsTSubstring_CharT::Assign( char_type c, const fallible_t& ) return true; } +void +nsTSubstring_CharT::Assign( const char_type* data ) + { + if (!Assign(data, size_type(-1), fallible_t())) + NS_RUNTIMEABORT("OOM"); + } + void nsTSubstring_CharT::Assign( const char_type* data, size_type length ) { From 247a3a62e7254e30171704e48e8ccc4287a7b545 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 12 Aug 2013 23:55:28 +0200 Subject: [PATCH 23/54] Bug 900201 - Implement GfxInfo on gonk - r=ehsan --- widget/gonk/GfxInfo.cpp | 189 ++++++++++++++++++++++++++++++++ widget/gonk/GfxInfo.h | 61 +++++++++++ widget/gonk/moz.build | 1 + widget/gonk/nsWidgetFactory.cpp | 13 +++ 4 files changed, 264 insertions(+) create mode 100644 widget/gonk/GfxInfo.cpp create mode 100644 widget/gonk/GfxInfo.h diff --git a/widget/gonk/GfxInfo.cpp b/widget/gonk/GfxInfo.cpp new file mode 100644 index 00000000000..fd5c693760a --- /dev/null +++ b/widget/gonk/GfxInfo.cpp @@ -0,0 +1,189 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GfxInfo.h" + +using namespace mozilla::widget; + +/* GetD2DEnabled and GetDwriteEnabled shouldn't be called until after gfxPlatform initialization + * has occurred because they depend on it for information. (See bug 591561) */ +nsresult +GfxInfo::GetD2DEnabled(bool *aEnabled) +{ + return NS_ERROR_FAILURE; +} + +nsresult +GfxInfo::GetDWriteEnabled(bool *aEnabled) +{ + return NS_ERROR_FAILURE; +} + +/* readonly attribute DOMString DWriteVersion; */ +NS_IMETHODIMP +GfxInfo::GetDWriteVersion(nsAString & aDwriteVersion) +{ + return NS_ERROR_FAILURE; +} + +/* readonly attribute DOMString cleartypeParameters; */ +NS_IMETHODIMP +GfxInfo::GetCleartypeParameters(nsAString & aCleartypeParams) +{ + return NS_ERROR_FAILURE; +} + +/* readonly attribute DOMString adapterDescription; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDescription(nsAString & aAdapterDescription) +{ + aAdapterDescription.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterDescription2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDescription2(nsAString & aAdapterDescription) +{ + return NS_ERROR_FAILURE; +} + +/* readonly attribute DOMString adapterRAM; */ +NS_IMETHODIMP +GfxInfo::GetAdapterRAM(nsAString & aAdapterRAM) +{ + aAdapterRAM.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterRAM2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterRAM2(nsAString & aAdapterRAM) +{ + return NS_ERROR_FAILURE; +} + +/* readonly attribute DOMString adapterDriver; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriver(nsAString & aAdapterDriver) +{ + aAdapterDriver.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterDriver2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriver2(nsAString & aAdapterDriver) +{ + return NS_ERROR_FAILURE; +} + +/* readonly attribute DOMString adapterDriverVersion; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriverVersion(nsAString & aAdapterDriverVersion) +{ + aAdapterDriverVersion.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterDriverVersion2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriverVersion2(nsAString & aAdapterDriverVersion) +{ + return NS_ERROR_FAILURE; +} + +/* readonly attribute DOMString adapterDriverDate; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriverDate(nsAString & aAdapterDriverDate) +{ + aAdapterDriverDate.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterDriverDate2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDriverDate2(nsAString & aAdapterDriverDate) +{ + return NS_ERROR_FAILURE; +} + +/* readonly attribute DOMString adapterVendorID; */ +NS_IMETHODIMP +GfxInfo::GetAdapterVendorID(nsAString & aAdapterVendorID) +{ + aAdapterVendorID.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterVendorID2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterVendorID2(nsAString & aAdapterVendorID) +{ + return NS_ERROR_FAILURE; +} + +/* readonly attribute DOMString adapterDeviceID; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDeviceID(nsAString & aAdapterDeviceID) +{ + aAdapterDeviceID.AssignLiteral(""); + return NS_OK; +} + +/* readonly attribute DOMString adapterDeviceID2; */ +NS_IMETHODIMP +GfxInfo::GetAdapterDeviceID2(nsAString & aAdapterDeviceID) +{ + return NS_ERROR_FAILURE; +} + +/* readonly attribute boolean isGPU2Active; */ +NS_IMETHODIMP +GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active) +{ + return NS_ERROR_FAILURE; +} + +const nsTArray& +GfxInfo::GetGfxDriverInfo() +{ + return *mDriverInfo; +} + +uint32_t GfxInfo::OperatingSystemVersion() const +{ + return 0; +} + +#ifdef DEBUG + +// Implement nsIGfxInfoDebug + +/* void spoofVendorID (in DOMString aVendorID); */ +NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString &) +{ + return NS_OK; +} + +/* void spoofDeviceID (in unsigned long aDeviceID); */ +NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString &) +{ + return NS_OK; +} + +/* void spoofDriverVersion (in DOMString aDriverVersion); */ +NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString &) +{ + return NS_OK; +} + +/* void spoofOSVersion (in unsigned long aVersion); */ +NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t) +{ + return NS_OK; +} + +#endif \ No newline at end of file diff --git a/widget/gonk/GfxInfo.h b/widget/gonk/GfxInfo.h new file mode 100644 index 00000000000..f9c97b78e73 --- /dev/null +++ b/widget/gonk/GfxInfo.h @@ -0,0 +1,61 @@ +/* vim: se cin sw=2 ts=2 et : */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __mozilla_widget_GfxInfo_h__ +#define __mozilla_widget_GfxInfo_h__ + +#include "GfxInfoBase.h" +#include "GfxDriverInfo.h" + +#include "nsString.h" + +namespace mozilla { +namespace widget { + +class GfxInfo : public GfxInfoBase +{ +public: + // We only declare the subset of nsIGfxInfo that we actually implement. The + // rest is brought forward from GfxInfoBase. + NS_IMETHOD GetD2DEnabled(bool *aD2DEnabled); + NS_IMETHOD GetDWriteEnabled(bool *aDWriteEnabled); + NS_IMETHOD GetDWriteVersion(nsAString & aDwriteVersion); + NS_IMETHOD GetCleartypeParameters(nsAString & aCleartypeParams); + NS_IMETHOD GetAdapterDescription(nsAString & aAdapterDescription); + NS_IMETHOD GetAdapterDriver(nsAString & aAdapterDriver); + NS_IMETHOD GetAdapterVendorID(nsAString & aAdapterVendorID); + NS_IMETHOD GetAdapterDeviceID(nsAString & aAdapterDeviceID); + NS_IMETHOD GetAdapterRAM(nsAString & aAdapterRAM); + NS_IMETHOD GetAdapterDriverVersion(nsAString & aAdapterDriverVersion); + NS_IMETHOD GetAdapterDriverDate(nsAString & aAdapterDriverDate); + NS_IMETHOD GetAdapterDescription2(nsAString & aAdapterDescription); + NS_IMETHOD GetAdapterDriver2(nsAString & aAdapterDriver); + NS_IMETHOD GetAdapterVendorID2(nsAString & aAdapterVendorID); + NS_IMETHOD GetAdapterDeviceID2(nsAString & aAdapterDeviceID); + NS_IMETHOD GetAdapterRAM2(nsAString & aAdapterRAM); + NS_IMETHOD GetAdapterDriverVersion2(nsAString & aAdapterDriverVersion); + NS_IMETHOD GetAdapterDriverDate2(nsAString & aAdapterDriverDate); + NS_IMETHOD GetIsGPU2Active(bool *aIsGPU2Active); + using GfxInfoBase::GetFeatureStatus; + using GfxInfoBase::GetFeatureSuggestedDriverVersion; + using GfxInfoBase::GetWebGLParameter; + + virtual uint32_t OperatingSystemVersion() const; + +#ifdef DEBUG + NS_DECL_NSIGFXINFODEBUG +#endif + +protected: + + virtual const nsTArray& GetGfxDriverInfo(); +}; + +} // namespace widget +} // namespace mozilla + +#endif /* __mozilla_widget_GfxInfo_h__ */ diff --git a/widget/gonk/moz.build b/widget/gonk/moz.build index bf37f187277..8bd0de5ba5d 100644 --- a/widget/gonk/moz.build +++ b/widget/gonk/moz.build @@ -53,6 +53,7 @@ CPP_SOURCES += [ 'nsLookAndFeel.cpp', 'nsWidgetFactory.cpp', 'nsWindow.cpp', + 'GfxInfo.cpp', ] if CONFIG['ANDROID_VERSION'] == '15': diff --git a/widget/gonk/nsWidgetFactory.cpp b/widget/gonk/nsWidgetFactory.cpp index 934828af8c8..78c6bae7e95 100644 --- a/widget/gonk/nsWidgetFactory.cpp +++ b/widget/gonk/nsWidgetFactory.cpp @@ -36,6 +36,16 @@ using namespace mozilla::widget; +// taken from android/nsWidgetFactory.cpp. GfxInfo is a legacy kludge, unfortunately +// for the time being we still have to implement it on all platforms. +#include "GfxInfo.h" +namespace mozilla { +namespace widget { +// This constructor should really be shared with all platforms. +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init) +} +} + NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow) NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerGonk) NS_GENERIC_FACTORY_CONSTRUCTOR(PuppetScreenManager) @@ -50,6 +60,7 @@ NS_DEFINE_NAMED_CID(NS_SCREENMANAGER_CID); NS_DEFINE_NAMED_CID(NS_HTMLFORMATCONVERTER_CID); NS_DEFINE_NAMED_CID(NS_IDLE_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_TRANSFERABLE_CID); +NS_DEFINE_NAMED_CID(NS_GFXINFO_CID); static nsresult ScreenManagerConstructor(nsISupports *aOuter, REFNSIID aIID, void **aResult) @@ -67,6 +78,7 @@ static const mozilla::Module::CIDEntry kWidgetCIDs[] = { { &kNS_HTMLFORMATCONVERTER_CID, false, NULL, nsHTMLFormatConverterConstructor }, { &kNS_IDLE_SERVICE_CID, false, NULL, nsIdleServiceGonkConstructor }, { &kNS_TRANSFERABLE_CID, false, NULL, nsTransferableConstructor }, + { &kNS_GFXINFO_CID, false, NULL, mozilla::widget::GfxInfoConstructor }, { NULL } }; @@ -78,6 +90,7 @@ static const mozilla::Module::ContractIDEntry kWidgetContracts[] = { { "@mozilla.org/widget/htmlformatconverter;1", &kNS_HTMLFORMATCONVERTER_CID }, { "@mozilla.org/widget/idleservice;1", &kNS_IDLE_SERVICE_CID }, { "@mozilla.org/widget/transferable;1", &kNS_TRANSFERABLE_CID }, + { "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID }, { NULL } }; From c2de5fb7941ce5076c3767c32ca55c46321f7c10 Mon Sep 17 00:00:00 2001 From: Neil Deakin Date: Wed, 21 Aug 2013 09:36:07 +0200 Subject: [PATCH 24/54] Bug 847863 - Part 6 of 8 - Convert tests for clearing recent history. r=paolo --- browser/base/content/sanitize.js | 80 ++-- .../test/browser_sanitize-timespans.js | 436 +++++++----------- .../content/test/browser_sanitizeDialog.js | 165 +++---- 3 files changed, 309 insertions(+), 372 deletions(-) diff --git a/browser/base/content/sanitize.js b/browser/base/content/sanitize.js index 771783e69eb..5c3aa05e936 100644 --- a/browser/base/content/sanitize.js +++ b/browser/base/content/sanitize.js @@ -16,7 +16,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", "resource:///modules/DownloadsCommon.jsm"); - + function Sanitizer() {} Sanitizer.prototype = { // warning to the caller: this one may raise an exception (e.g. bug #265028) @@ -37,14 +37,14 @@ Sanitizer.prototype = { aCallback(aItemName, canClear, aArg); return canClear; }, - + prefDomain: "", - + getNameFromPreference: function (aPreferenceName) { return aPreferenceName.substr(this.prefDomain.length); }, - + /** * Deletes privacy sensitive data in a batch, according to user preferences. * Returns a promise which is resolved if no errors occurred. If an error @@ -87,7 +87,8 @@ Sanitizer.prototype = { item.clear(); } catch(er) { seenError = true; - Cu.reportError("Error sanitizing " + itemName + ": " + er + "\n"); + Components.utils.reportError("Error sanitizing " + itemName + + ": " + er + "\n"); } onItemComplete(); }; @@ -99,7 +100,7 @@ Sanitizer.prototype = { return deferred.promise; }, - + // Time span only makes sense in certain cases. Consumers who want // to only clear some private data can opt in by setting this to false, // and can optionally specify a specific range. If timespan is not ignored, @@ -107,7 +108,7 @@ Sanitizer.prototype = { // pref to determine a range ignoreTimespan : true, range : null, - + items: { cache: { clear: function () @@ -126,13 +127,13 @@ Sanitizer.prototype = { imageCache.clearCache(false); // true=chrome, false=content } catch(er) {} }, - + get canClear() { return true; } }, - + cookies: { clear: function () { @@ -143,7 +144,7 @@ Sanitizer.prototype = { var cookiesEnum = cookieMgr.enumerator; while (cookiesEnum.hasMoreElements()) { var cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2); - + if (cookie.creationTime > this.range[0]) // This cookie was created after our cutoff, clear it cookieMgr.remove(cookie.host, cookie.name, cookie.path, false); @@ -211,14 +212,14 @@ Sanitizer.prototype = { PlacesUtils.history.removeVisitsByTimeframe(this.range[0], this.range[1]); else PlacesUtils.history.removeAllPages(); - + try { var os = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); os.notifyObservers(null, "browser:purge-session-history", ""); } catch (e) { } - + // Clear last URL of the Open Web Location dialog var prefs = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefBranch); @@ -227,7 +228,7 @@ Sanitizer.prototype = { } catch (e) { } }, - + get canClear() { // bug 347231: Always allow clearing history due to dependencies on @@ -235,7 +236,7 @@ Sanitizer.prototype = { return true; } }, - + formdata: { clear: function () { @@ -305,15 +306,20 @@ Sanitizer.prototype = { return false; } }, - + downloads: { clear: function () { if (DownloadsCommon.useJSTransfer) { Task.spawn(function () { - let filterByTime = this.range ? - (download => download.startTime >= this.range[0] && - download.startTime <= this.range[1]) : null; + let filterByTime = null; + if (this.range) { + // Convert microseconds back to milliseconds for date comparisons. + let rangeBeginMs = this.range[0] / 1000; + let rangeEndMs = this.range[1] / 1000; + filterByTime = download => download.startTime >= rangeBeginMs && + download.startTime <= rangeEndMs; + } // Clear all completed/cancelled downloads let publicList = yield Downloads.getPublicDownloadList(); @@ -321,7 +327,7 @@ Sanitizer.prototype = { let privateList = yield Downloads.getPrivateDownloadList(); privateList.removeFinished(filterByTime); - }.bind(this)).then(null, Cu.reportError); + }.bind(this)).then(null, Components.utils.reportError); } else { var dlMgr = Components.classes["@mozilla.org/download-manager;1"] @@ -352,7 +358,7 @@ Sanitizer.prototype = { return false; } }, - + passwords: { clear: function () { @@ -361,7 +367,7 @@ Sanitizer.prototype = { // Passwords are timeless, and don't respect the timeSpan setting pwmgr.removeAllLogins(); }, - + get canClear() { var pwmgr = Components.classes["@mozilla.org/login-manager;1"] @@ -370,7 +376,7 @@ Sanitizer.prototype = { return (count > 0); } }, - + sessions: { clear: function () { @@ -384,13 +390,13 @@ Sanitizer.prototype = { .getService(Components.interfaces.nsIObserverService); os.notifyObservers(null, "net:clear-active-logins", null); }, - + get canClear() { return true; } }, - + siteSettings: { clear: function () { @@ -398,12 +404,12 @@ Sanitizer.prototype = { var pm = Components.classes["@mozilla.org/permissionmanager;1"] .getService(Components.interfaces.nsIPermissionManager); pm.removeAll(); - + // Clear site-specific settings like page-zoom level var cps = Components.classes["@mozilla.org/content-pref/service;1"] .getService(Components.interfaces.nsIContentPrefService2); cps.removeAllDomains(null); - + // Clear "Never remember passwords for this site", which is not handled by // the permission manager var pwmgr = Components.classes["@mozilla.org/login-manager;1"] @@ -413,7 +419,7 @@ Sanitizer.prototype = { pwmgr.setLoginSavingEnabled(host, true); } }, - + get canClear() { return true; @@ -446,7 +452,7 @@ Sanitizer.getClearRange = function (ts) { ts = Sanitizer.prefs.getIntPref("timeSpan"); if (ts === Sanitizer.TIMESPAN_EVERYTHING) return null; - + // PRTime is microseconds while JS time is milliseconds var endDate = Date.now() * 1000; switch (ts) { @@ -473,7 +479,7 @@ Sanitizer.getClearRange = function (ts) { }; Sanitizer._prefs = null; -Sanitizer.__defineGetter__("prefs", function() +Sanitizer.__defineGetter__("prefs", function() { return Sanitizer._prefs ? Sanitizer._prefs : Sanitizer._prefs = Components.classes["@mozilla.org/preferences-service;1"] @@ -482,7 +488,7 @@ Sanitizer.__defineGetter__("prefs", function() }); // Shows sanitization UI -Sanitizer.showUI = function(aParentWindow) +Sanitizer.showUI = function(aParentWindow) { var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher); @@ -497,32 +503,32 @@ Sanitizer.showUI = function(aParentWindow) null); }; -/** - * Deletes privacy sensitive data in a batch, optionally showing the +/** + * Deletes privacy sensitive data in a batch, optionally showing the * sanitize UI, according to user preferences */ -Sanitizer.sanitize = function(aParentWindow) +Sanitizer.sanitize = function(aParentWindow) { Sanitizer.showUI(aParentWindow); }; -Sanitizer.onStartup = function() +Sanitizer.onStartup = function() { // we check for unclean exit with pending sanitization Sanitizer._checkAndSanitize(); }; -Sanitizer.onShutdown = function() +Sanitizer.onShutdown = function() { // we check if sanitization is needed and perform it Sanitizer._checkAndSanitize(); }; // this is called on startup and shutdown, to perform pending sanitizations -Sanitizer._checkAndSanitize = function() +Sanitizer._checkAndSanitize = function() { const prefs = Sanitizer.prefs; - if (prefs.getBoolPref(Sanitizer.prefShutdown) && + if (prefs.getBoolPref(Sanitizer.prefShutdown) && !prefs.prefHasUserValue(Sanitizer.prefDidShutdown)) { // this is a shutdown or a startup after an unclean exit var s = new Sanitizer(); diff --git a/browser/base/content/test/browser_sanitize-timespans.js b/browser/base/content/test/browser_sanitize-timespans.js index 6c6fb059b9f..e470b22cc61 100644 --- a/browser/base/content/test/browser_sanitize-timespans.js +++ b/browser/base/content/test/browser_sanitize-timespans.js @@ -2,10 +2,10 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); // Bug 453440 - Test the timespan-based logic of the sanitizer code -var now_uSec = Date.now() * 1000; - -const dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); +let now_mSec = Date.now(); +let now_uSec = now_mSec * 1000; +const kMsecPerMin = 60 * 1000; const kUsecPerMin = 60 * 1000000; let tempScope = {}; @@ -14,6 +14,7 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) let Sanitizer = tempScope.Sanitizer; let FormHistory = (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory; +let Downloads = (Components.utils.import("resource://gre/modules/Downloads.jsm", {})).Downloads; function promiseFormHistoryRemoved() { let deferred = Promise.defer(); @@ -24,15 +25,30 @@ function promiseFormHistoryRemoved() { return deferred.promise; } +function promiseDownloadRemoved(list) { + let deferred = Promise.defer(); + + let view = { + onDownloadRemoved: function(download) { + list.removeView(view); + deferred.resolve(); + } + }; + + list.addView(view); + + return deferred.promise; +} + function test() { waitForExplicitFinish(); Task.spawn(function() { - setupDownloads(); + yield setupDownloads(); yield setupFormHistory(); yield setupHistory(); yield onHistoryReady(); - }).then(finish); + }).then(null, ex => ok(false, ex)).then(finish); } function countEntries(name, message, check) { @@ -80,12 +96,16 @@ function onHistoryReady() { itemPrefs.setBoolPref("sessions", false); itemPrefs.setBoolPref("siteSettings", false); + let publicList = yield Downloads.getPublicDownloadList(); + let downloadPromise = promiseDownloadRemoved(publicList); + // Clear 10 minutes ago s.range = [now_uSec - 10*60*1000000, now_uSec]; s.sanitize(); s.range = null; yield promiseFormHistoryRemoved(); + yield downloadPromise; ok(!(yield promiseIsURIVisited(makeURI("http://10minutes.com"))), "Pretend visit to 10minutes.com should now be deleted"); @@ -122,23 +142,26 @@ function onHistoryReady() { yield countEntries("today", "today form entry should still exist", checkOne); yield countEntries("b4today", "b4today form entry should still exist", checkOne); - ok(!downloadExists(5555555), "10 minute download should now be deleted"); - ok(downloadExists(5555551), "<1 hour download should still be present"); - ok(downloadExists(5555556), "1 hour 10 minute download should still be present"); - ok(downloadExists(5555550), "Year old download should still be present"); - ok(downloadExists(5555552), "<2 hour old download should still be present"); - ok(downloadExists(5555557), "2 hour 10 minute download should still be present"); - ok(downloadExists(5555553), "<4 hour old download should still be present"); - ok(downloadExists(5555558), "4 hour 10 minute download should still be present"); + ok(!(yield downloadExists(publicList, "fakefile-10-minutes")), "10 minute download should now be deleted"); + ok((yield downloadExists(publicList, "fakefile-1-hour")), "<1 hour download should still be present"); + ok((yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "1 hour 10 minute download should still be present"); + ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present"); + ok((yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should still be present"); + ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present"); + ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present"); + ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present"); if (minutesSinceMidnight > 10) - ok(downloadExists(5555554), "'Today' download should still be present"); + ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present"); + + downloadPromise = promiseDownloadRemoved(publicList); // Clear 1 hour Sanitizer.prefs.setIntPref("timeSpan", 1); s.sanitize(); yield promiseFormHistoryRemoved(); + yield downloadPromise; ok(!(yield promiseIsURIVisited(makeURI("http://1hour.com"))), "Pretend visit to 1hour.com should now be deleted"); @@ -169,23 +192,26 @@ function onHistoryReady() { yield countEntries("today", "today form entry should still exist", checkOne); yield countEntries("b4today", "b4today form entry should still exist", checkOne); - ok(!downloadExists(5555551), "<1 hour download should now be deleted"); - ok(downloadExists(5555556), "1 hour 10 minute download should still be present"); - ok(downloadExists(5555550), "Year old download should still be present"); - ok(downloadExists(5555552), "<2 hour old download should still be present"); - ok(downloadExists(5555557), "2 hour 10 minute download should still be present"); - ok(downloadExists(5555553), "<4 hour old download should still be present"); - ok(downloadExists(5555558), "4 hour 10 minute download should still be present"); + ok(!(yield downloadExists(publicList, "fakefile-1-hour")), "<1 hour download should now be deleted"); + ok((yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "1 hour 10 minute download should still be present"); + ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present"); + ok((yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should still be present"); + ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present"); + ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present"); + ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present"); if (hoursSinceMidnight > 1) - ok(downloadExists(5555554), "'Today' download should still be present"); + ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present"); + downloadPromise = promiseDownloadRemoved(publicList); + // Clear 1 hour 10 minutes s.range = [now_uSec - 70*60*1000000, now_uSec]; s.sanitize(); s.range = null; yield promiseFormHistoryRemoved(); + yield downloadPromise; ok(!(yield promiseIsURIVisited(makeURI("http://1hour10minutes.com"))), "Pretend visit to 1hour10minutes.com should now be deleted"); @@ -213,20 +239,23 @@ function onHistoryReady() { yield countEntries("today", "today form entry should still exist", checkOne); yield countEntries("b4today", "b4today form entry should still exist", checkOne); - ok(!downloadExists(5555556), "1 hour 10 minute old download should now be deleted"); - ok(downloadExists(5555550), "Year old download should still be present"); - ok(downloadExists(5555552), "<2 hour old download should still be present"); - ok(downloadExists(5555557), "2 hour 10 minute download should still be present"); - ok(downloadExists(5555553), "<4 hour old download should still be present"); - ok(downloadExists(5555558), "4 hour 10 minute download should still be present"); + ok(!(yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "1 hour 10 minute old download should now be deleted"); + ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present"); + ok((yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should still be present"); + ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present"); + ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present"); + ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present"); if (minutesSinceMidnight > 70) - ok(downloadExists(5555554), "'Today' download should still be present"); + ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present"); + + downloadPromise = promiseDownloadRemoved(publicList); // Clear 2 hours Sanitizer.prefs.setIntPref("timeSpan", 2); s.sanitize(); yield promiseFormHistoryRemoved(); + yield downloadPromise; ok(!(yield promiseIsURIVisited(makeURI("http://2hour.com"))), "Pretend visit to 2hour.com should now be deleted"); @@ -251,20 +280,23 @@ function onHistoryReady() { yield countEntries("today", "today form entry should still exist", checkOne); yield countEntries("b4today", "b4today form entry should still exist", checkOne); - ok(!downloadExists(5555552), "<2 hour old download should now be deleted"); - ok(downloadExists(5555550), "Year old download should still be present"); - ok(downloadExists(5555557), "2 hour 10 minute download should still be present"); - ok(downloadExists(5555553), "<4 hour old download should still be present"); - ok(downloadExists(5555558), "4 hour 10 minute download should still be present"); + ok(!(yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should now be deleted"); + ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present"); + ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present"); + ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present"); + ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present"); if (hoursSinceMidnight > 2) - ok(downloadExists(5555554), "'Today' download should still be present"); + ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present"); + downloadPromise = promiseDownloadRemoved(publicList); + // Clear 2 hours 10 minutes s.range = [now_uSec - 130*60*1000000, now_uSec]; s.sanitize(); s.range = null; yield promiseFormHistoryRemoved(); + yield downloadPromise; ok(!(yield promiseIsURIVisited(makeURI("http://2hour10minutes.com"))), "Pretend visit to 2hour10minutes.com should now be deleted"); @@ -286,18 +318,21 @@ function onHistoryReady() { yield countEntries("today", "today form entry should still exist", checkOne); yield countEntries("b4today", "b4today form entry should still exist", checkOne); - ok(!downloadExists(5555557), "2 hour 10 minute old download should now be deleted"); - ok(downloadExists(5555553), "<4 hour old download should still be present"); - ok(downloadExists(5555558), "4 hour 10 minute download should still be present"); - ok(downloadExists(5555550), "Year old download should still be present"); + ok(!(yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute old download should now be deleted"); + ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present"); + ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present"); + ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present"); if (minutesSinceMidnight > 130) - ok(downloadExists(5555554), "'Today' download should still be present"); + ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present"); + + downloadPromise = promiseDownloadRemoved(publicList); // Clear 4 hours Sanitizer.prefs.setIntPref("timeSpan", 3); s.sanitize(); yield promiseFormHistoryRemoved(); + yield downloadPromise; ok(!(yield promiseIsURIVisited(makeURI("http://4hour.com"))), "Pretend visit to 4hour.com should now be deleted"); @@ -316,11 +351,13 @@ function onHistoryReady() { yield countEntries("today", "today form entry should still exist", checkOne); yield countEntries("b4today", "b4today form entry should still exist", checkOne); - ok(!downloadExists(5555553), "<4 hour old download should now be deleted"); - ok(downloadExists(5555558), "4 hour 10 minute download should still be present"); - ok(downloadExists(5555550), "Year old download should still be present"); + ok(!(yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should now be deleted"); + ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present"); + ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present"); if (hoursSinceMidnight > 4) - ok(downloadExists(5555554), "'Today' download should still be present"); + ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present"); + + downloadPromise = promiseDownloadRemoved(publicList); // Clear 4 hours 10 minutes s.range = [now_uSec - 250*60*1000000, now_uSec]; @@ -328,6 +365,7 @@ function onHistoryReady() { s.range = null; yield promiseFormHistoryRemoved(); + yield downloadPromise; ok(!(yield promiseIsURIVisited(makeURI("http://4hour10minutes.com"))), "Pretend visit to 4hour10minutes.com should now be deleted"); @@ -343,48 +381,60 @@ function onHistoryReady() { yield countEntries("today", "today form entry should still exist", checkOne); yield countEntries("b4today", "b4today form entry should still exist", checkOne); - ok(!downloadExists(5555558), "4 hour 10 minute download should now be deleted"); - ok(downloadExists(5555550), "Year old download should still be present"); + ok(!(yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should now be deleted"); + ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present"); if (minutesSinceMidnight > 250) - ok(downloadExists(5555554), "'Today' download should still be present"); + ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present"); + + // The 'Today' download might have been already deleted, in which case we + // should not wait for a download removal notification. + if (minutesSinceMidnight > 250) { + downloadPromise = promiseDownloadRemoved(publicList); + } else { + downloadPromise = Promise.resolve(); + } // Clear Today Sanitizer.prefs.setIntPref("timeSpan", 4); s.sanitize(); yield promiseFormHistoryRemoved(); + yield downloadPromise; // Be careful. If we add our objectss just before midnight, and sanitize // runs immediately after, they won't be expired. This is expected, but // we should not test in that case. We cannot just test for opposite // condition because we could cross midnight just one moment after we // cache our time, then we would have an even worse random failure. - var today = isToday(new Date(now_uSec/1000)); + var today = isToday(new Date(now_mSec)); if (today) { ok(!(yield promiseIsURIVisited(makeURI("http://today.com"))), "Pretend visit to today.com should now be deleted"); yield countEntries("today", "today form entry should be deleted", checkZero); - ok(!downloadExists(5555554), "'Today' download should now be deleted"); + ok(!(yield downloadExists(publicList, "fakefile-today")), "'Today' download should now be deleted"); } ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))), "Pretend visit to before-today.com should still exist"); yield countEntries("b4today", "b4today form entry should still exist", checkOne); - ok(downloadExists(5555550), "Year old download should still be present"); + ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present"); + + downloadPromise = promiseDownloadRemoved(publicList); // Choose everything Sanitizer.prefs.setIntPref("timeSpan", 0); s.sanitize(); yield promiseFormHistoryRemoved(); + yield downloadPromise; ok(!(yield promiseIsURIVisited(makeURI("http://before-today.com"))), "Pretend visit to before-today.com should now be deleted"); yield countEntries("b4today", "b4today form entry should be deleted", checkZero); - ok(!downloadExists(5555550), "Year old download should now be deleted"); + ok(!(yield downloadExists(publicList, "fakefile-old")), "Year old download should now be deleted"); } function setupHistory() { @@ -562,227 +612,103 @@ function setupFormHistory() { function setupDownloads() { - // Add 10-minutes download to DB - let data = { - id: "5555555", - name: "fakefile-10-minutes", - source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169", - target: "fakefile-10-minutes", - startTime: now_uSec - 10 * kUsecPerMin, // 10 minutes ago, in uSec - endTime: now_uSec - 11 * kUsecPerMin, // 1 minute later - state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED, - currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0, - guid: "a1bcD23eF4g5" - }; + let publicList = yield Downloads.getPublicDownloadList(); - let db = dm.DBConnection; - let stmt = db.createStatement( - "INSERT INTO moz_downloads (id, name, source, target, startTime, endTime, " + - "state, currBytes, maxBytes, preferredAction, autoResume, guid) " + - "VALUES (:id, :name, :source, :target, :startTime, :endTime, :state, " + - ":currBytes, :maxBytes, :preferredAction, :autoResume, :guid)"); - try { - for (let prop in data) - stmt.params[prop] = data[prop]; - stmt.execute(); - } - finally { - stmt.reset(); - } - - // Add within-1-hour download to DB - data = { - id: "5555551", - name: "fakefile-1-hour", + let download = yield Downloads.createDownload({ + source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169", + target: "fakefile-10-minutes" + }); + download.startTime = new Date(now_mSec - 10 * kMsecPerMin), // 10 minutes ago + download.canceled = true; + publicList.add(download); + + download = yield Downloads.createDownload({ source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440", - target: "fakefile-1-hour", - startTime: now_uSec - 45 * kUsecPerMin, // 45 minutes ago, in uSec - endTime: now_uSec - 44 * kUsecPerMin, // 1 minute later - state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED, - currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0, - guid: "1bcD23eF4g5a" - }; + target: "fakefile-1-hour" + }); + download.startTime = new Date(now_mSec - 45 * kMsecPerMin), // 45 minutes ago + download.canceled = true; + publicList.add(download); - try { - for (let prop in data) - stmt.params[prop] = data[prop]; - stmt.execute(); - } - finally { - stmt.reset(); - } - - // Add 1-hour-10-minutes download to DB - data = { - id: "5555556", - name: "fakefile-1-hour-10-minutes", + download = yield Downloads.createDownload({ source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169", - target: "fakefile-1-hour-10-minutes", - startTime: now_uSec - 70 * kUsecPerMin, // 70 minutes ago, in uSec - endTime: now_uSec - 71 * kUsecPerMin, // 1 minute later - state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED, - currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0, - guid: "a1cbD23e4Fg5" - }; + target: "fakefile-1-hour-10-minutes" + }); + download.startTime = new Date(now_mSec - 70 * kMsecPerMin), // 70 minutes ago + download.canceled = true; + publicList.add(download); - try { - for (let prop in data) - stmt.params[prop] = data[prop]; - stmt.execute(); - } - finally { - stmt.reset(); - } - - // Add within-2-hour download - data = { - id: "5555552", - name: "fakefile-2-hour", + download = yield Downloads.createDownload({ source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440", - target: "fakefile-2-hour", - startTime: now_uSec - 90 * kUsecPerMin, // 90 minutes ago, in uSec - endTime: now_uSec - 89 * kUsecPerMin, // 1 minute later - state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED, - currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0, - guid: "b1aDc23eFg54" - }; + target: "fakefile-2-hour" + }); + download.startTime = new Date(now_mSec - 90 * kMsecPerMin), // 90 minutes ago + download.canceled = true; + publicList.add(download); - try { - for (let prop in data) - stmt.params[prop] = data[prop]; - stmt.execute(); - } - finally { - stmt.reset(); - } - - // Add 2-hour-10-minutes download - data = { - id: "5555557", - name: "fakefile-2-hour-10-minutes", + download = yield Downloads.createDownload({ source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169", - target: "fakefile-2-hour-10-minutes", - startTime: now_uSec - 130 * kUsecPerMin, // 130 minutes ago, in uSec - endTime: now_uSec - 131 * kUsecPerMin, // 1 minute later - state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED, - currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0, - guid: "z1bcD23eF4g5" - }; + target: "fakefile-2-hour-10-minutes" + }); + download.startTime = new Date(now_mSec - 130 * kMsecPerMin), // 130 minutes ago + download.canceled = true; + publicList.add(download); - try { - for (let prop in data) - stmt.params[prop] = data[prop]; - stmt.execute(); - } - finally { - stmt.reset(); - } - - // Add within-4-hour download - data = { - id: "5555553", - name: "fakefile-4-hour", + download = yield Downloads.createDownload({ source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440", - target: "fakefile-4-hour", - startTime: now_uSec - 180 * kUsecPerMin, // 180 minutes ago, in uSec - endTime: now_uSec - 179 * kUsecPerMin, // 1 minute later - state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED, - currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0, - guid: "zzzcD23eF4g5" - }; - - try { - for (let prop in data) - stmt.params[prop] = data[prop]; - stmt.execute(); - } - finally { - stmt.reset(); - } - - // Add 4-hour-10-minutes download - data = { - id: "5555558", - name: "fakefile-4-hour-10-minutes", + target: "fakefile-4-hour" + }); + download.startTime = new Date(now_mSec - 180 * kMsecPerMin), // 180 minutes ago + download.canceled = true; + publicList.add(download); + + download = yield Downloads.createDownload({ source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169", - target: "fakefile-4-hour-10-minutes", - startTime: now_uSec - 250 * kUsecPerMin, // 250 minutes ago, in uSec - endTime: now_uSec - 251 * kUsecPerMin, // 1 minute later - state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED, - currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0, - guid: "z1bzz23eF4gz" - }; - - try { - for (let prop in data) - stmt.params[prop] = data[prop]; - stmt.execute(); - } - finally { - stmt.reset(); - } + target: "fakefile-4-hour-10-minutes" + }); + download.startTime = new Date(now_mSec - 250 * kMsecPerMin), // 250 minutes ago + download.canceled = true; + publicList.add(download); // Add "today" download let today = new Date(); today.setHours(0); today.setMinutes(0); today.setSeconds(1); - - data = { - id: "5555554", - name: "fakefile-today", + + download = yield Downloads.createDownload({ source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440", - target: "fakefile-today", - startTime: today.getTime() * 1000, // 12:00:30am this morning, in uSec - endTime: (today.getTime() + 1000) * 1000, // 1 second later - state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED, - currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0, - guid: "ffffD23eF4g5" - }; - - try { - for (let prop in data) - stmt.params[prop] = data[prop]; - stmt.execute(); - } - finally { - stmt.reset(); - } + target: "fakefile-today" + }); + download.startTime = today, // 12:00:01 AM this morning + download.canceled = true; + publicList.add(download); // Add "before today" download let lastYear = new Date(); lastYear.setFullYear(lastYear.getFullYear() - 1); - data = { - id: "5555550", - name: "fakefile-old", + + download = yield Downloads.createDownload({ source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440", - target: "fakefile-old", - startTime: lastYear.getTime() * 1000, // 1 year ago, in uSec - endTime: (lastYear.getTime() + 1000) * 1000, // 1 second later - state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED, - currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0, - guid: "ggggg23eF4g5" - }; - - try { - for (let prop in data) - stmt.params[prop] = data[prop]; - stmt.execute(); - } - finally { - stmt.finalize(); - } + target: "fakefile-old" + }); + download.startTime = lastYear, + download.canceled = true; + publicList.add(download); // Confirm everything worked - ok(downloadExists(5555550), "Pretend download for everything case should exist"); - ok(downloadExists(5555555), "Pretend download for 10-minutes case should exist"); - ok(downloadExists(5555551), "Pretend download for 1-hour case should exist"); - ok(downloadExists(5555556), "Pretend download for 1-hour-10-minutes case should exist"); - ok(downloadExists(5555552), "Pretend download for 2-hour case should exist"); - ok(downloadExists(5555557), "Pretend download for 2-hour-10-minutes case should exist"); - ok(downloadExists(5555553), "Pretend download for 4-hour case should exist"); - ok(downloadExists(5555558), "Pretend download for 4-hour-10-minutes case should exist"); - ok(downloadExists(5555554), "Pretend download for Today case should exist"); + let downloads = yield publicList.getAll(); + is(downloads.length, 9, "9 Pretend downloads added"); + + ok((yield downloadExists(publicList, "fakefile-old")), "Pretend download for everything case should exist"); + ok((yield downloadExists(publicList, "fakefile-10-minutes")), "Pretend download for 10-minutes case should exist"); + ok((yield downloadExists(publicList, "fakefile-1-hour")), "Pretend download for 1-hour case should exist"); + ok((yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "Pretend download for 1-hour-10-minutes case should exist"); + ok((yield downloadExists(publicList, "fakefile-2-hour")), "Pretend download for 2-hour case should exist"); + ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "Pretend download for 2-hour-10-minutes case should exist"); + ok((yield downloadExists(publicList, "fakefile-4-hour")), "Pretend download for 4-hour case should exist"); + ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "Pretend download for 4-hour-10-minutes case should exist"); + ok((yield downloadExists(publicList, "fakefile-today")), "Pretend download for Today case should exist"); } /** @@ -791,18 +717,12 @@ function setupDownloads() { * @param aID * The ids of the downloads to check. */ -function downloadExists(aID) +function downloadExists(list, path) { - let db = dm.DBConnection; - let stmt = db.createStatement( - "SELECT * " + - "FROM moz_downloads " + - "WHERE id = :id" - ); - stmt.params.id = aID; - var rows = stmt.executeStep(); - stmt.finalize(); - return rows; + return Task.spawn(function() { + let listArray = yield list.getAll(); + throw new Task.Result(listArray.some(i => i.target.path == path)); + }); } function isToday(aDate) { diff --git a/browser/base/content/test/browser_sanitizeDialog.js b/browser/base/content/test/browser_sanitizeDialog.js index 8f87b61a114..7f4e75d317a 100644 --- a/browser/base/content/test/browser_sanitizeDialog.js +++ b/browser/base/content/test/browser_sanitizeDialog.js @@ -21,18 +21,18 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "FormHistory", "resource://gre/modules/FormHistory.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Downloads", + "resource://gre/modules/Downloads.jsm"); let tempScope = {}; Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader) .loadSubScript("chrome://browser/content/sanitize.js", tempScope); let Sanitizer = tempScope.Sanitizer; -const dm = Cc["@mozilla.org/download-manager;1"]. - getService(Ci.nsIDownloadManager); - +const kMsecPerMin = 60 * 1000; const kUsecPerMin = 60 * 1000000; -let formEntries; +let formEntries, downloadIDs, olderDownloadIDs; // Add tests here. Each is a function that's called by doNextTest(). var gAllTests = [ @@ -92,6 +92,23 @@ var gAllTests = [ }); }, + function () { + // Add downloads (within the past hour). + Task.spawn(function () { + downloadIDs = []; + for (let i = 0; i < 5; i++) { + yield addDownloadWithMinutesAgo(downloadIDs, i); + } + // Add downloads (over an hour ago). + olderDownloadIDs = []; + for (let i = 0; i < 5; i++) { + yield addDownloadWithMinutesAgo(olderDownloadIDs, 61 + i); + } + + doNextTest(); + }).then(null, Components.utils.reportError); + }, + /** * Ensures that the combined history-downloads checkbox clears both history * visits and downloads when checked; the dialog respects simple timespan. @@ -115,16 +132,6 @@ var gAllTests = [ } addVisits(places, function() { - // Add downloads (within the past hour). - let downloadIDs = []; - for (let i = 0; i < 5; i++) { - downloadIDs.push(addDownloadWithMinutesAgo(i)); - } - // Add downloads (over an hour ago). - let olderDownloadIDs = []; - for (let i = 0; i < 5; i++) { - olderDownloadIDs.push(addDownloadWithMinutesAgo(61 + i)); - } let totalHistoryVisits = uris.length + olderURIs.length; let wh = new WindowHelper(); @@ -146,16 +153,16 @@ var gAllTests = [ wh.onunload = function () { // History visits and downloads within one hour should be cleared. yield promiseHistoryClearedState(uris, true); - ensureDownloadsClearedState(downloadIDs, true); + yield ensureDownloadsClearedState(downloadIDs, true); // Visits and downloads > 1 hour should still exist. yield promiseHistoryClearedState(olderURIs, false); - ensureDownloadsClearedState(olderDownloadIDs, false); + yield ensureDownloadsClearedState(olderDownloadIDs, false); // OK, done, cleanup after ourselves. yield blankSlate(); yield promiseHistoryClearedState(olderURIs, true); - ensureDownloadsClearedState(olderDownloadIDs, true); + yield ensureDownloadsClearedState(olderDownloadIDs, true); }; wh.open(); }); @@ -178,6 +185,18 @@ var gAllTests = [ iter.next(); }, + function () { + // Add downloads (within the past hour). + Task.spawn(function () { + downloadIDs = []; + for (let i = 0; i < 5; i++) { + yield addDownloadWithMinutesAgo(downloadIDs, i); + } + + doNextTest(); + }).then(null, Components.utils.reportError); + }, + /** * Ensures that the combined history-downloads checkbox removes neither * history visits nor downloads when not checked. @@ -194,11 +213,6 @@ var gAllTests = [ } addVisits(places, function() { - let downloadIDs = []; - for (let i = 0; i < 5; i++) { - downloadIDs.push(addDownloadWithMinutesAgo(i)); - } - let wh = new WindowHelper(); wh.onload = function () { is(this.isWarningPanelVisible(), false, @@ -224,7 +238,7 @@ var gAllTests = [ wh.onunload = function () { // Of the three only form entries should be cleared. yield promiseHistoryClearedState(uris, false); - ensureDownloadsClearedState(downloadIDs, false); + yield ensureDownloadsClearedState(downloadIDs, false); formEntries.forEach(function (entry) { let exists = yield formNameExists(entry); @@ -234,7 +248,7 @@ var gAllTests = [ // OK, done, cleanup after ourselves. yield blankSlate(); yield promiseHistoryClearedState(uris, true); - ensureDownloadsClearedState(downloadIDs, true); + yield ensureDownloadsClearedState(downloadIDs, true); }; wh.open(); }); @@ -639,15 +653,12 @@ var gAllTests = [ } ]; -// Used as the download database ID for a new download. Incremented for each -// new download. See addDownloadWithMinutesAgo(). -var gDownloadId = 5555551; - // Index in gAllTests of the test currently being run. Incremented for each // test run. See doNextTest(). var gCurrTest = 0; -var now_uSec = Date.now() * 1000; +let now_mSec = Date.now(); +let now_uSec = now_mSec * 1000; /////////////////////////////////////////////////////////////////////////////// @@ -847,7 +858,7 @@ WindowHelper.prototype = { if (wh.onunload) { Task.spawn(wh.onunload).then(function() { waitForAsyncUpdates(doNextTest); - }); + }).then(null, Components.utils.reportError); } else { waitForAsyncUpdates(doNextTest); } @@ -900,40 +911,23 @@ WindowHelper.prototype = { * @param aMinutesAgo * The download will be downloaded this many minutes ago */ -function addDownloadWithMinutesAgo(aMinutesAgo) { +function addDownloadWithMinutesAgo(aExpectedPathList, aMinutesAgo) { + let publicList = yield Downloads.getPublicDownloadList(); + let name = "fakefile-" + aMinutesAgo + "-minutes-ago"; - let data = { - id: gDownloadId, - name: name, - source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169", - target: name, - startTime: now_uSec - (aMinutesAgo * kUsecPerMin), - endTime: now_uSec - ((aMinutesAgo + 1) * kUsecPerMin), - state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED, - currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0 - }; + let download = yield Downloads.createDownload({ + source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169", + target: name + }); + download.startTime = new Date(now_mSec - (aMinutesAgo * kMsecPerMin)); + download.canceled = true; + publicList.add(download); - let db = dm.DBConnection; - let stmt = db.createStatement( - "INSERT INTO moz_downloads (id, name, source, target, startTime, endTime, " + - "state, currBytes, maxBytes, preferredAction, autoResume) " + - "VALUES (:id, :name, :source, :target, :startTime, :endTime, :state, " + - ":currBytes, :maxBytes, :preferredAction, :autoResume)"); - try { - for (let prop in data) { - stmt.params[prop] = data[prop]; - } - stmt.execute(); - } - finally { - stmt.reset(); - } - - is(downloadExists(gDownloadId), true, - "Sanity check: download " + gDownloadId + + ok((yield downloadExists(name)), + "Sanity check: download " + name + " should exist after creating it"); - return gDownloadId++; + aExpectedPathList.push(name); } /** @@ -984,15 +978,37 @@ function formNameExists(name) */ function blankSlate() { PlacesUtils.bhistory.removeAllPages(); - dm.cleanUp(); + // The promise is resolved only when removing both downloads and form history are done. let deferred = Promise.defer(); + let formHistoryDone = false, downloadsDone = false; + + Task.spawn(function deleteAllDownloads() { + let publicList = yield Downloads.getPublicDownloadList(); + let downloads = yield publicList.getAll(); + for (let download of downloads) { + publicList.remove(download); + yield download.finalize(true); + } + downloadsDone = true; + if (formHistoryDone) { + deferred.resolve(); + } + }).then(null, Components.utils.reportError); + FormHistory.update({ op: "remove" }, { handleError: function (error) { do_throw("Error occurred updating form history: " + error); deferred.reject(error); }, - handleCompletion: function (reason) { if (!reason) deferred.resolve(); } + handleCompletion: function (reason) { + if (!reason) { + formHistoryDone = true; + if (downloadsDone) { + deferred.resolve(); + } + } + } }); return deferred.promise; } @@ -1012,24 +1028,19 @@ function boolPrefIs(aPrefName, aExpectedVal, aMsg) { } /** - * Checks to see if the download with the specified ID exists. + * Checks to see if the download with the specified path exists. * - * @param aID - * The ID of the download to check + * @param aPath + * The path of the download to check * @return True if the download exists, false otherwise */ -function downloadExists(aID) +function downloadExists(aPath) { - let db = dm.DBConnection; - let stmt = db.createStatement( - "SELECT * " + - "FROM moz_downloads " + - "WHERE id = :id" - ); - stmt.params.id = aID; - let rows = stmt.executeStep(); - stmt.finalize(); - return !!rows; + return Task.spawn(function() { + let publicList = yield Downloads.getPublicDownloadList(); + let listArray = yield publicList.getAll(); + throw new Task.Result(listArray.some(i => i.target.path == aPath)); + }); } /** @@ -1059,7 +1070,7 @@ function doNextTest() { function ensureDownloadsClearedState(aDownloadIDs, aShouldBeCleared) { let niceStr = aShouldBeCleared ? "no longer" : "still"; aDownloadIDs.forEach(function (id) { - is(downloadExists(id), !aShouldBeCleared, + is((yield downloadExists(id)), !aShouldBeCleared, "download " + id + " should " + niceStr + " exist"); }); } From 8012e1cebeabc3875b779ae611e77ac358439ca9 Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Wed, 21 Aug 2013 09:36:07 +0200 Subject: [PATCH 25/54] Bug 847863 - Part 7 of 8 - Convert Downloads Panel tests. r=enn --- .../browser/browser_basic_functionality.js | 48 ++- .../browser/browser_first_download_panel.js | 53 ++-- .../components/downloads/test/browser/head.js | 286 ++++-------------- 3 files changed, 107 insertions(+), 280 deletions(-) diff --git a/browser/components/downloads/test/browser/browser_basic_functionality.js b/browser/components/downloads/test/browser/browser_basic_functionality.js index b1a8f6595e5..1df28aa7c7f 100644 --- a/browser/components/downloads/test/browser/browser_basic_functionality.js +++ b/browser/components/downloads/test/browser/browser_basic_functionality.js @@ -7,39 +7,33 @@ * Make sure the downloads panel can display items in the right order and * contains the expected data. */ -function gen_test() +function test_task() { // Display one of each download state. const DownloadData = [ - { endTime: 1180493839859239, state: nsIDM.DOWNLOAD_NOTSTARTED }, - { endTime: 1180493839859238, state: nsIDM.DOWNLOAD_DOWNLOADING }, - { endTime: 1180493839859237, state: nsIDM.DOWNLOAD_PAUSED }, - { endTime: 1180493839859236, state: nsIDM.DOWNLOAD_SCANNING }, - { endTime: 1180493839859235, state: nsIDM.DOWNLOAD_QUEUED }, - { endTime: 1180493839859234, state: nsIDM.DOWNLOAD_FINISHED }, - { endTime: 1180493839859233, state: nsIDM.DOWNLOAD_FAILED }, - { endTime: 1180493839859232, state: nsIDM.DOWNLOAD_CANCELED }, - { endTime: 1180493839859231, state: nsIDM.DOWNLOAD_BLOCKED_PARENTAL }, - { endTime: 1180493839859230, state: nsIDM.DOWNLOAD_DIRTY }, - { endTime: 1180493839859229, state: nsIDM.DOWNLOAD_BLOCKED_POLICY }, + { state: nsIDM.DOWNLOAD_NOTSTARTED }, + { state: nsIDM.DOWNLOAD_PAUSED }, + { state: nsIDM.DOWNLOAD_FINISHED }, + { state: nsIDM.DOWNLOAD_FAILED }, + { state: nsIDM.DOWNLOAD_CANCELED }, ]; - // For testing purposes, show all the download items at once. - var originalCountLimit = DownloadsView.kItemCountLimit; - DownloadsView.kItemCountLimit = DownloadData.length; - registerCleanupFunction(function () { - DownloadsView.kItemCountLimit = originalCountLimit; - }); - try { // Ensure that state is reset in case previous tests didn't finish. - for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined; + yield task_resetState(); + + // For testing purposes, show all the download items at once. + var originalCountLimit = DownloadsView.kItemCountLimit; + DownloadsView.kItemCountLimit = DownloadData.length; + registerCleanupFunction(function () { + DownloadsView.kItemCountLimit = originalCountLimit; + }); // Populate the downloads database with the data required by this test. - for (let yy in gen_addDownloadRows(DownloadData)) yield undefined; + yield task_addDownloads(DownloadData); // Open the user interface and wait for data to be fully loaded. - for (let yy in gen_openPanel(DownloadsCommon.getData(window))) yield undefined; + yield task_openPanel(); // Test item data and count. This also tests the ordering of the display. let richlistbox = document.getElementById("downloadsListBox"); @@ -47,16 +41,14 @@ function gen_test() is(richlistbox.children.length, DownloadData.length, "There is the correct number of richlistitems"); */ - for (let i = 0; i < richlistbox.children.length; i++) { - let element = richlistbox.children[i]; + let itemCount = richlistbox.children.length; + for (let i = 0; i < itemCount; i++) { + let element = richlistbox.children[itemCount - i - 1]; let dataItem = new DownloadsViewItemController(element).dataItem; - is(dataItem.target, DownloadData[i].name, "Download names match up"); is(dataItem.state, DownloadData[i].state, "Download states match up"); - is(dataItem.file, DownloadData[i].target, "Download targets match up"); - is(dataItem.uri, DownloadData[i].source, "Download sources match up"); } } finally { // Clean up when the test finishes. - for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined; + yield task_resetState(); } } diff --git a/browser/components/downloads/test/browser/browser_first_download_panel.js b/browser/components/downloads/test/browser/browser_first_download_panel.js index ed1885ba3f7..1faeb6c7e33 100644 --- a/browser/components/downloads/test/browser/browser_first_download_panel.js +++ b/browser/components/downloads/test/browser/browser_first_download_panel.js @@ -8,19 +8,19 @@ * download it notices. All subsequent downloads, even across sessions, should * not open the panel automatically. */ -function gen_test() +function test_task() { try { // Ensure that state is reset in case previous tests didn't finish. - for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined; + yield task_resetState(); - // With this set to false, we should automatically open the panel - // the first time a download is started. + // With this set to false, we should automatically open the panel the first + // time a download is started. DownloadsCommon.getData(window).panelHasShownBefore = false; - prepareForPanelOpen(); + let promise = promisePanelOpened(); DownloadsCommon.getData(window)._notifyDownloadEvent("start"); - yield undefined; + yield promise; // If we got here, that means the panel opened. DownloadsPanel.hidePanel(); @@ -28,29 +28,26 @@ function gen_test() ok(DownloadsCommon.getData(window).panelHasShownBefore, "Should have recorded that the panel was opened on a download.") - // Next, make sure that if we start another download, we don't open - // the panel automatically. - panelShouldNotOpen(); - DownloadsCommon.getData(window)._notifyDownloadEvent("start"); - yield waitFor(2); - } catch(e) { - ok(false, e); + // Next, make sure that if we start another download, we don't open the + // panel automatically. + let originalOnPopupShown = DownloadsPanel.onPopupShown; + DownloadsPanel.onPopupShown = function () { + originalOnPopupShown.apply(this, arguments); + ok(false, "Should not have opened the downloads panel."); + }; + + try { + DownloadsCommon.getData(window)._notifyDownloadEvent("start"); + + // Wait 2 seconds to ensure that the panel does not open. + let deferTimeout = Promise.defer(); + setTimeout(deferTimeout.resolve, 2000); + yield deferTimeout.promise; + } finally { + DownloadsPanel.onPopupShown = originalOnPopupShown; + } } finally { // Clean up when the test finishes. - for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined; + yield task_resetState(); } } - -/** - * Call this to record a test failure for the next time the downloads panel - * opens. - */ -function panelShouldNotOpen() -{ - // Hook to wait until the test data has been loaded. - let originalOnViewLoadCompleted = DownloadsPanel.onViewLoadCompleted; - DownloadsPanel.onViewLoadCompleted = function () { - DownloadsPanel.onViewLoadCompleted = originalOnViewLoadCompleted; - ok(false, "Should not have opened the downloads panel."); - }; -} diff --git a/browser/components/downloads/test/browser/head.js b/browser/components/downloads/test/browser/head.js index 7e4d857c818..35526334ca1 100644 --- a/browser/components/downloads/test/browser/head.js +++ b/browser/components/downloads/test/browser/head.js @@ -10,10 +10,16 @@ //////////////////////////////////////////////////////////////////////////////// //// Globals -XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", - "resource://gre/modules/FileUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Downloads", + "resource://gre/modules/Downloads.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", "resource:///modules/DownloadsCommon.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", + "resource://gre/modules/FileUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Promise", + "resource://gre/modules/Promise.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Task", + "resource://gre/modules/Task.jsm"); const nsIDM = Ci.nsIDownloadManager; let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]); @@ -22,253 +28,85 @@ registerCleanupFunction(function () { gTestTargetFile.remove(false); }); -/** - * This objects contains a property for each column in the downloads table. - */ -let gDownloadRowTemplate = { - name: "test-download.txt", - source: "http://www.example.com/test-download.txt", - target: NetUtil.newURI(gTestTargetFile).spec, - startTime: 1180493839859230, - endTime: 1180493839859234, - state: nsIDM.DOWNLOAD_FINISHED, - currBytes: 0, - maxBytes: -1, - preferredAction: 0, - autoResume: 0 -}; - //////////////////////////////////////////////////////////////////////////////// //// Infrastructure -// All test are run through the test runner. function test() { - testRunner.runTest(this.gen_test); + waitForExplicitFinish(); + Task.spawn(test_task).then(null, ex => ok(false, ex)).then(finish); } -/** - * Runs a browser-chrome test defined through a generator function. - * - * This object is a singleton, initialized automatically when this script is - * included. Every browser-chrome test file includes a new copy of this object. - */ -var testRunner = { - _testIterator: null, - _lastEventResult: undefined, - _testRunning: false, - _eventRaised: false, - - // --- Main test runner --- - - /** - * Runs the test described by the provided generator function asynchronously. - * - * Calling yield in the generator will cause it to wait until continueTest is - * called. The parameter provided to continueTest will be the return value of - * the yield operator. - * - * @param aGenerator - * Test generator function. The function will be called with no - * arguments to retrieve its iterator. - */ - runTest: function TR_runTest(aGenerator) { - waitForExplicitFinish(); - testRunner._testIterator = aGenerator(); - testRunner.continueTest(); - }, - - /** - * Continues the currently running test. - * - * @param aEventResult - * This will be the return value of the yield operator in the test. - */ - continueTest: function TR_continueTest(aEventResult) { - // Store the last event result, or set it to undefined. - testRunner._lastEventResult = aEventResult; - - // Never reenter the main loop, but notify that the event has been raised. - if (testRunner._testRunning) { - testRunner._eventRaised = true; - return; - } - - // Enter the main iteration loop. - testRunner._testRunning = true; - try { - do { - // Call the iterator, but don't leave the loop if the expected event is - // raised during the execution of the generator. - testRunner._eventRaised = false; - testRunner._testIterator.send(testRunner._lastEventResult); - } while (testRunner._eventRaised); - } - catch (e) { - // This block catches exceptions raised by the generator, including the - // normal StopIteration exception. Unexpected exceptions are reported as - // test failures. - if (!(e instanceof StopIteration)) - ok(false, e); - // In any case, stop the tests in this file. - finish(); - } - - // Wait for the next event or finish. - testRunner._testRunning = false; - } -}; - //////////////////////////////////////////////////////////////////////////////// -//// Asynchronous generator-based support subroutines +//// Asynchronous support subroutines -// -// The following functions are all generators that can be used inside the main -// test generator to perform specific tasks asynchronously. To invoke these -// subroutines correctly, an iteration syntax should be used: -// -// for (let yy in gen_example("Parameter")) yield undefined; -// - -function gen_resetState(aData) +function promiseFocus() { - let statement = Services.downloads.DBConnection.createAsyncStatement( - "DELETE FROM moz_downloads"); - try { - statement.executeAsync({ - handleResult: function(aResultSet) { }, - handleError: function(aError) - { - Cu.reportError(aError); - }, - handleCompletion: function(aReason) - { - testRunner.continueTest(); - } - }); - yield undefined; - } finally { - statement.finalize(); + let deferred = Promise.defer(); + waitForFocus(deferred.resolve); + return deferred.promise; +} + +function promisePanelOpened() +{ + let deferred = Promise.defer(); + + // Hook to wait until the panel is shown. + let originalOnPopupShown = DownloadsPanel.onPopupShown; + DownloadsPanel.onPopupShown = function () { + DownloadsPanel.onPopupShown = originalOnPopupShown; + originalOnPopupShown.apply(this, arguments); + + // Defer to the next tick of the event loop so that we don't continue + // processing during the DOM event handler itself. + setTimeout(deferred.resolve, 0); + }; + + return deferred.promise; +} + +function task_resetState() +{ + // Remove all downloads. + let publicList = yield Downloads.getPublicDownloadList(); + let downloads = yield publicList.getAll(); + for (let download of downloads) { + publicList.remove(download); + yield download.finalize(true); } // Reset any prefs that might have been changed. Services.prefs.clearUserPref("browser.download.panel.shown"); - // Ensure that the panel is closed and data is unloaded. - aData.clear(); - aData._loadState = aData.kLoadNone; DownloadsPanel.hidePanel(); - // Wait for focus on the main window. - waitForFocus(testRunner.continueTest); - yield undefined; + yield promiseFocus(); } -function gen_addDownloadRows(aDataRows) +function task_addDownloads(aItems) { - let columnNames = Object.keys(gDownloadRowTemplate).join(", "); - let parameterNames = Object.keys(gDownloadRowTemplate) - .map(function(n) ":" + n) - .join(", "); - let statement = Services.downloads.DBConnection.createAsyncStatement( - "INSERT INTO moz_downloads (" + columnNames + - ", guid) VALUES(" + parameterNames + ", GENERATE_GUID())"); - try { - // Execute the statement for each of the provided downloads in reverse. - for (let i = aDataRows.length - 1; i >= 0; i--) { - let dataRow = aDataRows[i]; + let startTimeMs = Date.now(); - // Populate insert parameters from the provided data. - for (let columnName in gDownloadRowTemplate) { - if (!(columnName in dataRow)) { - // Update the provided row object with data from the global template, - // for columns whose value is not provided explicitly. - dataRow[columnName] = gDownloadRowTemplate[columnName]; - } - statement.params[columnName] = dataRow[columnName]; - } - - // Run the statement asynchronously and wait. - statement.executeAsync({ - handleResult: function(aResultSet) { }, - handleError: function(aError) - { - Cu.reportError(aError.message + " (Result = " + aError.result + ")"); - }, - handleCompletion: function(aReason) - { - testRunner.continueTest(); - } - }); - yield undefined; - - // At each iteration, ensure that the start and end time in the global - // template is distinct, as these column are used to sort each download - // in its category. - gDownloadRowTemplate.startTime++; - gDownloadRowTemplate.endTime++; - } - } finally { - statement.finalize(); + let publicList = yield Downloads.getPublicDownloadList(); + for (let item of aItems) { + publicList.add(yield Downloads.createDownload({ + source: "http://www.example.com/test-download.txt", + target: gTestTargetFile, + succeeded: item.state == nsIDM.DOWNLOAD_FINISHED, + canceled: item.state == nsIDM.DOWNLOAD_CANCELED || + item.state == nsIDM.DOWNLOAD_PAUSED, + error: item.state == nsIDM.DOWNLOAD_FAILED ? new Error("Failed.") : null, + hasPartialData: item.state == nsIDM.DOWNLOAD_PAUSED, + startTime: new Date(startTimeMs++), + })); } } -function gen_openPanel(aData) +function task_openPanel() { - // Hook to wait until the test data has been loaded. - let originalOnViewLoadCompleted = DownloadsPanel.onViewLoadCompleted; - DownloadsPanel.onViewLoadCompleted = function () { - DownloadsPanel.onViewLoadCompleted = originalOnViewLoadCompleted; - originalOnViewLoadCompleted.apply(this); - testRunner.continueTest(); - }; + yield promiseFocus(); - // Start loading all the downloads from the database asynchronously. - aData.ensurePersistentDataLoaded(false); - - // Wait for focus on the main window. - waitForFocus(testRunner.continueTest); - yield undefined; - - // Open the downloads panel, waiting until loading is completed. + let promise = promisePanelOpened(); DownloadsPanel.showPanel(); - yield undefined; -} - -/** - * Spin the event loop for aSeconds seconds, and then signal the test to - * continue. - * - * @param aSeconds the number of seconds to wait. - * @note This helper should _only_ be used when there's no valid event to - * listen to and one can't be made. - */ -function waitFor(aSeconds) -{ - setTimeout(function() { - testRunner.continueTest(); - }, aSeconds * 1000); -} - -/** - * Make it so that the next time the downloads panel opens, we signal to - * continue the test. This function needs to be called each time you want - * to wait for the panel to open. - * - * Example usage: - * - * prepareForPanelOpen(); - * // Do something to open the panel - * yield undefined; - * // We can assume the panel is open now. - */ -function prepareForPanelOpen() -{ - // Hook to wait until the test data has been loaded. - let originalOnPopupShown = DownloadsPanel.onPopupShown; - DownloadsPanel.onPopupShown = function (aEvent) { - DownloadsPanel.onPopupShown = originalOnPopupShown; - DownloadsPanel.onPopupShown.apply(this, [aEvent]); - testRunner.continueTest(); - }; + yield promise; } From 2173853e0fea183470a781f88941fee37d587d9d Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Wed, 21 Aug 2013 09:36:07 +0200 Subject: [PATCH 26/54] Bug 847863 - Part 8 of 8 - Use the JavaScript API instead of nsIDownloadManager as the back-end for the panel. r=enn --- browser/app/profile/firefox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 6dadc853ef3..801ccf845f7 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -336,7 +336,7 @@ pref("browser.download.manager.scanWhenDone", true); pref("browser.download.manager.resumeOnWakeDelay", 10000); // Enables the asynchronous Downloads API in the Downloads Panel. -pref("browser.download.useJSTransfer", false); +pref("browser.download.useJSTransfer", true); // This allows disabling the Downloads Panel in favor of the old interface. pref("browser.download.useToolkitUI", false); From 2541bd731dbac23602e182badbceb181bb1636b1 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Wed, 21 Aug 2013 18:03:07 +0200 Subject: [PATCH 27/54] Bug 907254 - reftest.js doesnt need anymore to avoid using GfxInfo on B2G - r=jrmuizel --- layout/tools/reftest/reftest.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/layout/tools/reftest/reftest.js b/layout/tools/reftest/reftest.js index 33835df9e5b..41305e0ccad 100644 --- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -538,14 +538,6 @@ function BuildConditionSandbox(aURL) { sandbox.smallScreen = true; } -#if REFTEST_B2G - // XXX nsIGfxInfo isn't available in B2G - sandbox.d2d = false; - sandbox.azureQuartz = false; - sandbox.azureSkia = false; - sandbox.azureSkiaGL = false; - sandbox.contentSameGfxBackendAsCanvas = false; -#else var gfxInfo = (NS_GFXINFO_CONTRACTID in CC) && CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo); try { sandbox.d2d = gfxInfo.D2DEnabled; @@ -559,7 +551,6 @@ function BuildConditionSandbox(aURL) { // true if we are using the same Azure backend for rendering canvas and content sandbox.contentSameGfxBackendAsCanvas = info.AzureContentBackend == info.AzureCanvasBackend || (info.AzureContentBackend == "none" && info.AzureCanvasBackend == "cairo"); -#endif sandbox.layersGPUAccelerated = gWindowUtils.layerManagerType != "Basic"; From 6fbe65da847c5d5c28a0694ace463b6ea74fc41a Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Wed, 21 Aug 2013 18:03:07 +0200 Subject: [PATCH 28/54] Bug 907256 - Adjust the canvas reftest.list for SkiaGL/B2G failures - r=jrmuizel --- layout/reftests/canvas/reftest.list | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/layout/reftests/canvas/reftest.list b/layout/reftests/canvas/reftest.list index d294a44ab50..b557068d5e8 100644 --- a/layout/reftests/canvas/reftest.list +++ b/layout/reftests/canvas/reftest.list @@ -25,8 +25,8 @@ skip-if(B2G) asserts-if(cocoaWidget,0-2) == size-change-1.html size-change-1-ref == text-ltr-alignment-test.html text-ltr-alignment-ref.html == text-rtl-alignment-test.html text-rtl-alignment-ref.html -== text-horzline-with-bottom.html text-horzline.html -== text-horzline-with-top.html text-horzline.html +fuzzy-if(B2G&&azureSkiaGL,1,256) == text-horzline-with-bottom.html text-horzline.html +fuzzy-if(B2G&&azureSkiaGL,1,256) == text-horzline-with-top.html text-horzline.html != text-big-stroke.html text-blank.html != text-big-stroke.html text-big-fill.html From 1c892804b26e5fe9302c22f001fbe2e0890bee6c Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Wed, 21 Aug 2013 18:03:07 +0200 Subject: [PATCH 29/54] Bug 907723 - Add some fuzz for B2G to SVG reftest.list - r=jrmuizel --- layout/reftests/svg/reftest.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index 84206c319e5..b059633aea4 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -200,7 +200,7 @@ pref(layout.css.masking.enabled,true) fuzzy-if(d2d,1,6400) == mask-type-04.svg m == nested-viewBox-01.svg pass.svg == nesting-invalid-01.svg nesting-invalid-01-ref.svg == non-scaling-stroke-01.svg non-scaling-stroke-01-ref.svg -fuzzy-if(Android,1,99) fuzzy-if(!contentSameGfxBackendAsCanvas,9,99) == non-scaling-stroke-02.svg non-scaling-stroke-02-ref.svg +fuzzy-if(Android||B2G,1,99) fuzzy-if(!contentSameGfxBackendAsCanvas,9,99) == non-scaling-stroke-02.svg non-scaling-stroke-02-ref.svg == non-scaling-stroke-03.svg non-scaling-stroke-03-ref.svg == objectBoundingBox-and-clipPath.svg pass.svg # Bug 588684 From 7a530a9ac4d9d890639bb824f6fbf7548636773a Mon Sep 17 00:00:00 2001 From: Dan Minor Date: Wed, 21 Aug 2013 08:20:24 -0400 Subject: [PATCH 30/54] Bug 858620 - Add jit-tests to test package;r=glandium --- testing/testsuite-targets.mk | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/testing/testsuite-targets.mk b/testing/testsuite-targets.mk index 510723784a9..2cf45aff80b 100644 --- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -405,6 +405,7 @@ package-tests: \ stage-modules \ stage-marionette \ stage-cppunittests \ + stage-jittest \ $(NULL) else # This staging area has been built for us by universal/flight.mk @@ -503,6 +504,12 @@ endif $(NSINSTALL) $(topsrcdir)/testing/runcppunittests.py $(PKG_STAGE)/cppunittests $(NSINSTALL) $(topsrcdir)/testing/remotecppunittests.py $(PKG_STAGE)/cppunittests +stage-jittest: + $(NSINSTALL) -D $(PKG_STAGE)/jit-test/tests + cp -RL $(topsrcdir)/js/src/jsapi.h $(PKG_STAGE)/jit-test + cp -RL $(topsrcdir)/js/src/jit-test $(PKG_STAGE)/jit-test/jit-test + cp -RL $(topsrcdir)/js/src/tests/lib $(PKG_STAGE)/jit-test/tests/lib + MARIONETTE_DIR=$(PKG_STAGE)/marionette stage-marionette: make-stage-dir $(NSINSTALL) -D $(MARIONETTE_DIR)/tests From 231bde23d4cf5af159ddf396da0e3a3045b7c72d Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Wed, 21 Aug 2013 08:32:47 -0400 Subject: [PATCH 31/54] Bug 901126 - Split browser_newtab_drag_drop.js into two tests. r=ttaubert --- browser/base/content/test/newtab/Makefile.in | 1 + .../test/newtab/browser_newtab_drag_drop.js | 43 --------------- .../newtab/browser_newtab_drag_drop_ext.js | 55 +++++++++++++++++++ 3 files changed, 56 insertions(+), 43 deletions(-) create mode 100644 browser/base/content/test/newtab/browser_newtab_drag_drop_ext.js diff --git a/browser/base/content/test/newtab/Makefile.in b/browser/base/content/test/newtab/Makefile.in index 0a61302b694..6ac460d14d3 100644 --- a/browser/base/content/test/newtab/Makefile.in +++ b/browser/base/content/test/newtab/Makefile.in @@ -14,6 +14,7 @@ MOCHITEST_BROWSER_FILES = \ browser_newtab_block.js \ browser_newtab_disable.js \ browser_newtab_drag_drop.js \ + browser_newtab_drag_drop_ext.js \ browser_newtab_drop_preview.js \ browser_newtab_focus.js \ browser_newtab_reset.js \ diff --git a/browser/base/content/test/newtab/browser_newtab_drag_drop.js b/browser/base/content/test/newtab/browser_newtab_drag_drop.js index 449032a7d7c..412969c2944 100644 --- a/browser/base/content/test/newtab/browser_newtab_drag_drop.js +++ b/browser/base/content/test/newtab/browser_newtab_drag_drop.js @@ -71,47 +71,4 @@ function runTests() { yield simulateDrop(0, 4); checkGrid("3,1p,2p,4,0p,5p,6,7,8"); - - // drag a new site onto the very first cell - yield setLinks("0,1,2,3,4,5,6,7,8"); - setPinnedLinks(",,,,,,,7,8"); - - yield addNewTabPageTab(); - checkGrid("0,1,2,3,4,5,6,7p,8p"); - - yield simulateExternalDrop(0); - checkGrid("99p,0,1,2,3,4,5,7p,8p"); - - // drag a new site onto the grid and make sure that pinned cells don't get - // pushed out - yield setLinks("0,1,2,3,4,5,6,7,8"); - setPinnedLinks(",,,,,,,7,8"); - - yield addNewTabPageTab(); - checkGrid("0,1,2,3,4,5,6,7p,8p"); - - yield simulateExternalDrop(7); - checkGrid("0,1,2,3,4,5,7p,99p,8p"); - - // drag a new site beneath a pinned cell and make sure the pinned cell is - // not moved - yield setLinks("0,1,2,3,4,5,6,7,8"); - setPinnedLinks(",,,,,,,,8"); - - yield addNewTabPageTab(); - checkGrid("0,1,2,3,4,5,6,7,8p"); - - yield simulateExternalDrop(7); - checkGrid("0,1,2,3,4,5,6,99p,8p"); - - // drag a new site onto a block of pinned sites and make sure they're shifted - // around accordingly - yield setLinks("0,1,2,3,4,5,6,7,8"); - setPinnedLinks("0,1,2,,,,,,"); - - yield addNewTabPageTab(); - checkGrid("0p,1p,2p"); - - yield simulateExternalDrop(1); - checkGrid("0p,99p,1p,2p,3,4,5,6,7"); } diff --git a/browser/base/content/test/newtab/browser_newtab_drag_drop_ext.js b/browser/base/content/test/newtab/browser_newtab_drag_drop_ext.js new file mode 100644 index 00000000000..1ba7cfed8be --- /dev/null +++ b/browser/base/content/test/newtab/browser_newtab_drag_drop_ext.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * These tests make sure that dragging and dropping sites works as expected. + * Sites contained in the grid need to shift around to indicate the result + * of the drag-and-drop operation. If the grid is full and we're dragging + * a new site into it another one gets pushed out. + * This is a continuation of browser_newtab_drag_drop.js + * to decrease test run time, focusing on external sites. + */ +function runTests() { + // drag a new site onto the very first cell + yield setLinks("0,1,2,3,4,5,6,7,8"); + setPinnedLinks(",,,,,,,7,8"); + + yield addNewTabPageTab(); + checkGrid("0,1,2,3,4,5,6,7p,8p"); + + yield simulateExternalDrop(0); + checkGrid("99p,0,1,2,3,4,5,7p,8p"); + + // drag a new site onto the grid and make sure that pinned cells don't get + // pushed out + yield setLinks("0,1,2,3,4,5,6,7,8"); + setPinnedLinks(",,,,,,,7,8"); + + yield addNewTabPageTab(); + checkGrid("0,1,2,3,4,5,6,7p,8p"); + + yield simulateExternalDrop(7); + checkGrid("0,1,2,3,4,5,7p,99p,8p"); + + // drag a new site beneath a pinned cell and make sure the pinned cell is + // not moved + yield setLinks("0,1,2,3,4,5,6,7,8"); + setPinnedLinks(",,,,,,,,8"); + + yield addNewTabPageTab(); + checkGrid("0,1,2,3,4,5,6,7,8p"); + + yield simulateExternalDrop(7); + checkGrid("0,1,2,3,4,5,6,99p,8p"); + + // drag a new site onto a block of pinned sites and make sure they're shifted + // around accordingly + yield setLinks("0,1,2,3,4,5,6,7,8"); + setPinnedLinks("0,1,2,,,,,,"); + + yield addNewTabPageTab(); + checkGrid("0p,1p,2p"); + + yield simulateExternalDrop(1); + checkGrid("0p,99p,1p,2p,3,4,5,6,7"); +} \ No newline at end of file From 75bb9054b695a786888512a2139c35acddf689b6 Mon Sep 17 00:00:00 2001 From: Andrew Wang Date: Wed, 21 Aug 2013 08:33:17 -0400 Subject: [PATCH 32/54] Bug 904480 - Return values of _SessionFile.wipe() and .writeLoadStateOnceAfterStartup() are unused. r=ttaubert --- browser/components/sessionstore/src/_SessionFile.jsm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/browser/components/sessionstore/src/_SessionFile.jsm b/browser/components/sessionstore/src/_SessionFile.jsm index 0f2508faac3..74e55af0da3 100644 --- a/browser/components/sessionstore/src/_SessionFile.jsm +++ b/browser/components/sessionstore/src/_SessionFile.jsm @@ -75,7 +75,7 @@ this._SessionFile = { * state. This must only be called once, it will throw an error otherwise. */ writeLoadStateOnceAfterStartup: function (aLoadState) { - return SessionFileInternal.writeLoadStateOnceAfterStartup(aLoadState); + SessionFileInternal.writeLoadStateOnceAfterStartup(aLoadState); }, /** * Create a backup copy, asynchronously. @@ -95,7 +95,7 @@ this._SessionFile = { * Wipe the contents of the session file, asynchronously. */ wipe: function () { - return SessionFileInternal.wipe(); + SessionFileInternal.wipe(); } }; @@ -231,7 +231,7 @@ let SessionFileInternal = { }, writeLoadStateOnceAfterStartup: function (aLoadState) { - return SessionWorker.post("writeLoadStateOnceAfterStartup", [aLoadState]).then(msg => { + SessionWorker.post("writeLoadStateOnceAfterStartup", [aLoadState]).then(msg => { this._recordTelemetry(msg.telemetry); return msg; }); @@ -246,7 +246,7 @@ let SessionFileInternal = { }, wipe: function () { - return SessionWorker.post("wipe"); + SessionWorker.post("wipe"); }, _recordTelemetry: function(telemetry) { From 9439d6554383b1532e9071628f0f34f86c9537cc Mon Sep 17 00:00:00 2001 From: Nicolas Carlo Date: Wed, 21 Aug 2013 08:33:45 -0400 Subject: [PATCH 33/54] Bug 906798 - Add periods to if statements in _stripHost function in aboutReader.js. r=margaret --- mobile/android/chrome/content/aboutReader.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/android/chrome/content/aboutReader.js b/mobile/android/chrome/content/aboutReader.js index c6d7a8c9ecf..bf3efffe771 100644 --- a/mobile/android/chrome/content/aboutReader.js +++ b/mobile/android/chrome/content/aboutReader.js @@ -587,11 +587,11 @@ AboutReader.prototype = { let start = 0; - if (host.startsWith("www")) + if (host.startsWith("www.")) start = 4; - else if (host.startsWith("m")) + else if (host.startsWith("m.")) start = 2; - else if (host.startsWith("mobile")) + else if (host.startsWith("mobile.")) start = 7; return host.substring(start); From 07ab4a3741d6ed97edb0c399fced113ebca2a080 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Wed, 21 Aug 2013 08:47:53 -0400 Subject: [PATCH 34/54] bug 750932 - multipart delimiter check r=jduell --HG-- extra : rebase_source : 9328212a2c7a9649d997624ad0dfe54d60435622 --- netwerk/streamconv/converters/nsMultiMixedConv.cpp | 3 ++- netwerk/test/unit/xpcshell.ini | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/netwerk/streamconv/converters/nsMultiMixedConv.cpp b/netwerk/streamconv/converters/nsMultiMixedConv.cpp index 024c057ce04..bd8fa0249ac 100644 --- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp +++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp @@ -565,7 +565,8 @@ nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context, int32_t tokenLinefeed = 1; while ( (token = FindToken(cursor, bufLen)) ) { - if (*(token+mTokenLen+1) == '-') { + if (((token + mTokenLen - cursor) < bufLen) && + (*(token + mTokenLen + 1) == '-')) { // This was the last delimiter so we can stop processing rv = SendData(cursor, LengthToToken(cursor, token)); if (NS_FAILED(rv)) return rv; diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index a577e083331..171c09a5737 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -129,8 +129,6 @@ skip-if = bits != 32 [test_multipart_byteranges.js] [test_multipart_streamconv.js] [test_multipart_streamconv_missing_lead_boundary.js] -# Bug 750932: test fails under AddressSanitizer -fail-if = asan [test_nestedabout_serialize.js] [test_net_addr.js] # Bug 732363: test fails on windows for unknown reasons. From 157f78e9137e4a01711d4b26486af99afddcdb7a Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Wed, 21 Aug 2013 19:08:23 +0300 Subject: [PATCH 35/54] Bug 906301, follow the DOM specs more strictly in case of mutations, r=peterv --HG-- extra : rebase_source : 775b570151782223cf69705e2225e4f1da4014e7 --- content/base/src/nsINode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/base/src/nsINode.cpp b/content/base/src/nsINode.cpp index 8ee9b3551e4..88bd5eade86 100644 --- a/content/base/src/nsINode.cpp +++ b/content/base/src/nsINode.cpp @@ -1808,7 +1808,7 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild, } // Verify that newContent has no parent. - if (newContent->GetParent()) { + if (newContent->GetParentNode()) { aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); return nullptr; } @@ -1885,7 +1885,7 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild, // Verify that all the things in fragChildren have no parent. for (uint32_t i = 0; i < count; ++i) { - if (fragChildren.ref().ElementAt(i)->GetParent()) { + if (fragChildren.ref().ElementAt(i)->GetParentNode()) { aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); return nullptr; } From 43d038b169f1bcf6673bdf5719e078b8e5cabaec Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Wed, 21 Aug 2013 12:13:50 -0400 Subject: [PATCH 36/54] Bug 784739 - Switch from NULL to nullptr in browser/components. r=ehsan --- browser/app/nsBrowserApp.cpp | 18 ++++----- browser/components/build/nsModule.cpp | 20 +++++----- .../migration/src/nsIEHistoryEnumerator.cpp | 4 +- .../shell/src/nsGNOMEShellService.cpp | 15 +++---- .../shell/src/nsMacShellService.cpp | 40 ++++++++++--------- .../shell/src/nsWindowsShellService.cpp | 35 ++++++++-------- 6 files changed, 68 insertions(+), 64 deletions(-) diff --git a/browser/app/nsBrowserApp.cpp b/browser/app/nsBrowserApp.cpp index 001f99be876..1b3a68d4d15 100644 --- a/browser/app/nsBrowserApp.cpp +++ b/browser/app/nsBrowserApp.cpp @@ -78,9 +78,9 @@ static void Output(const char *fmt, ... ) #if MOZ_WINCONSOLE fwprintf_s(stderr, wide_msg); #else - MessageBoxW(NULL, wide_msg, L"Firefox", MB_OK - | MB_ICONERROR - | MB_SETFOREGROUND); + MessageBoxW(nullptr, wide_msg, L"Firefox", MB_OK + | MB_ICONERROR + | MB_SETFOREGROUND); #endif #endif @@ -327,16 +327,16 @@ static int do_main(int argc, char* argv[], nsIFile *xreDirectory) // Check for a metro test harness command line args file HANDLE hTestFile = CreateFileA(path.get(), GENERIC_READ, - 0, NULL, OPEN_EXISTING, + 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, - NULL); + nullptr); if (hTestFile != INVALID_HANDLE_VALUE) { // Typical test harness command line args string is around 100 bytes. char buffer[1024]; memset(buffer, 0, sizeof(buffer)); DWORD bytesRead = 0; if (!ReadFile(hTestFile, (VOID*)buffer, sizeof(buffer)-1, - &bytesRead, NULL) || !bytesRead) { + &bytesRead, nullptr) || !bytesRead) { CloseHandle(hTestFile); printf("failed to read test file '%s'", testFile); return -1; @@ -351,7 +351,7 @@ static int do_main(int argc, char* argv[], nsIFile *xreDirectory) char* ptr = buffer; newArgv[0] = ptr; - while (*ptr != NULL && + while (*ptr != '\0' && (ptr - buffer) < sizeof(buffer) && newArgc < ARRAYSIZE(newArgv)) { if (isspace(*ptr)) { @@ -513,13 +513,13 @@ InitXPCOMGlue(const char *argv0, nsIFile **xreDirectory) } if (absfwurl) { CFURLRef xulurl = - CFURLCreateCopyAppendingPathComponent(NULL, absfwurl, + CFURLCreateCopyAppendingPathComponent(nullptr, absfwurl, CFSTR("XUL.framework"), true); if (xulurl) { CFURLRef xpcomurl = - CFURLCreateCopyAppendingPathComponent(NULL, xulurl, + CFURLCreateCopyAppendingPathComponent(nullptr, xulurl, CFSTR("libxpcom.dylib"), false); diff --git a/browser/components/build/nsModule.cpp b/browser/components/build/nsModule.cpp index 193f323290e..82172fd2d52 100644 --- a/browser/components/build/nsModule.cpp +++ b/browser/components/build/nsModule.cpp @@ -61,20 +61,20 @@ NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID); #endif static const mozilla::Module::CIDEntry kBrowserCIDs[] = { - { &kNS_BROWSERDIRECTORYPROVIDER_CID, false, NULL, DirectoryProviderConstructor }, + { &kNS_BROWSERDIRECTORYPROVIDER_CID, false, nullptr, DirectoryProviderConstructor }, #if defined(XP_WIN) - { &kNS_SHELLSERVICE_CID, false, NULL, nsWindowsShellServiceConstructor }, + { &kNS_SHELLSERVICE_CID, false, nullptr, nsWindowsShellServiceConstructor }, #elif defined(MOZ_WIDGET_GTK) - { &kNS_SHELLSERVICE_CID, false, NULL, nsGNOMEShellServiceConstructor }, + { &kNS_SHELLSERVICE_CID, false, nullptr, nsGNOMEShellServiceConstructor }, #endif - { &kNS_FEEDSNIFFER_CID, false, NULL, nsFeedSnifferConstructor }, - { &kNS_BROWSER_ABOUT_REDIRECTOR_CID, false, NULL, AboutRedirector::Create }, + { &kNS_FEEDSNIFFER_CID, false, nullptr, nsFeedSnifferConstructor }, + { &kNS_BROWSER_ABOUT_REDIRECTOR_CID, false, nullptr, AboutRedirector::Create }, #if defined(XP_WIN) - { &kNS_WINIEHISTORYENUMERATOR_CID, false, NULL, nsIEHistoryEnumeratorConstructor }, + { &kNS_WINIEHISTORYENUMERATOR_CID, false, nullptr, nsIEHistoryEnumeratorConstructor }, #elif defined(XP_MACOSX) - { &kNS_SHELLSERVICE_CID, false, NULL, nsMacShellServiceConstructor }, + { &kNS_SHELLSERVICE_CID, false, nullptr, nsMacShellServiceConstructor }, #endif - { NULL } + { nullptr } }; static const mozilla::Module::ContractIDEntry kBrowserContracts[] = { @@ -113,13 +113,13 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = { #elif defined(XP_MACOSX) { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID }, #endif - { NULL } + { nullptr } }; static const mozilla::Module::CategoryEntry kBrowserCategories[] = { { XPCOM_DIRECTORY_PROVIDER_CATEGORY, "browser-directory-provider", NS_BROWSERDIRECTORYPROVIDER_CONTRACTID }, { NS_CONTENT_SNIFFER_CATEGORY, "Feed Sniffer", NS_FEEDSNIFFER_CONTRACTID }, - { NULL } + { nullptr } }; static const mozilla::Module kBrowserModule = { diff --git a/browser/components/migration/src/nsIEHistoryEnumerator.cpp b/browser/components/migration/src/nsIEHistoryEnumerator.cpp index 1259670e376..9154ac8a5e7 100644 --- a/browser/components/migration/src/nsIEHistoryEnumerator.cpp +++ b/browser/components/migration/src/nsIEHistoryEnumerator.cpp @@ -45,7 +45,7 @@ NS_IMPL_ISUPPORTS1(nsIEHistoryEnumerator, nsISimpleEnumerator) nsIEHistoryEnumerator::nsIEHistoryEnumerator() { - ::CoInitialize(NULL); + ::CoInitialize(nullptr); } nsIEHistoryEnumerator::~nsIEHistoryEnumerator() @@ -60,7 +60,7 @@ nsIEHistoryEnumerator::EnsureInitialized() return; HRESULT hr = ::CoCreateInstance(CLSID_CUrlHistory, - NULL, + nullptr, CLSCTX_INPROC_SERVER, IID_IUrlHistoryStg2, getter_AddRefs(mIEHistory)); diff --git a/browser/components/shell/src/nsGNOMEShellService.cpp b/browser/components/shell/src/nsGNOMEShellService.cpp index 23e8896e20d..901561fd6a1 100644 --- a/browser/components/shell/src/nsGNOMEShellService.cpp +++ b/browser/components/shell/src/nsGNOMEShellService.cpp @@ -152,7 +152,8 @@ nsGNOMEShellService::KeyMatchesAppName(const char *aKeyValue) const gchar *commandPath; if (mUseLocaleFilenames) { - gchar *nativePath = g_filename_from_utf8(aKeyValue, -1, NULL, NULL, NULL); + gchar *nativePath = g_filename_from_utf8(aKeyValue, -1, + nullptr, nullptr, nullptr); if (!nativePath) { NS_ERROR("Error converting path to filesystem encoding"); return false; @@ -182,7 +183,7 @@ nsGNOMEShellService::CheckHandlerMatchesAppName(const nsACString &handler) const // The string will be something of the form: [/path/to/]browser "%s" // We want to remove all of the parameters and get just the binary name. - if (g_shell_parse_argv(command.get(), &argc, &argv, NULL) && argc > 0) { + if (g_shell_parse_argv(command.get(), &argc, &argv, nullptr) && argc > 0) { command.Assign(argv[0]); g_strfreev(argv); } @@ -380,7 +381,7 @@ WriteImage(const nsCString& aPath, imgIContainer* aImage) if (!pixbuf) return NS_ERROR_NOT_AVAILABLE; - gboolean res = gdk_pixbuf_save(pixbuf, aPath.get(), "png", NULL, NULL); + gboolean res = gdk_pixbuf_save(pixbuf, aPath.get(), "png", nullptr, nullptr); g_object_unref(pixbuf); return res ? NS_OK : NS_ERROR_FAILURE; @@ -454,7 +455,7 @@ nsGNOMEShellService::SetDesktopBackground(nsIDOMElement* aElement, gsettings->GetCollectionForSchema( NS_LITERAL_CSTRING(kDesktopBGSchema), getter_AddRefs(background_settings)); if (background_settings) { - gchar *file_uri = g_filename_to_uri(filePath.get(), NULL, NULL); + gchar *file_uri = g_filename_to_uri(filePath.get(), nullptr, nullptr); if (!file_uri) return NS_ERROR_FAILURE; @@ -615,7 +616,7 @@ nsGNOMEShellService::OpenApplication(int32_t aApplication) // Perform shell argument expansion int argc; char **argv; - if (!g_shell_parse_argv(appCommand.get(), &argc, &argv, NULL)) + if (!g_shell_parse_argv(appCommand.get(), &argc, &argv, nullptr)) return NS_ERROR_FAILURE; char **newArgv = new char*[argc + 1]; @@ -630,8 +631,8 @@ nsGNOMEShellService::OpenApplication(int32_t aApplication) newArgv[newArgc] = nullptr; - gboolean err = g_spawn_async(NULL, newArgv, NULL, G_SPAWN_SEARCH_PATH, - NULL, NULL, NULL, NULL); + gboolean err = g_spawn_async(nullptr, newArgv, nullptr, G_SPAWN_SEARCH_PATH, + nullptr, nullptr, nullptr, nullptr); g_strfreev(argv); delete[] newArgv; diff --git a/browser/components/shell/src/nsMacShellService.cpp b/browser/components/shell/src/nsMacShellService.cpp index b896cb0205e..06c744cccfb 100644 --- a/browser/components/shell/src/nsMacShellService.cpp +++ b/browser/components/shell/src/nsMacShellService.cpp @@ -40,13 +40,14 @@ nsMacShellService::IsDefaultBrowser(bool aStartupCheck, CFStringRef firefoxID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle()); if (!firefoxID) { - // CFBundleGetIdentifier is expected to return NULL only if the specified + // CFBundleGetIdentifier is expected to return nullptr only if the specified // bundle doesn't have a bundle identifier in its plist. In this case, that // means a failure, since our bundle does have an identifier. return NS_ERROR_FAILURE; } - // Get the default http handler's bundle ID (or NULL if it has not been explicitly set) + // Get the default http handler's bundle ID (or nullptr if it has not been + // explicitly set) CFStringRef defaultBrowserID = ::LSCopyDefaultHandlerForURLScheme(CFSTR("http")); if (defaultBrowserID) { *aIsDefaultBrowser = ::CFStringCompare(firefoxID, defaultBrowserID, 0) == kCFCompareEqualTo; @@ -259,7 +260,8 @@ nsMacShellService::OnStateChange(nsIWebProgress* aWebProgress, OSStatus status; // Convert the path into a FSRef - status = ::FSPathMakeRef((const UInt8*)nativePath.get(), &pictureRef, NULL); + status = ::FSPathMakeRef((const UInt8*)nativePath.get(), &pictureRef, + nullptr); if (status == noErr) { err = ::FSNewAlias(nil, &pictureRef, &aliasHandle); if (err == noErr && aliasHandle == nil) @@ -312,22 +314,22 @@ nsMacShellService::OpenApplication(int32_t aApplication) case nsIShellService::APPLICATION_MAIL: { CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault, - CFSTR("mailto:"), NULL); - err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, NULL, &appURL); + CFSTR("mailto:"), nullptr); + err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL); ::CFRelease(tempURL); } break; case nsIShellService::APPLICATION_NEWS: { CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault, - CFSTR("news:"), NULL); - err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, NULL, &appURL); + CFSTR("news:"), nullptr); + err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL); ::CFRelease(tempURL); } break; case nsIMacShellService::APPLICATION_KEYCHAIN_ACCESS: - err = ::LSGetApplicationForInfo('APPL', 'kcmr', NULL, kLSRolesAll, NULL, - &appURL); + err = ::LSGetApplicationForInfo('APPL', 'kcmr', nullptr, kLSRolesAll, + nullptr, &appURL); break; case nsIMacShellService::APPLICATION_NETWORK: { @@ -356,7 +358,7 @@ nsMacShellService::OpenApplication(int32_t aApplication) } if (appURL && err == noErr) { - err = ::LSOpenCFURLRef(appURL, NULL); + err = ::LSOpenCFURLRef(appURL, nullptr); rv = err != noErr ? NS_ERROR_FAILURE : NS_OK; ::CFRelease(appURL); @@ -394,12 +396,12 @@ nsMacShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACStrin const nsCString spec(aURI); const UInt8* uriString = (const UInt8*)spec.get(); - CFURLRef uri = ::CFURLCreateWithBytes(NULL, uriString, aURI.Length(), - kCFStringEncodingUTF8, NULL); + CFURLRef uri = ::CFURLCreateWithBytes(nullptr, uriString, aURI.Length(), + kCFStringEncodingUTF8, nullptr); if (!uri) return NS_ERROR_OUT_OF_MEMORY; - CFArrayRef uris = ::CFArrayCreate(NULL, (const void**)&uri, 1, NULL); + CFArrayRef uris = ::CFArrayCreate(nullptr, (const void**)&uri, 1, nullptr); if (!uris) { ::CFRelease(uri); return NS_ERROR_OUT_OF_MEMORY; @@ -408,11 +410,11 @@ nsMacShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACStrin LSLaunchURLSpec launchSpec; launchSpec.appURL = appURL; launchSpec.itemURLs = uris; - launchSpec.passThruParams = NULL; + launchSpec.passThruParams = nullptr; launchSpec.launchFlags = kLSLaunchDefaults; - launchSpec.asyncRefCon = NULL; + launchSpec.asyncRefCon = nullptr; - OSErr err = ::LSOpenFromURLSpec(&launchSpec, NULL); + OSErr err = ::LSOpenFromURLSpec(&launchSpec, nullptr); ::CFRelease(uris); ::CFRelease(uri); @@ -433,11 +435,11 @@ nsMacShellService::GetDefaultFeedReader(nsIFile** _retval) kCFStringEncodingASCII); } - CFURLRef defaultHandlerURL = NULL; + CFURLRef defaultHandlerURL = nullptr; OSStatus status = ::LSFindApplicationForInfo(kLSUnknownCreator, defaultHandlerID, - NULL, // inName - NULL, // outAppRef + nullptr, // inName + nullptr, // outAppRef &defaultHandlerURL); if (status == noErr && defaultHandlerURL) { diff --git a/browser/components/shell/src/nsWindowsShellService.cpp b/browser/components/shell/src/nsWindowsShellService.cpp index a31c32f6a42..5fb156390d3 100644 --- a/browser/components/shell/src/nsWindowsShellService.cpp +++ b/browser/components/shell/src/nsWindowsShellService.cpp @@ -223,8 +223,8 @@ LaunchHelper(nsAutoString& aPath) STARTUPINFOW si = {sizeof(si), 0}; PROCESS_INFORMATION pi = {0}; - if (!CreateProcessW(NULL, (LPWSTR)aPath.get(), NULL, NULL, FALSE, 0, NULL, - NULL, &si, &pi)) { + if (!CreateProcessW(nullptr, (LPWSTR)aPath.get(), nullptr, nullptr, FALSE, + 0, nullptr, nullptr, &si, &pi)) { return NS_ERROR_FAILURE; } @@ -361,7 +361,7 @@ nsWindowsShellService::IsDefaultBrowserVista(bool aCheckAllTypes, { IApplicationAssociationRegistration* pAAR; HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration, - NULL, + nullptr, CLSCTX_INPROC, IID_IApplicationAssociationRegistration, (void**)&pAAR); @@ -447,7 +447,8 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck, ::ZeroMemory(currValue, sizeof(currValue)); DWORD len = sizeof currValue; - res = ::RegQueryValueExW(theKey, L"", NULL, NULL, (LPBYTE)currValue, &len); + res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, + (LPBYTE)currValue, &len); // Close the key that was opened. ::RegCloseKey(theKey); if (REG_FAILED(res) || @@ -516,8 +517,8 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck, ::ZeroMemory(currValue, sizeof(currValue)); DWORD len = sizeof currValue; - res = ::RegQueryValueExW(theKey, L"", NULL, NULL, (LPBYTE)currValue, - &len); + res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, + (LPBYTE)currValue, &len); // Close the key that was opened. ::RegCloseKey(theKey); if (REG_FAILED(res) || PRUnichar('\0') != *currValue) { @@ -525,9 +526,9 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck, // Delete the key along with all of its childrean and then recreate it. const nsString &flatName = PromiseFlatString(keyName); ::SHDeleteKeyW(HKEY_CURRENT_USER, flatName.get()); - res = ::RegCreateKeyExW(HKEY_CURRENT_USER, flatName.get(), 0, NULL, - REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, - &theKey, NULL); + res = ::RegCreateKeyExW(HKEY_CURRENT_USER, flatName.get(), 0, nullptr, + REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, + nullptr, &theKey, nullptr); if (REG_FAILED(res)) { // If disabling DDE fails try to disable it using the helper // application when setting Firefox as the default browser. @@ -564,7 +565,7 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck, ::ZeroMemory(currValue, sizeof(currValue)); DWORD len = sizeof currValue; - res = ::RegQueryValueExW(theKey, L"", NULL, NULL, (LPBYTE)currValue, + res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, (LPBYTE)currValue, &len); // Don't update the FTP protocol handler's shell open command when the @@ -606,7 +607,7 @@ DynSHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo) { typedef HRESULT (WINAPI * SHOpenWithDialogPtr)(HWND hwndParent, const OPENASINFO *poainfo); - static SHOpenWithDialogPtr SHOpenWithDialogFn = NULL; + static SHOpenWithDialogPtr SHOpenWithDialogFn = nullptr; if (!SHOpenWithDialogFn) { // shell32.dll is in the knownDLLs list so will always be loaded from the // system32 directory. @@ -650,8 +651,8 @@ nsWindowsShellService::LaunchControlPanelDefaultPrograms() si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOWDEFAULT; PROCESS_INFORMATION pi = {0}; - if (!CreateProcessW(controlEXEPath, params, NULL, NULL, FALSE, 0, NULL, - NULL, &si, &pi)) { + if (!CreateProcessW(controlEXEPath, params, nullptr, nullptr, FALSE, + 0, nullptr, nullptr, &si, &pi)) { return NS_ERROR_FAILURE; } CloseHandle(pi.hProcess); @@ -665,11 +666,11 @@ nsWindowsShellService::LaunchHTTPHandlerPane() { OPENASINFO info; info.pcszFile = L"http"; - info.pcszClass = NULL; + info.pcszClass = nullptr; info.oaifInFlags = OAIF_FORCE_REGISTRATION | OAIF_URL_PROTOCOL | OAIF_REGISTER_EXT; - return DynSHOpenWithDialog(NULL, &info); + return DynSHOpenWithDialog(nullptr, &info); } NS_IMETHODIMP @@ -1028,8 +1029,8 @@ nsWindowsShellService::OpenApplication(int32_t aApplication) ::ZeroMemory(&si, sizeof(STARTUPINFOW)); ::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); - BOOL success = ::CreateProcessW(NULL, (LPWSTR)path.get(), NULL, - NULL, FALSE, 0, NULL, NULL, + BOOL success = ::CreateProcessW(nullptr, (LPWSTR)path.get(), nullptr, + nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); if (!success) return NS_ERROR_FAILURE; From 5e6d61b852712e89f6385b1819ec3943f709ded9 Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Wed, 21 Aug 2013 12:13:50 -0400 Subject: [PATCH 37/54] Bug 784739 - Switch from NULL to nullptr in browser/metro. r=ehsan --- .../shell/commandexecutehandler/CEHHelper.cpp | 14 ++--- .../CommandExecuteHandler.cpp | 56 +++++++++---------- browser/metro/shell/linktool/linktool.cpp | 32 +++++++---- .../metro/shell/testing/metrotestharness.cpp | 32 ++++++----- 4 files changed, 73 insertions(+), 61 deletions(-) diff --git a/browser/metro/shell/commandexecutehandler/CEHHelper.cpp b/browser/metro/shell/commandexecutehandler/CEHHelper.cpp index 31d65b36964..e81d2ac9f8b 100644 --- a/browser/metro/shell/commandexecutehandler/CEHHelper.cpp +++ b/browser/metro/shell/commandexecutehandler/CEHHelper.cpp @@ -23,7 +23,7 @@ Log(const wchar_t *fmt, ...) #if !defined(SHOW_CONSOLE) return; #endif - va_list a = NULL; + va_list a = nullptr; wchar_t szDebugString[1024]; if(!lstrlenW(fmt)) return; @@ -34,8 +34,8 @@ Log(const wchar_t *fmt, ...) return; DWORD len; - WriteConsoleW(sCon, szDebugString, lstrlenW(szDebugString), &len, NULL); - WriteConsoleW(sCon, L"\n", 1, &len, NULL); + WriteConsoleW(sCon, szDebugString, lstrlenW(szDebugString), &len, nullptr); + WriteConsoleW(sCon, L"\n", 1, &len, nullptr); if (IsDebuggerPresent()) { OutputDebugStringW(szDebugString); @@ -53,7 +53,7 @@ SetupConsole() int fd = _open_osfhandle(reinterpret_cast(sCon), 0); fp = _fdopen(fd, "w"); *stdout = *fp; - setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stdout, nullptr, _IONBF, 0); } #endif @@ -104,19 +104,19 @@ IsDX10Available() CComPtr device; // Try for DX10.1 - if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, NULL, + if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr, D3D10_CREATE_DEVICE_BGRA_SUPPORT | D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, D3D10_FEATURE_LEVEL_10_1, D3D10_1_SDK_VERSION, &device))) { // Try for DX10 - if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, NULL, + if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr, D3D10_CREATE_DEVICE_BGRA_SUPPORT | D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, D3D10_FEATURE_LEVEL_10_0, D3D10_1_SDK_VERSION, &device))) { // Try for DX9.3 (we fall back to cairo and cairo has support for D3D 9.3) - if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, NULL, + if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr, D3D10_CREATE_DEVICE_BGRA_SUPPORT | D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, D3D10_FEATURE_LEVEL_9_3, diff --git a/browser/metro/shell/commandexecutehandler/CommandExecuteHandler.cpp b/browser/metro/shell/commandexecutehandler/CommandExecuteHandler.cpp index a786e589400..28c0579b6f1 100644 --- a/browser/metro/shell/commandexecutehandler/CommandExecuteHandler.cpp +++ b/browser/metro/shell/commandexecutehandler/CommandExecuteHandler.cpp @@ -49,7 +49,7 @@ static bool GetModulePath(CStringW& aPathBuffer) WCHAR buffer[MAX_PATH]; memset(buffer, 0, sizeof(buffer)); - if (!GetModuleFileName(NULL, buffer, MAX_PATH)) { + if (!GetModuleFileName(nullptr, buffer, MAX_PATH)) { Log(L"GetModuleFileName failed."); return false; } @@ -68,7 +68,7 @@ template void SafeRelease(T **ppT) { if (*ppT) { (*ppT)->Release(); - *ppT = NULL; + *ppT = nullptr; } } @@ -89,8 +89,8 @@ public: CExecuteCommandVerb() : mRef(1), - mShellItemArray(NULL), - mUnkSite(NULL), + mShellItemArray(nullptr), + mUnkSite(nullptr), mTargetIsFileSystemLink(false), mTargetIsDefaultBrowser(false), mTargetIsBrowser(false), @@ -176,9 +176,9 @@ public: #ifdef SHOW_CONSOLE Log(L"SetSelection param count: %d", count); for (DWORD idx = 0; idx < count; idx++) { - IShellItem* item = NULL; + IShellItem* item = nullptr; if (SUCCEEDED(aArray->GetItemAt(idx, &item))) { - LPWSTR str = NULL; + LPWSTR str = nullptr; if (FAILED(item->GetDisplayName(SIGDN_FILESYSPATH, &str))) { if (FAILED(item->GetDisplayName(SIGDN_URL, &str))) { Log(L"Failed to get a shell item array item."); @@ -193,7 +193,7 @@ public: } #endif - IShellItem* item = NULL; + IShellItem* item = nullptr; if (FAILED(aArray->GetItemAt(0, &item))) { return E_FAIL; } @@ -211,7 +211,7 @@ public: IFACEMETHODIMP GetSelection(REFIID aRefID, void **aInt) { - *aInt = NULL; + *aInt = nullptr; return mShellItemArray ? mShellItemArray->QueryInterface(aRefID, aInt) : E_FAIL; } @@ -235,7 +235,7 @@ public: IFACEMETHODIMP GetSite(REFIID aRefID, void **aInt) { - *aInt = NULL; + *aInt = nullptr; return mUnkSite ? mUnkSite->QueryInterface(aRefID, aInt) : E_FAIL; } @@ -252,14 +252,14 @@ public: } HRESULT hr; - IServiceProvider* pSvcProvider = NULL; + IServiceProvider* pSvcProvider = nullptr; hr = mUnkSite->QueryInterface(IID_IServiceProvider, (void**)&pSvcProvider); if (!pSvcProvider) { Log(L"Couldn't get IServiceProvider service from explorer. (%X)", hr); return S_OK; } - IExecuteCommandHost* pHost = NULL; + IExecuteCommandHost* pHost = nullptr; // If we can't get this it's a conventional desktop launch hr = pSvcProvider->QueryService(SID_ExecuteCommandHost, IID_IExecuteCommandHost, (void**)&pHost); @@ -340,7 +340,7 @@ public: { IApplicationAssociationRegistration* pAAR; HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration, - NULL, + nullptr, CLSCTX_INPROC, IID_IApplicationAssociationRegistration, (void**)&pAAR); @@ -416,7 +416,7 @@ static bool GetDefaultBrowserPath(CStringW& aPathBuffer) if (FAILED(AssocQueryStringW(ASSOCF_NOTRUNCATE | ASSOCF_INIT_IGNOREUNKNOWN, ASSOCSTR_EXECUTABLE, - kDefaultMetroBrowserIDPathKey, NULL, + kDefaultMetroBrowserIDPathKey, nullptr, buffer, &length))) { Log(L"AssocQueryString failed."); return false; @@ -451,7 +451,7 @@ static bool GetDefaultBrowserAppModelID(WCHAR* aIDBuffer, } DWORD len = aCharLength * sizeof(WCHAR); memset(aIDBuffer, 0, len); - if (RegQueryValueExW(key, L"AppUserModelID", NULL, NULL, + if (RegQueryValueExW(key, L"AppUserModelID", nullptr, nullptr, (LPBYTE)aIDBuffer, &len) != ERROR_SUCCESS || !len) { RegCloseKey(key); return false; @@ -513,7 +513,7 @@ bool CExecuteCommandVerb::SetTargetPath(IShellItem* aItem) CComPtr object; // Check the underlying data object first to insure we get // absolute uri. See chromium bug 157184. - if (SUCCEEDED(aItem->BindToHandler(NULL, BHID_DataObject, + if (SUCCEEDED(aItem->BindToHandler(nullptr, BHID_DataObject, IID_IDataObject, reinterpret_cast(&object))) && GetPlainText(object, cstrText)) { @@ -537,7 +537,7 @@ bool CExecuteCommandVerb::SetTargetPath(IShellItem* aItem) Log(L"No data object or data object has no text."); // Use the shell item display name - LPWSTR str = NULL; + LPWSTR str = nullptr; mTargetIsFileSystemLink = true; if (FAILED(aItem->GetDisplayName(SIGDN_FILESYSPATH, &str))) { mTargetIsFileSystemLink = false; @@ -596,12 +596,12 @@ void CExecuteCommandVerb::LaunchDesktopBrowser() SHELLEXECUTEINFOW seinfo; memset(&seinfo, 0, sizeof(seinfo)); seinfo.cbSize = sizeof(SHELLEXECUTEINFOW); - seinfo.fMask = NULL; - seinfo.hwnd = NULL; - seinfo.lpVerb = NULL; + seinfo.fMask = 0; + seinfo.hwnd = nullptr; + seinfo.lpVerb = nullptr; seinfo.lpFile = browserPath; seinfo.lpParameters = params; - seinfo.lpDirectory = NULL; + seinfo.lpDirectory = nullptr; seinfo.nShow = SW_SHOWNORMAL; ShellExecuteExW(&seinfo); @@ -635,9 +635,9 @@ IFACEMETHODIMP CExecuteCommandVerb::Execute() } // Launch into Metro - IApplicationActivationManager* activateMgr = NULL; + IApplicationActivationManager* activateMgr = nullptr; DWORD processID; - if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, NULL, + if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, nullptr, CLSCTX_LOCAL_SERVER, IID_IApplicationActivationManager, (void**)&activateMgr))) { @@ -657,7 +657,7 @@ IFACEMETHODIMP CExecuteCommandVerb::Execute() // Hand off focus rights to the out-of-process activation server. Without // this the metro interface won't launch. - hr = CoAllowSetForegroundWindow(activateMgr, NULL); + hr = CoAllowSetForegroundWindow(activateMgr, nullptr); if (FAILED(hr)) { Log(L"CoAllowSetForegroundWindow result %X", hr); activateMgr->Release(); @@ -727,7 +727,7 @@ ClassFactory::Register(CLSCTX aClass, REGCLS aUse) STDMETHODIMP ClassFactory::QueryInterface(REFIID riid, void **ppv) { - IUnknown *punk = NULL; + IUnknown *punk = nullptr; if (riid == IID_IUnknown || riid == IID_IClassFactory) { punk = static_cast(this); } @@ -743,7 +743,7 @@ ClassFactory::QueryInterface(REFIID riid, void **ppv) STDMETHODIMP ClassFactory::CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { - *ppv = NULL; + *ppv = nullptr; if (punkOuter) return CLASS_E_NOAGGREGATION; return mUnkObject->QueryInterface(riid, ppv); @@ -771,7 +771,7 @@ int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR pszCmdLine, int) if (!wcslen(pszCmdLine) || StrStrI(pszCmdLine, L"-Embedding")) { - CoInitialize(NULL); + CoInitialize(nullptr); CExecuteCommandVerb *pHandler = new CExecuteCommandVerb(); if (!pHandler) @@ -784,13 +784,13 @@ int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR pszCmdLine, int) ClassFactory classFactory(ppi); ppi->Release(); - ppi = NULL; + ppi = nullptr; // REGCLS_SINGLEUSE insures we only get used once and then discarded. if (FAILED(classFactory.Register(CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE))) return -1; - if (!SetTimer(NULL, 1, HEARTBEAT_MSEC, NULL)) { + if (!SetTimer(nullptr, 1, HEARTBEAT_MSEC, nullptr)) { Log(L"Failed to set timer, can't process request."); return -1; } diff --git a/browser/metro/shell/linktool/linktool.cpp b/browser/metro/shell/linktool/linktool.cpp index 2fba6bf298a..f10dbbd20e5 100644 --- a/browser/metro/shell/linktool/linktool.cpp +++ b/browser/metro/shell/linktool/linktool.cpp @@ -37,10 +37,13 @@ HRESULT SetShortcutProps(LPCWSTR aShortcutPath, LPCWSTR aAppModelID, bool aSetID, bool aSetMode) { HRESULT hres; - ::CoInitialize(NULL); + ::CoInitialize(nullptr); - IPropertyStore *m_pps = NULL; - if (FAILED(hres = SHGetPropertyStoreFromParsingName(aShortcutPath, NULL, GPS_READWRITE, IID_PPV_ARGS(&m_pps)))) { + IPropertyStore *m_pps = nullptr; + if (FAILED(hres = SHGetPropertyStoreFromParsingName(aShortcutPath, + nullptr, + GPS_READWRITE, + IID_PPV_ARGS(&m_pps)))) { printf("SHGetPropertyStoreFromParsingName failed\n"); goto Exit; } @@ -79,10 +82,13 @@ HRESULT PrintShortcutProps(LPCWSTR aTargetPath) { HRESULT hres; - ::CoInitialize(NULL); + ::CoInitialize(nullptr); - IPropertyStore *m_pps = NULL; - if (FAILED(hres = SHGetPropertyStoreFromParsingName(aTargetPath, NULL, GPS_READWRITE, IID_PPV_ARGS(&m_pps)))) { + IPropertyStore *m_pps = nullptr; + if (FAILED(hres = SHGetPropertyStoreFromParsingName(aTargetPath, + nullptr, + GPS_READWRITE, + IID_PPV_ARGS(&m_pps)))) { printf("SHGetPropertyStoreFromParsingName failed\n"); goto Exit; } @@ -125,9 +131,9 @@ CreateLink(LPCWSTR aTargetPath, LPCWSTR aShortcutPath, LPCWSTR aDescription) wprintf(L"creating shortcut: '%s'\n", aShortcutPath); - CoInitialize(NULL); + CoInitialize(nullptr); - hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); if (FAILED(hres)) { CoUninitialize(); @@ -140,7 +146,7 @@ CreateLink(LPCWSTR aTargetPath, LPCWSTR aShortcutPath, LPCWSTR aDescription) psl->SetDescription(L""); } - IPersistFile* ppf = NULL; + IPersistFile* ppf = nullptr; hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); if (SUCCEEDED(hres)) { @@ -261,7 +267,9 @@ int wmain(int argc, WCHAR* argv[]) } if (createShortcutFound) { - if (FAILED(hres = CreateLink(targetPathStr, shortcutPathStr, (descriptionFound ? descriptionStr : NULL)))) { + if (FAILED(hres = CreateLink(targetPathStr, + shortcutPathStr, + (descriptionFound ? descriptionStr : nullptr)))) { printf("failed creating shortcut HRESULT=%X\n", hres); return -1; } @@ -275,7 +283,9 @@ int wmain(int argc, WCHAR* argv[]) } if (appModelIDFound || modeFound) { - if (FAILED(hres = SetShortcutProps(target, (appModelIDFound ? appModelIDStr : NULL), appModelIDFound, modeFound))) { + if (FAILED(hres = SetShortcutProps(target, + (appModelIDFound ? appModelIDStr : nullptr), + appModelIDFound, modeFound))) { printf("failed adding property HRESULT=%X\n", hres); return -1; } diff --git a/browser/metro/shell/testing/metrotestharness.cpp b/browser/metro/shell/testing/metrotestharness.cpp index d6eaead4d1b..55c3e257010 100644 --- a/browser/metro/shell/testing/metrotestharness.cpp +++ b/browser/metro/shell/testing/metrotestharness.cpp @@ -50,7 +50,7 @@ CString sFirefoxPath; static void Log(const wchar_t *fmt, ...) { - va_list a = NULL; + va_list a = nullptr; wchar_t szDebugString[1024]; if(!lstrlenW(fmt)) return; @@ -66,7 +66,7 @@ static void Log(const wchar_t *fmt, ...) static void Fail(bool aRequestRetry, const wchar_t *fmt, ...) { - va_list a = NULL; + va_list a = nullptr; wchar_t szDebugString[1024]; if(!lstrlenW(fmt)) return; @@ -93,7 +93,7 @@ static bool GetModulePath(CStringW& aPathBuffer) WCHAR buffer[MAX_PATH]; memset(buffer, 0, sizeof(buffer)); - if (!GetModuleFileName(NULL, buffer, MAX_PATH)) { + if (!GetModuleFileName(nullptr, buffer, MAX_PATH)) { Fail(false, L"GetModuleFileName failed."); return false; } @@ -145,7 +145,7 @@ static bool GetDefaultBrowserAppModelID(WCHAR* aIDBuffer, } DWORD len = aCharLength * sizeof(WCHAR); memset(aIDBuffer, 0, len); - if (RegQueryValueExW(key, L"AppUserModelID", NULL, NULL, + if (RegQueryValueExW(key, L"AppUserModelID", nullptr, nullptr, (LPBYTE)aIDBuffer, &len) != ERROR_SUCCESS || !len) { RegCloseKey(key); return false; @@ -174,7 +174,7 @@ static bool SetupTestOutputPipe() SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; - saAttr.lpSecurityDescriptor = NULL; + saAttr.lpSecurityDescriptor = nullptr; gTestOutputPipe = CreateNamedPipeW(L"\\\\.\\pipe\\metrotestharness", @@ -182,7 +182,7 @@ static bool SetupTestOutputPipe() PIPE_TYPE_BYTE|PIPE_WAIT, 1, PIPE_BUFFER_SIZE, - PIPE_BUFFER_SIZE, 0, NULL); + PIPE_BUFFER_SIZE, 0, nullptr); if (gTestOutputPipe == INVALID_HANDLE_VALUE) { Log(L"Failed to create named logging pipe."); @@ -194,7 +194,8 @@ static bool SetupTestOutputPipe() static void ReadPipe() { DWORD numBytesRead; - while (ReadFile(gTestOutputPipe, buffer, PIPE_BUFFER_SIZE, &numBytesRead, NULL) && + while (ReadFile(gTestOutputPipe, buffer, PIPE_BUFFER_SIZE, + &numBytesRead, nullptr) && numBytesRead) { buffer[numBytesRead] = '\0'; printf("%s", buffer); @@ -209,7 +210,7 @@ static int Launch() // The interface that allows us to activate the browser CComPtr activateMgr; - if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, NULL, + if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, nullptr, CLSCTX_LOCAL_SERVER, IID_IApplicationActivationManager, (void**)&activateMgr))) { @@ -229,7 +230,7 @@ static int Launch() // Hand off focus rights if the terminal has focus to the out-of-process // activation server (explorer.exe). Without this the metro interface // won't launch. - hr = CoAllowSetForegroundWindow(activateMgr, NULL); + hr = CoAllowSetForegroundWindow(activateMgr, nullptr); if (FAILED(hr)) { // Log but don't fail. This has happened on vms with certain terminals run by // QA during mozmill testing. @@ -264,7 +265,7 @@ static int Launch() } else { // Use the module path char path[MAX_PATH]; - if (!GetModuleFileNameA(NULL, path, MAX_PATH)) { + if (!GetModuleFileNameA(nullptr, path, MAX_PATH)) { Fail(false, L"GetModuleFileNameA errorno=%d", GetLastError()); return FAILURE; } @@ -289,9 +290,9 @@ static int Launch() Log(L"Writing out tests.ini to: '%s'", CStringW(testFilePath)); HANDLE hTestFile = CreateFileA(testFilePath, GENERIC_WRITE, - 0, NULL, CREATE_ALWAYS, + 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, - NULL); + nullptr); if (hTestFile == INVALID_HANDLE_VALUE) { Fail(false, L"CreateFileA errorno=%d", GetLastError()); return FAILURE; @@ -306,7 +307,8 @@ static int Launch() asciiParams += sAppParams; asciiParams.Trim(); Log(L"Browser command line args: '%s'", CString(asciiParams)); - if (!WriteFile(hTestFile, asciiParams, asciiParams.GetLength(), NULL, 0)) { + if (!WriteFile(hTestFile, asciiParams, asciiParams.GetLength(), + nullptr, 0)) { CloseHandle(hTestFile); Fail(false, L"WriteFile errorno=%d", GetLastError()); return FAILURE; @@ -347,7 +349,7 @@ static int Launch() } else if (waitResult == WAIT_OBJECT_0 + 1) { ReadPipe(); } else if (waitResult == WAIT_OBJECT_0 + 2 && - PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } @@ -363,7 +365,7 @@ static int Launch() int wmain(int argc, WCHAR* argv[]) { - CoInitialize(NULL); + CoInitialize(nullptr); int idx; bool firefoxParam = false; From 9edb6e2e8f9aa82d38dff9f0209c8c1de3ea0e64 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Wed, 21 Aug 2013 09:16:31 -0700 Subject: [PATCH 38/54] Bug 907049 - Cache unicode representation of substs variables; r=glandium --- .../mozbuild/backend/configenvironment.py | 34 +++++++++++++++++-- python/mozbuild/mozbuild/frontend/reader.py | 20 ++--------- .../test/backend/test_configenvironment.py | 15 ++++++-- python/mozbuild/mozbuild/test/common.py | 13 +++++-- .../mozbuild/test/frontend/test_emitter.py | 7 ++-- .../mozbuild/test/frontend/test_reader.py | 10 +++--- .../mozbuild/test/frontend/test_sandbox.py | 3 +- 7 files changed, 67 insertions(+), 35 deletions(-) diff --git a/python/mozbuild/mozbuild/backend/configenvironment.py b/python/mozbuild/mozbuild/backend/configenvironment.py index 4841ec0081c..62a664e8456 100644 --- a/python/mozbuild/mozbuild/backend/configenvironment.py +++ b/python/mozbuild/mozbuild/backend/configenvironment.py @@ -6,6 +6,7 @@ import ntpath import os import posixpath import re +import sys from os.path import relpath @@ -14,9 +15,16 @@ from Preprocessor import Preprocessor from ..util import ( ensureParentDir, FileAvoidWrite, + ReadOnlyDict, ) +if sys.version_info.major == 2: + text_type = unicode +else: + text_type = str + + RE_SHELL_ESCAPE = re.compile('''([ \t`#$^&*(){}\\|;'"<>?\[\]])''') @@ -105,7 +113,7 @@ class ConfigEnvironment(object): def __init__(self, topsrcdir, topobjdir, defines=[], non_global_defines=[], substs=[]): - self.defines = dict(defines) + self.defines = ReadOnlyDict(defines) self.substs = dict(substs) self.topsrcdir = topsrcdir self.topobjdir = topobjdir @@ -120,6 +128,29 @@ class ConfigEnvironment(object): self.substs['ALLDEFINES'] = '\n'.join(sorted(['#define %s %s' % (name, self.defines[name]) for name in global_defines])) + self.substs = ReadOnlyDict(self.substs) + + # Populate a Unicode version of substs. This is an optimization to make + # moz.build reading faster, since each sandbox needs a Unicode version + # of these variables and doing it over a thousand times is a hotspot + # during sandbox execution! + # Bug 844509 tracks moving everything to Unicode. + self.substs_unicode = {} + for k, v in self.substs.items(): + if not isinstance(v, text_type): + try: + v = v.decode('utf-8') + except UnicodeDecodeError: + log(self._log, logging.INFO, 'lossy_encoding', + {'variable': k}, + 'Lossy Unicode encoding for {variable}. See bug 844509.') + + v = v.decode('utf-8', 'replace') + + self.substs_unicode[k] = v + + self.substs_unicode = ReadOnlyDict(self.substs_unicode) + @staticmethod def from_config_status(path): config = BuildConfig.from_config_status(path) @@ -225,4 +256,3 @@ class ConfigEnvironment(object): output.write(l) return output.close() - diff --git a/python/mozbuild/mozbuild/frontend/reader.py b/python/mozbuild/mozbuild/frontend/reader.py index 1c39b5470bb..56f131a4e8d 100644 --- a/python/mozbuild/mozbuild/frontend/reader.py +++ b/python/mozbuild/mozbuild/frontend/reader.py @@ -168,24 +168,8 @@ class MozbuildSandbox(Sandbox): d['SRCDIR'] = os.path.join(topsrcdir, reldir).replace(os.sep, '/').rstrip('/') d['OBJDIR'] = os.path.join(topobjdir, reldir).replace(os.sep, '/').rstrip('/') - # config.status does not yet use unicode. However, mozbuild expects - # unicode everywhere. So, decode binary into unicode as necessary. - # Bug 844509 tracks a better way to do this. - substs = {} - for k, v in config.substs.items(): - if not isinstance(v, text_type): - try: - v = v.decode('utf-8') - except UnicodeDecodeError: - log(self._log, logging.INFO, 'lossy_encoding', - {'variable': k}, - 'Lossy Unicode encoding for {variable}. See bug 844509.') - - v = v.decode('utf-8', 'replace') - - substs[k] = v - - d['CONFIG'] = ReadOnlyDefaultDict(substs, global_default=None) + d['CONFIG'] = ReadOnlyDefaultDict(self.config.substs_unicode, + global_default=None) # Register functions. for name, func in FUNCTIONS.items(): diff --git a/python/mozbuild/mozbuild/test/backend/test_configenvironment.py b/python/mozbuild/mozbuild/test/backend/test_configenvironment.py index dd202a3b290..8b65b6aeb44 100644 --- a/python/mozbuild/mozbuild/test/backend/test_configenvironment.py +++ b/python/mozbuild/mozbuild/test/backend/test_configenvironment.py @@ -9,15 +9,26 @@ from mozunit import main, MockedOpen import mozbuild.backend.configenvironment as ConfigStatus +from mozbuild.util import ReadOnlyDict + + class ConfigEnvironment(ConfigStatus.ConfigEnvironment): def __init__(self, *args, **kwargs): ConfigStatus.ConfigEnvironment.__init__(self, *args, **kwargs) # Be helpful to unit tests if not 'top_srcdir' in self.substs: if os.path.isabs(self.topsrcdir): - self.substs['top_srcdir'] = self.topsrcdir.replace(os.sep, '/') + top_srcdir = self.topsrcdir.replace(os.sep, '/') else: - self.substs['top_srcdir'] = os.path.relpath(self.topsrcdir, self.topobjdir).replace(os.sep, '/') + top_srcdir = os.path.relpath(self.topsrcdir, self.topobjdir).replace(os.sep, '/') + + d = dict(self.substs) + d['top_srcdir'] = top_srcdir + self.substs = ReadOnlyDict(d) + + d = dict(self.substs_unicode) + d[u'top_srcdir'] = top_srcdir.decode('utf-8') + self.substs_unicode = ReadOnlyDict(d) class TestEnvironment(unittest.TestCase): diff --git a/python/mozbuild/mozbuild/test/common.py b/python/mozbuild/mozbuild/test/common.py index 05d34fd573e..63c7c9a945f 100644 --- a/python/mozbuild/mozbuild/test/common.py +++ b/python/mozbuild/mozbuild/test/common.py @@ -8,6 +8,8 @@ import os from mach.logging import LoggingManager +from mozbuild.util import ReadOnlyDict + # By including this module, tests get structured logging. log_manager = LoggingManager() @@ -16,16 +18,21 @@ log_manager.add_terminal_logging() # mozconfig is not a reusable type (it's actually a module) so, we # have to mock it. class MockConfig(object): - def __init__(self, topsrcdir='/path/to/topsrcdir'): + def __init__(self, topsrcdir='/path/to/topsrcdir', extra_substs={}): self.topsrcdir = topsrcdir self.topobjdir = '/path/to/topobjdir' - self.substs = { + self.substs = ReadOnlyDict({ 'MOZ_FOO': 'foo', 'MOZ_BAR': 'bar', 'MOZ_TRUE': '1', 'MOZ_FALSE': '', - } + }) + + self.substs.update(extra_substs) + + self.substs_unicode = ReadOnlyDict({k.decode('utf-8'): v.decode('utf-8', + 'replace') for k, v in self.substs.items()}) def child_path(self, p): return os.path.join(self.topsrcdir, p) diff --git a/python/mozbuild/mozbuild/test/frontend/test_emitter.py b/python/mozbuild/mozbuild/test/frontend/test_emitter.py index 6c8d6ede6df..f284b3e8c10 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py +++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py @@ -31,9 +31,10 @@ data_path = os.path.join(data_path, 'data') class TestEmitterBasic(unittest.TestCase): def reader(self, name): - config = MockConfig(os.path.join(data_path, name)) - config.substs['ENABLE_TESTS'] = '1' - config.substs['BIN_SUFFIX'] = '.prog' + config = MockConfig(os.path.join(data_path, name), extra_substs=dict( + ENABLE_TESTS='1', + BIN_SUFFIX='.prog', + )) return BuildReader(config) diff --git a/python/mozbuild/mozbuild/test/frontend/test_reader.py b/python/mozbuild/mozbuild/test/frontend/test_reader.py index a4d1f7d119d..258fb3d3ef0 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_reader.py +++ b/python/mozbuild/mozbuild/test/frontend/test_reader.py @@ -26,16 +26,16 @@ data_path = os.path.join(data_path, 'data') class TestBuildReader(unittest.TestCase): - def config(self, name): + def config(self, name, **kwargs): path = os.path.join(data_path, name) - return MockConfig(path) + return MockConfig(path, **kwargs) def reader(self, name, enable_tests=False): - config = self.config(name) - + extra = {} if enable_tests: - config.substs['ENABLE_TESTS'] = '1' + extra['ENABLE_TESTS'] = '1' + config = self.config(name, extra_substs=extra) return BuildReader(config) diff --git a/python/mozbuild/mozbuild/test/frontend/test_sandbox.py b/python/mozbuild/mozbuild/test/frontend/test_sandbox.py index cbda3be7a9b..c5919460bb2 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_sandbox.py +++ b/python/mozbuild/mozbuild/test/frontend/test_sandbox.py @@ -303,9 +303,8 @@ add_tier_dir('t1', 'bat', static=True) def test_invalid_utf8_substs(self): """Ensure invalid UTF-8 in substs is converted with an error.""" - config = MockConfig() # This is really mbcs. It's a bunch of invalid UTF-8. - config.substs['BAD_UTF8'] = b'\x83\x81\x83\x82\x3A' + config = MockConfig(extra_substs={'BAD_UTF8': b'\x83\x81\x83\x82\x3A'}) sandbox = MozbuildSandbox(config, '/foo/moz.build') From 9838c75c5b7aa0706384ba7c5411effd9451c640 Mon Sep 17 00:00:00 2001 From: Stephen Pohl Date: Wed, 21 Aug 2013 12:21:59 -0400 Subject: [PATCH 39/54] Revert fd6c7792b048 from bug 673875 for causing bug 907275. r=smichaud --- widget/cocoa/nsChildView.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 124b1d18acc..25d5504cc53 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -3829,6 +3829,8 @@ NSEvent* gLastDragMouseDownEvent = nil; - (void)beginGestureWithEvent:(NSEvent *)anEvent { + NS_ASSERTION(mGestureState == eGestureState_None, "mGestureState should be eGestureState_None"); + if (!anEvent) return; @@ -4194,7 +4196,6 @@ NSEvent* gLastDragMouseDownEvent = nil; } __block BOOL animationCanceled = NO; - __block BOOL geckoSwipeEventSent = NO; // At this point, anEvent is the first scroll wheel event in a two-finger // horizontal gesture that we've decided to treat as a swipe. When we call // [NSEvent trackSwipeEventWithOptions:...], the OS interprets all @@ -4248,7 +4249,7 @@ NSEvent* gLastDragMouseDownEvent = nil; direction:0.0 delta:gestureAmount]; - if (phase == NSEventPhaseEnded && !geckoSwipeEventSent) { + if (phase == NSEventPhaseEnded) { // The result of the swipe is now known, so the main event can be sent. // The animation might continue even after this event was sent, so // don't tear down the animation overlay yet. @@ -4261,7 +4262,6 @@ NSEvent* gLastDragMouseDownEvent = nil; // gestureAmount is negative when it will be '-1' at isComplete, and // positive when it will be '1'. And phase is never equal to // NSEventPhaseEnded when gestureAmount will be '0' at isComplete. - geckoSwipeEventSent = YES; [self sendSwipeEvent:anEvent withKind:NS_SIMPLE_GESTURE_SWIPE allowedDirections:&allowedDirectionsCopy From 137e8534cdc35fba1b773abc01f46e6cfac5c533 Mon Sep 17 00:00:00 2001 From: Stephen Pohl Date: Wed, 21 Aug 2013 12:22:03 -0400 Subject: [PATCH 40/54] Revert 366e6a39d71a from bug 673875 for causing bug 907275. r=smichaud --- .../base/content/browser-gestureSupport.js | 127 +++------ content/events/src/nsEventStateManager.cpp | 15 +- widget/cocoa/nsChildView.h | 6 +- widget/cocoa/nsChildView.mm | 267 ++++++++---------- widget/nsGUIEvent.h | 8 +- 5 files changed, 150 insertions(+), 273 deletions(-) diff --git a/browser/base/content/browser-gestureSupport.js b/browser/base/content/browser-gestureSupport.js index 152a5b52f32..d88f47c793d 100644 --- a/browser/base/content/browser-gestureSupport.js +++ b/browser/base/content/browser-gestureSupport.js @@ -195,21 +195,7 @@ let gGestureSupport = { aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_RIGHT : aEvent.DIRECTION_LEFT; - let isVerticalSwipe = false; - if (gHistorySwipeAnimation.active) { - if (aEvent.direction == aEvent.DIRECTION_UP) { - isVerticalSwipe = true; - // Force a synchronous scroll to the top of the page. - content.scrollTo(content.scrollX, 0); - } - else if (aEvent.direction == aEvent.DIRECTION_DOWN) { - isVerticalSwipe = true; - // Force a synchronous scroll to the bottom of the page. - content.scrollTo(content.scrollX, content.scrollMaxY); - } - } - - gHistorySwipeAnimation.startAnimation(isVerticalSwipe); + gHistorySwipeAnimation.startAnimation(); this._doUpdate = function GS__doUpdate(aEvent) { gHistorySwipeAnimation.updateAnimation(aEvent.delta); @@ -563,13 +549,10 @@ let gHistorySwipeAnimation = { this.isLTR = document.documentElement.mozMatchesSelector( ":-moz-locale-dir(ltr)"); this._trackedSnapshots = []; - this._startingIndex = -1; this._historyIndex = -1; this._boxWidth = -1; - this._boxHeight = -1; this._maxSnapshots = this._getMaxSnapshots(); this._lastSwipeDir = ""; - this._isVerticalSwipe = false; // We only want to activate history swipe animations if we store snapshots. // If we don't store any, we handle horizontal swipes without animations. @@ -578,7 +561,6 @@ let gHistorySwipeAnimation = { gBrowser.addEventListener("pagehide", this, false); gBrowser.addEventListener("pageshow", this, false); gBrowser.addEventListener("popstate", this, false); - gBrowser.addEventListener("DOMModalDialogClosed", this, false); gBrowser.tabContainer.addEventListener("TabClose", this, false); } }, @@ -590,7 +572,6 @@ let gHistorySwipeAnimation = { gBrowser.removeEventListener("pagehide", this, false); gBrowser.removeEventListener("pageshow", this, false); gBrowser.removeEventListener("popstate", this, false); - gBrowser.removeEventListener("DOMModalDialogClosed", this, false); gBrowser.tabContainer.removeEventListener("TabClose", this, false); this.active = false; @@ -600,32 +581,17 @@ let gHistorySwipeAnimation = { /** * Starts the swipe animation and handles fast swiping (i.e. a swipe animation * is already in progress when a new one is initiated). - * - * @param aIsVerticalSwipe - * Whether we're dealing with a vertical swipe or not. */ - startAnimation: function HSA_startAnimation(aIsVerticalSwipe) { - this._isVerticalSwipe = aIsVerticalSwipe; - + startAnimation: function HSA_startAnimation() { if (this.isAnimationRunning()) { - // If this is a horizontal scroll, or if this is a vertical scroll that - // was started while a horizontal scroll was still running, handle it as - // as a fast swipe. In the case of the latter scenario, this allows us to - // start the vertical animation without first loading the final page, or - // taking another snapshot. If vertical scrolls are initiated repeatedly - // without prior horizontal scroll we skip this and restart the animation - // from 0. - if (!this._isVerticalSwipe || this._lastSwipeDir != "") { - gBrowser.stop(); - this._lastSwipeDir = "RELOAD"; // just ensure that != "" - this._canGoBack = this.canGoBack(); - this._canGoForward = this.canGoForward(); - this._handleFastSwiping(); - } + gBrowser.stop(); + this._lastSwipeDir = "RELOAD"; // just ensure that != "" + this._canGoBack = this.canGoBack(); + this._canGoForward = this.canGoForward(); + this._handleFastSwiping(); } else { - this._startingIndex = gBrowser.webNavigation.sessionHistory.index; - this._historyIndex = this._startingIndex; + this._historyIndex = gBrowser.webNavigation.sessionHistory.index; this._canGoBack = this.canGoBack(); this._canGoForward = this.canGoForward(); if (this.active) { @@ -656,29 +622,20 @@ let gHistorySwipeAnimation = { if (!this.isAnimationRunning()) return; - // We use the following value to decrease the bounce effect when scrolling - // to the top or bottom of the page, or when swiping back/forward past the - // browsing history. This value was determined experimentally. - let dampValue = 4; - if (this._isVerticalSwipe) { - this._prevBox.collapsed = true; - this._nextBox.collapsed = true; - this._positionBox(this._curBox, -1 * aVal / dampValue); - } - else if ((aVal >= 0 && this.isLTR) || - (aVal <= 0 && !this.isLTR)) { - let tempDampValue = 1; + if ((aVal >= 0 && this.isLTR) || + (aVal <= 0 && !this.isLTR)) { + if (aVal > 1) + aVal = 1; // Cap value to avoid sliding the page further than allowed. + if (this._canGoBack) this._prevBox.collapsed = false; - else { - tempDampValue = dampValue; + else this._prevBox.collapsed = true; - } // The current page is pushed to the right (LTR) or left (RTL), // the intention is to go back. // If there is a page to go back to, it should show in the background. - this._positionBox(this._curBox, aVal / tempDampValue); + this._positionBox(this._curBox, aVal); // The forward page should be pushed offscreen all the way to the right. this._positionBox(this._nextBox, 1); @@ -694,14 +651,13 @@ let gHistorySwipeAnimation = { // For the backdrop to be visible in that case, the previous page needs // to be hidden (if it exists). if (this._canGoForward) { - this._nextBox.collapsed = false; let offset = this.isLTR ? 1 : -1; this._positionBox(this._curBox, 0); - this._positionBox(this._nextBox, offset + aVal); + this._positionBox(this._nextBox, offset + aVal); // aVal is negative } else { this._prevBox.collapsed = true; - this._positionBox(this._curBox, aVal / dampValue); + this._positionBox(this._curBox, aVal); } } }, @@ -718,14 +674,13 @@ let gHistorySwipeAnimation = { let browser = gBrowser.getBrowserForTab(aEvent.target); this._removeTrackedSnapshot(-1, browser); break; - case "DOMModalDialogClosed": - this.stopAnimation(); - break; case "pageshow": case "popstate": - if (aEvent.target != gBrowser.selectedBrowser.contentDocument) - break; - this.stopAnimation(); + if (this.isAnimationRunning()) { + if (aEvent.target != gBrowser.selectedBrowser.contentDocument) + break; + this.stopAnimation(); + } this._historyIndex = gBrowser.webNavigation.sessionHistory.index; break; case "pagehide": @@ -793,7 +748,7 @@ let gHistorySwipeAnimation = { * any. This will also result in the animation overlay to be torn down. */ swipeEndEventReceived: function HSA_swipeEndEventReceived() { - if (this._lastSwipeDir != "" && this._historyIndex != this._startingIndex) + if (this._lastSwipeDir != "") this._navigateToHistoryIndex(); else this.stopAnimation(); @@ -821,10 +776,9 @@ let gHistorySwipeAnimation = { * |this|. */ _navigateToHistoryIndex: function HSA__navigateToHistoryIndex() { - if (this._doesIndexExistInHistory(this._historyIndex)) + if (this._doesIndexExistInHistory(this._historyIndex)) { gBrowser.webNavigation.gotoIndex(this._historyIndex); - else - this.stopAnimation(); + } }, /** @@ -870,9 +824,7 @@ let gHistorySwipeAnimation = { "box"); this._container.appendChild(this._nextBox); - // Cache width and height. - this._boxWidth = this._curBox.getBoundingClientRect().width; - this._boxHeight = this._curBox.getBoundingClientRect().height; + this._boxWidth = this._curBox.getBoundingClientRect().width; // cache width }, /** @@ -886,7 +838,6 @@ let gHistorySwipeAnimation = { this._container.parentNode.removeChild(this._container); this._container = null; this._boxWidth = -1; - this._boxHeight = -1; }, /** @@ -914,14 +865,7 @@ let gHistorySwipeAnimation = { * The position (in X coordinates) to move the box element to. */ _positionBox: function HSA__positionBox(aBox, aPosition) { - let transform = ""; - - if (this._isVerticalSwipe) - transform = "translateY(" + this._boxHeight * aPosition + "px)"; - else - transform = "translateX(" + this._boxWidth * aPosition + "px)"; - - aBox.style.transform = transform; + aBox.style.transform = "translateX(" + this._boxWidth * aPosition + "px)"; }, /** @@ -1060,17 +1004,12 @@ let gHistorySwipeAnimation = { return aBlob; let img = new Image(); - let url = ""; - try { - url = URL.createObjectURL(aBlob); - img.onload = function() { - URL.revokeObjectURL(url); - }; - } - finally { - img.src = url; - return img; - } + let url = URL.createObjectURL(aBlob); + img.onload = function() { + URL.revokeObjectURL(url); + }; + img.src = url; + return img; }, /** diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index 6147aaa2fcf..17d7d053b38 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -2643,12 +2643,6 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame, nsIScrollableFrame* frameToScroll = lastScrollFrame->GetScrollTargetFrame(); if (frameToScroll) { - nsIFrame* activeRootFrame = nsLayoutUtils::GetActiveScrolledRootFor( - lastScrollFrame, nullptr); - if (!nsLayoutUtils::GetCrossDocParentFrame(activeRootFrame)) { - // Record the fact that the scroll occurred on the top-level page. - aEvent->viewPortIsScrollTargetParent = true; - } return frameToScroll; } } @@ -2714,14 +2708,7 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame, aTargetFrame->PresContext()->FrameManager()->GetRootFrame()); aOptions = static_cast(aOptions & ~START_FROM_PARENT); - if (newFrame) { - return ComputeScrollTarget(newFrame, aEvent, aOptions); - } - - // Record the fact that the scroll occurred past the bounds of the top-level - // page. - aEvent->viewPortIsScrollTargetParent = true; - return nullptr; + return newFrame ? ComputeScrollTarget(newFrame, aEvent, aOptions) : nullptr; } nsSize diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index 5008caf854e..ee7d56140c1 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -286,8 +286,7 @@ typedef NSInteger NSEventGestureAxis; #ifdef __LP64__ // Support for fluid swipe tracking. - BOOL* mCancelSwipeAnimation; - uint32_t mCurrentSwipeDir; + void (^mCancelSwipeAnimation)(); #endif // Whether this uses off-main-thread compositing. @@ -358,8 +357,7 @@ typedef NSInteger NSEventGestureAxis; // Support for fluid swipe tracking. #ifdef __LP64__ - (void)maybeTrackScrollEventAsSwipe:(NSEvent *)anEvent - scrollOverflowX:(double)overflowX - scrollOverflowY:(double)overflowY; + scrollOverflow:(double)overflow; #endif - (void)setUsingOMTCompositor:(BOOL)aUseOMTC; diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 25d5504cc53..227a6116fb4 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -2786,7 +2786,6 @@ NSEvent* gLastDragMouseDownEvent = nil; #ifdef __LP64__ mCancelSwipeAnimation = nil; - mCurrentSwipeDir = 0; #endif mTopLeftCornerMask = NULL; @@ -4053,6 +4052,18 @@ NSEvent* gLastDragMouseDownEvent = nil; return eventCancelled; // event cancelled == swipe should start } +- (void)cancelSwipeIfRunning +{ + // Clear gesture state. + mGestureState = eGestureState_None; + + if (mCancelSwipeAnimation) { + mCancelSwipeAnimation(); + [mCancelSwipeAnimation release]; + mCancelSwipeAnimation = nil; + } +} + - (void)sendSwipeEndEvent:(NSEvent *)anEvent allowedDirections:(uint32_t)aAllowedDirections { @@ -4065,19 +4076,17 @@ NSEvent* gLastDragMouseDownEvent = nil; delta:0.0]; } -// Support fluid swipe tracking on OS X 10.7 and higher. We must be careful -// to only invoke this support on a two-finger gesture that really +// Support fluid swipe tracking on OS X 10.7 and higher. We must be careful +// to only invoke this support on a horizontal two-finger gesture that really // is a swipe (and not a scroll) -- in other words, the app is responsible -// for deciding which is which. But once the decision is made, the OS tracks +// for deciding which is which. But once the decision is made, the OS tracks // the swipe until it has finished, and decides whether or not it succeeded. -// A horizontal swipe has the same functionality as the Back and Forward -// buttons. -// This method is partly based on Apple sample code available at -// developer.apple.com/library/mac/#releasenotes/Cocoa/AppKitOlderNotes.html -// (under Fluid Swipe Tracking API). +// A swipe has the same functionality as the Back and Forward buttons. For +// now swipe animation is unsupported (e.g. no bounces). This method is +// partly based on Apple sample code available at +// http://developer.apple.com/library/mac/#releasenotes/Cocoa/AppKit.html - (void)maybeTrackScrollEventAsSwipe:(NSEvent *)anEvent - scrollOverflowX:(double)overflowX - scrollOverflowY:(double)overflowY + scrollOverflow:(double)overflow { if (!nsCocoaFeatures::OnLionOrLater()) { return; @@ -4092,17 +4101,23 @@ NSEvent* gLastDragMouseDownEvent = nil; return; } - // Verify that this is a scroll wheel event with proper phase to be tracked - // by the OS. - if ([anEvent type] != NSScrollWheel || [anEvent phase] == NSEventPhaseNone) { + if ([anEvent type] != NSScrollWheel) { return; } // Only initiate tracking if the user has tried to scroll past the edge of - // the current page (as indicated by 'overflowX' or 'overflowY' being - // non-zero). Gecko only sets nsMouseScrollEvent.scrollOverflow when it's - // processing NS_MOUSE_PIXEL_SCROLL events (not NS_MOUSE_SCROLL events). - if (overflowX == 0.0 && overflowY == 0.0) { + // the current page (as indicated by 'overflow' being non-zero). Gecko only + // sets nsMouseScrollEvent.scrollOverflow when it's processing + // NS_MOUSE_PIXEL_SCROLL events (not NS_MOUSE_SCROLL events). + // nsMouseScrollEvent.scrollOverflow only indicates left or right overflow + // for horizontal NS_MOUSE_PIXEL_SCROLL events. + if (!overflow) { + return; + } + + // Only initiate tracking for gestures that have just begun -- otherwise a + // scroll to one side of the page can have a swipe tacked on to it. + if ([anEvent phase] != NSEventPhaseBegan) { return; } @@ -4110,92 +4125,43 @@ NSEvent* gLastDragMouseDownEvent = nil; if ([anEvent hasPreciseScrollingDeltas]) { deltaX = [anEvent scrollingDeltaX]; deltaY = [anEvent scrollingDeltaY]; + } else { + deltaX = [anEvent deltaX]; + deltaY = [anEvent deltaY]; } - else { + // Only initiate tracking for events whose horizontal element is at least + // eight times larger than its vertical element. This minimizes performance + // problems with vertical scrolls (by minimizing the possibility that they'll + // be misinterpreted as horizontal swipes), while still tolerating a small + // vertical element to a true horizontal swipe. The number '8' was arrived + // at by trial and error. + if ((deltaX == 0) || (fabs(deltaX) <= fabs(deltaY) * 8)) { return; } - uint32_t vDirs = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_DOWN | - (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_UP; - uint32_t direction = 0; - // Only initiate horizontal tracking for events whose horizontal element is - // at least eight times larger than its vertical element. This minimizes - // performance problems with vertical scrolls (by minimizing the possibility - // that they'll be misinterpreted as horizontal swipes), while still - // tolerating a small vertical element to a true horizontal swipe. The number - // '8' was arrived at by trial and error. - if (overflowX != 0.0 && deltaX != 0.0 && - fabsf(deltaX) > fabsf(deltaY) * 8) { - // Only initiate horizontal tracking for gestures that have just begun -- - // otherwise a scroll to one side of the page can have a swipe tacked on - // to it. - if ([anEvent phase] != NSEventPhaseBegan) - return; + // If a swipe is currently being tracked kill it -- it's been interrupted by + // another gesture or legacy scroll wheel event. + [self cancelSwipeIfRunning]; - if (deltaX < 0.0) - direction = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_RIGHT; - else - direction = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_LEFT; - } - // Only initiate vertical tracking for events whose vertical element is - // at least two times larger than its horizontal element. This minimizes - // performance problems. The number '2' was arrived at by trial and error. - else if (overflowY != 0.0 && deltaY != 0.0 && - fabsf(deltaY) > fabsf(deltaX) * 2) { - if (deltaY < 0.0) - direction = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_DOWN; - else - direction = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_UP; - - if ((mCurrentSwipeDir & vDirs) && (mCurrentSwipeDir != direction)) { - // If a swipe is currently being tracked kill it -- it's been interrupted - // by another gesture event. - if (mCancelSwipeAnimation && *mCancelSwipeAnimation == NO) { - *mCancelSwipeAnimation = YES; - mCancelSwipeAnimation = nil; - [self sendSwipeEndEvent:anEvent allowedDirections:0]; - } - return; - } - } - else { - return; - } - - // Track the direction we're going in. - mCurrentSwipeDir = direction; - - // If a swipe is currently being tracked kill it -- it's been interrupted - // by another gesture event. - if (mCancelSwipeAnimation && *mCancelSwipeAnimation == NO) { - *mCancelSwipeAnimation = YES; - mCancelSwipeAnimation = nil; - } - - uint32_t allowedDirections = 0; // We're ready to start the animation. Tell Gecko about it, and at the same // time ask it if it really wants to start an animation for this event. // This event also reports back the directions that we can swipe in. + uint32_t allowedDirections = 0; bool shouldStartSwipe = [self sendSwipeEvent:anEvent - withKind:NS_SIMPLE_GESTURE_SWIPE_START - allowedDirections:&allowedDirections - direction:direction - delta:0.0]; + withKind:NS_SIMPLE_GESTURE_SWIPE_START + allowedDirections:&allowedDirections + direction:0 + delta:0.0]; if (!shouldStartSwipe) { return; } - CGFloat min = 0.0; - CGFloat max = 0.0; - if (!(direction & vDirs)) { - min = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_RIGHT) ? - -1.0 : 0.0; - max = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_LEFT) ? - 1.0 : 0.0; - } + double min = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_RIGHT) ? -1 : 0; + double max = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_LEFT) ? 1 : 0; - __block BOOL animationCanceled = NO; + __block BOOL animationCancelled = NO; + __block BOOL geckoSwipeEventSent = NO; // At this point, anEvent is the first scroll wheel event in a two-finger // horizontal gesture that we've decided to treat as a swipe. When we call // [NSEvent trackSwipeEventWithOptions:...], the OS interprets all @@ -4211,72 +4177,67 @@ NSEvent* gLastDragMouseDownEvent = nil; // the anEvent object because it's retained by the block, see bug 682445. // The block will release it when the block goes away at the end of the // animation, or when the animation is canceled. - [anEvent trackSwipeEventWithOptions:NSEventSwipeTrackingLockDirection | - NSEventSwipeTrackingClampGestureAmount + [anEvent trackSwipeEventWithOptions:NSEventSwipeTrackingLockDirection dampenAmountThresholdMin:min max:max - usingHandler:^(CGFloat gestureAmount, - NSEventPhase phase, - BOOL isComplete, - BOOL *stop) { - uint32_t allowedDirectionsCopy = allowedDirections; - // Since this tracking handler can be called asynchronously, mGeckoChild - // might have become NULL here (our child widget might have been - // destroyed). - // Checking for gestureAmount == 0.0 also works around bug 770626, which - // happens when DispatchWindowEvent() triggers a modal dialog, which spins - // the event loop and confuses the OS. This results in several re-entrant - // calls to this handler. - if (animationCanceled || !mGeckoChild || gestureAmount == 0.0) { - *stop = YES; - animationCanceled = YES; - if (gestureAmount == 0.0 || - ((direction & vDirs) && (direction != mCurrentSwipeDir))) { - if (mCancelSwipeAnimation) - *mCancelSwipeAnimation = YES; - mCancelSwipeAnimation = nil; - [self sendSwipeEndEvent:anEvent - allowedDirections:allowedDirectionsCopy]; + usingHandler:^(CGFloat gestureAmount, NSEventPhase phase, BOOL isComplete, BOOL *stop) { + // Since this tracking handler can be called asynchronously, mGeckoChild + // might have become NULL here (our child widget might have been + // destroyed). + if (animationCancelled || !mGeckoChild) { + *stop = YES; + return; } - mCurrentSwipeDir = 0; - return; - } - // Update animation overlay to match gestureAmount. - [self sendSwipeEvent:anEvent - withKind:NS_SIMPLE_GESTURE_SWIPE_UPDATE - allowedDirections:&allowedDirectionsCopy - direction:0.0 - delta:gestureAmount]; + uint32_t allowedDirectionsCopy = allowedDirections; - if (phase == NSEventPhaseEnded) { - // The result of the swipe is now known, so the main event can be sent. - // The animation might continue even after this event was sent, so - // don't tear down the animation overlay yet. - - uint32_t directionCopy = direction; - - // gestureAmount is documented to be '-1', '0' or '1' when isComplete - // is TRUE, but the docs don't say anything about its value at other - // times. However, tests show that, when phase == NSEventPhaseEnded, - // gestureAmount is negative when it will be '-1' at isComplete, and - // positive when it will be '1'. And phase is never equal to - // NSEventPhaseEnded when gestureAmount will be '0' at isComplete. + // Update animation overlay to match gestureAmount. [self sendSwipeEvent:anEvent - withKind:NS_SIMPLE_GESTURE_SWIPE + withKind:NS_SIMPLE_GESTURE_SWIPE_UPDATE allowedDirections:&allowedDirectionsCopy - direction:directionCopy - delta:0.0]; - } + direction:0 + delta:gestureAmount]; - if (isComplete) { - [self sendSwipeEndEvent:anEvent allowedDirections:allowedDirectionsCopy]; - mCurrentSwipeDir = 0; - mCancelSwipeAnimation = nil; - } - }]; + if (phase == NSEventPhaseEnded && !geckoSwipeEventSent) { + // The result of the swipe is now known, so the main event can be sent. + // The animation might continue even after this event was sent, so + // don't tear down the animation overlay yet. + // gestureAmount is documented to be '-1', '0' or '1' when isComplete + // is TRUE, but the docs don't say anything about its value at other + // times. However, tests show that, when phase == NSEventPhaseEnded, + // gestureAmount is negative when it will be '-1' at isComplete, and + // positive when it will be '1'. And phase is never equal to + // NSEventPhaseEnded when gestureAmount will be '0' at isComplete. + uint32_t direction = gestureAmount > 0 ? + (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_LEFT : + (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_RIGHT; + // If DispatchWindowEvent() does something to trigger a modal dialog + // (which spins the event loop), the OS gets confused and makes + // several re-entrant calls to this handler, all of which have + // 'phase' set to NSEventPhaseEnded. Unless we do something about + // it, this results in an equal number of re-entrant calls to + // DispatchWindowEvent(), and to our modal-event handling code. + // Probably because of bug 478703, this really messes things up, + // and requires a force quit to get out of. We avoid this by + // avoiding re-entrant calls to DispatchWindowEvent(). See bug + // 770626. + geckoSwipeEventSent = YES; + [self sendSwipeEvent:anEvent + withKind:NS_SIMPLE_GESTURE_SWIPE + allowedDirections:&allowedDirectionsCopy + direction:direction + delta:0.0]; + } - mCancelSwipeAnimation = &animationCanceled; + if (isComplete) { + [self cancelSwipeIfRunning]; + [self sendSwipeEndEvent:anEvent allowedDirections:allowedDirections]; + } + }]; + + mCancelSwipeAnimation = [^{ + animationCancelled = YES; + } copy]; } #endif // #ifdef __LP64__ @@ -4403,7 +4364,7 @@ NSEvent* gLastDragMouseDownEvent = nil; nsAutoRetainCocoaObject kungFuDeathGrip(self); NPCocoaEvent cocoaEvent; - + nsMouseEvent geckoEvent(true, NS_MOUSE_BUTTON_UP, mGeckoChild, nsMouseEvent::eReal); [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; if ([theEvent modifierFlags] & NSControlKeyMask) @@ -4853,14 +4814,12 @@ static int32_t RoundUp(double aDouble) } #ifdef __LP64__ - // overflowDeltaX and overflowDeltaY tell us when the user has tried to - // scroll past the edge of a page (in those cases it's non-zero). - if ((wheelEvent.deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) && - (wheelEvent.viewPortIsScrollTargetParent) && - (wheelEvent.deltaX != 0.0 || wheelEvent.deltaY != 0.0)) { + // overflowDeltaX tells us when the user has tried to scroll past the edge + // of a page to the left or the right (in those cases it's non-zero). + if (wheelEvent.deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL && + wheelEvent.deltaX != 0.0) { [self maybeTrackScrollEventAsSwipe:theEvent - scrollOverflowX:wheelEvent.overflowDeltaX - scrollOverflowY:wheelEvent.overflowDeltaY]; + scrollOverflow:wheelEvent.overflowDeltaX]; } #endif // #ifdef __LP64__ diff --git a/widget/nsGUIEvent.h b/widget/nsGUIEvent.h index b82f2fc0e8c..4c2005eb5e3 100644 --- a/widget/nsGUIEvent.h +++ b/widget/nsGUIEvent.h @@ -1345,8 +1345,7 @@ public: deltaMode(nsIDOMWheelEvent::DOM_DELTA_PIXEL), customizedByUserPrefs(false), isMomentum(false), isPixelOnlyDevice(false), lineOrPageDeltaX(0), lineOrPageDeltaY(0), scrollType(SCROLL_DEFAULT), - overflowDeltaX(0.0), overflowDeltaY(0.0), - viewPortIsScrollTargetParent(false) + overflowDeltaX(0.0), overflowDeltaY(0.0) { } @@ -1425,11 +1424,6 @@ public: // it would need to check the deltaX and deltaY. double overflowDeltaX; double overflowDeltaY; - - // Whether or not the parent of the currently scrolled frame is the ViewPort. - // This is false in situations when an element on the page is being scrolled - // (such as a text field), but true when the 'page' is being scrolled. - bool viewPortIsScrollTargetParent; }; } // namespace widget From ae574b36e471218b3afe490fc35e802b84051376 Mon Sep 17 00:00:00 2001 From: Dan Minor Date: Wed, 21 Aug 2013 12:11:41 -0400 Subject: [PATCH 41/54] Bug 858622 - Make jit-tests runnable on mobile;r=terrence --- js/src/jit-test/jit_test.py | 29 ++++++++-- js/src/tests/lib/jittests.py | 101 ++++++++++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 10 deletions(-) diff --git a/js/src/jit-test/jit_test.py b/js/src/jit-test/jit_test.py index 0fe918684dd..aa33123455c 100755 --- a/js/src/jit-test/jit_test.py +++ b/js/src/jit-test/jit_test.py @@ -3,7 +3,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -import os, shlex, subprocess, sys, traceback +import os, posixpath, shlex, subprocess, sys, traceback def add_libdir_to_path(): from os.path import dirname, exists, join, realpath @@ -74,6 +74,23 @@ def main(argv): help='Run tests with all IonMonkey option combinations (ignores --jitflags)') op.add_option('-j', '--worker-count', dest='max_jobs', type=int, default=max_jobs_default, help='Number of tests to run in parallel (default %default)') + op.add_option('--remote', action='store_true', + help='Run tests on a remote device') + op.add_option('--deviceIP', action='store', + type='string', dest='device_ip', + help='IP address of remote device to test') + op.add_option('--devicePort', action='store', + type=int, dest='device_port', default=20701, + help='port of remote device to test') + op.add_option('--deviceTransport', action='store', + type='string', dest='device_transport', default='sut', + help='The transport to use to communicate with device: [adb|sut]; default=sut') + op.add_option('--remoteTestRoot', dest='remote_test_root', action='store', + type='string', default='/data/local/tests', + help='The remote directory to use as test root (eg. /data/local/tests)') + op.add_option('--localLib', dest='local_lib', action='store', + type='string', + help='The location of libraries to push -- preferably stripped') options, args = op.parse_args(argv) if len(args) < 1: @@ -171,7 +188,11 @@ def main(argv): job_list.append(new_test) prefix = [os.path.abspath(args[0])] + shlex.split(options.shell_args) - prefix += ['-f', os.path.join(jittests.LIB_DIR, 'prolog.js')] + prolog = os.path.join(jittests.LIB_DIR, 'prolog.js') + if options.remote: + prolog = posixpath.join(options.remote_test_root, 'jit-tests/lib/prolog.js') + + prefix += ['-f', prolog] if options.debug: if len(job_list) > 1: print 'Multiple tests match command line arguments, debugger can only run one' @@ -186,7 +207,9 @@ def main(argv): try: ok = None - if options.max_jobs > 1 and jittests.HAVE_MULTIPROCESSING: + if options.remote: + ok = jittests.run_tests_remote(job_list, prefix, options) + elif options.max_jobs > 1 and jittests.HAVE_MULTIPROCESSING: ok = jittests.run_tests_parallel(job_list, prefix, options) else: ok = jittests.run_tests(job_list, prefix, options) diff --git a/js/src/tests/lib/jittests.py b/js/src/tests/lib/jittests.py index 17ca875d475..2db25a21037 100755 --- a/js/src/tests/lib/jittests.py +++ b/js/src/tests/lib/jittests.py @@ -7,11 +7,12 @@ # jit_test.py -- Python harness for JavaScript trace tests. from __future__ import print_function -import os, sys, tempfile, traceback, time +import os, posixpath, sys, tempfile, traceback, time import subprocess from subprocess import Popen, PIPE from threading import Thread import signal +import StringIO try: from multiprocessing import Process, Manager, cpu_count @@ -152,15 +153,29 @@ class Test: return test - def command(self, prefix): - scriptdir_var = os.path.dirname(self.path); + def command(self, prefix, libdir, remote_prefix=None): + path = self.path + if remote_prefix: + path = self.path.replace(TEST_DIR, remote_prefix) + + scriptdir_var = os.path.dirname(path); if not scriptdir_var.endswith('/'): scriptdir_var += '/' - expr = ("const platform=%r; const libdir=%r; const scriptdir=%r" - % (sys.platform, LIB_DIR, scriptdir_var)) + + # Platforms where subprocess immediately invokes exec do not care + # whether we use double or single quotes. On windows and when using + # a remote device, however, we have to be careful to use the quote + # style that is the opposite of what the exec wrapper uses. + # This uses %r to get single quotes on windows and special cases + # the remote device. + fmt = 'const platform=%r; const libdir=%r; const scriptdir=%r' + if remote_prefix: + fmt = 'const platform="%s"; const libdir="%s"; const scriptdir="%s"' + expr = fmt % (sys.platform, libdir, scriptdir_var) + # We may have specified '-a' or '-d' twice: once via --jitflags, once # via the "|jit-test|" line. Remove dups because they are toggles. - cmd = prefix + list(set(self.jitflags)) + ['-e', expr, '-f', self.path] + cmd = prefix + list(set(self.jitflags)) + ['-e', expr, '-f', path] if self.valgrind: cmd = self.VALGRIND_CMD + cmd return cmd @@ -268,7 +283,7 @@ def run_cmd_avoid_stdio(cmdline, env, timeout): return read_and_unlink(stdoutPath), read_and_unlink(stderrPath), code def run_test(test, prefix, options): - cmd = test.command(prefix) + cmd = test.command(prefix, LIB_DIR) if options.show_cmd: print(subprocess.list2cmdline(cmd)) @@ -284,6 +299,26 @@ def run_test(test, prefix, options): out, err, code, timed_out = run(cmd, env, options.timeout) return TestOutput(test, cmd, out, err, code, None, timed_out) +def run_test_remote(test, device, prefix, options): + cmd = test.command(prefix, posixpath.join(options.remote_test_root, 'lib/'), posixpath.join(options.remote_test_root, 'tests')) + if options.show_cmd: + print(subprocess.list2cmdline(cmd)) + + env = {} + if test.tz_pacific: + env['TZ'] = 'PST8PDT' + + env['LD_LIBRARY_PATH'] = options.remote_test_root + + buf = StringIO.StringIO() + returncode = device.shell(cmd, buf, env=env, cwd=options.remote_test_root, + timeout=int(options.timeout)) + + out = buf.getvalue() + # We can't distinguish between stdout and stderr so we pass + # the same buffer to both. + return TestOutput(test, cmd, out, out, returncode, None, False) + def check_output(out, err, rc, test): if test.expect_error: # The shell exits with code 3 on uncaught exceptions. @@ -541,6 +576,58 @@ def run_tests(tests, prefix, options): ok = process_test_results(gen, len(tests), options) return ok +def get_remote_results(tests, device, prefix, options): + for test in tests: + yield run_test_remote(test, device, prefix, options) + +def push_libs(options, device): + # This saves considerable time in pushing unnecessary libraries + # to the device but needs to be updated if the dependencies change. + required_libs = ['libnss3.so', 'libmozglue.so'] + + for file in os.listdir(options.local_lib): + if file in required_libs: + remote_file = posixpath.join(options.remote_test_root, file) + device.pushFile(os.path.join(options.local_lib, file), remote_file) + +def push_progs(options, device, progs): + for local_file in progs: + remote_file = posixpath.join(options.remote_test_root, os.path.basename(local_file)) + device.pushFile(local_file, remote_file) + +def run_tests_remote(tests, prefix, options): + # Setup device with everything needed to run our tests. + from mozdevice import devicemanager, devicemanagerADB, devicemanagerSUT + + if options.device_transport == 'adb': + if options.device_ip: + dm = devicemanagerADB.DeviceManagerADB(options.device_ip, options.devicePort, packageName=None, deviceRoot=options.remoteTestRoot) + else: + dm = devicemanagerADB.DeviceManagerADB(packageName=None, deviceRoot=options.remote_test_root) + else: + dm = devicemanagerSUT.DeviceManagerSUT(options.device_ip, options.device_port, deviceRoot=options.remote_test_root) + if options.device_ip == None: + print('Error: you must provide a device IP to connect to via the --device option') + sys.exit(1) + + # Update the test root to point to our test directory. + options.remote_test_root = posixpath.join(options.remote_test_root, 'jit-tests') + + # Push js shell and libraries. + if dm.dirExists(options.remote_test_root): + dm.removeDir(options.remote_test_root) + dm.mkDir(options.remote_test_root) + push_libs(options, dm) + push_progs(options, dm, [prefix[0]]) + dm.chmodDir(options.remote_test_root) + dm.pushDir(os.path.dirname(TEST_DIR), options.remote_test_root) + prefix[0] = os.path.join(options.remote_test_root, 'js') + + # Run all tests. + gen = get_remote_results(tests, dm, prefix, options) + ok = process_test_results(gen, len(tests), options) + return ok + def parse_jitflags(options): jitflags = [ [ '-' + flag for flag in flags ] for flags in options.jitflags.split(',') ] From d57e8764ad2434d10bc47c013f1f3986bdf5bcea Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 21 Aug 2013 10:49:45 -0400 Subject: [PATCH 42/54] Bug 907749 - move -Wno-uninitialized addition to CXXFLAGS after including rules.mk; r=khuey --- dom/bindings/Makefile.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dom/bindings/Makefile.in b/dom/bindings/Makefile.in index fee696093f9..9ec45ff0c58 100644 --- a/dom/bindings/Makefile.in +++ b/dom/bindings/Makefile.in @@ -108,10 +108,6 @@ EXPORTS_GENERATED_DEST := $(DIST)/include/$(binding_include_path) EXPORTS_GENERATED_TARGET := export INSTALL_TARGETS += EXPORTS_GENERATED -ifdef GNU_CC -CXXFLAGS += -Wno-uninitialized -endif - # Install auto-generated GlobalGen files. The rules for the install must # be in the same target/subtier as GlobalGen.py, otherwise the files will not # get installed into the appropriate location as they are generated. @@ -128,6 +124,10 @@ INSTALL_TARGETS += globalgen_headers include $(topsrcdir)/config/rules.mk include $(topsrcdir)/ipc/chromium/chromium-config.mk +ifdef GNU_CC +CXXFLAGS += -Wno-uninitialized +endif + # If you change bindinggen_dependencies here, change it in # dom/bindings/test/Makefile.in too. bindinggen_dependencies := \ From 7cd2fb67373b58d6c303ce90d9c3061a94c2d3c6 Mon Sep 17 00:00:00 2001 From: Steven MacLeod Date: Wed, 21 Aug 2013 12:14:33 -0400 Subject: [PATCH 43/54] Bug 902280 - Update _lastSaveTime before writing to avoid deceeding the write interval. r=ttaubert --- browser/components/sessionstore/src/SessionSaver.jsm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/browser/components/sessionstore/src/SessionSaver.jsm b/browser/components/sessionstore/src/SessionSaver.jsm index b01dac26d43..b1977d9727c 100644 --- a/browser/components/sessionstore/src/SessionSaver.jsm +++ b/browser/components/sessionstore/src/SessionSaver.jsm @@ -262,6 +262,12 @@ let SessionSaverInternal = { return; } + // We update the time stamp before writing so that we don't write again + // too soon, if saving is requested before the write completes. Without + // this update we may save repeatedly if actions cause a runDelayed + // before writing has completed. See Bug 902280 + this.updateLastSaveTime(); + // Write (atomically) to a session file, using a tmp file. Once the session // file is successfully updated, save the time stamp of the last save and // notify the observers. From 53d2a48acc3bc472b5e38531ef9741871e39a158 Mon Sep 17 00:00:00 2001 From: Yura Zenevich Date: Wed, 21 Aug 2013 12:40:06 -0400 Subject: [PATCH 44/54] Bug 795957 - [PATCH 1/2][AccessFu] Adding support for live regions. r=eejay --- accessible/src/jsat/AccessFu.jsm | 6 + accessible/src/jsat/EventManager.jsm | 192 ++++++++++++++++++-- accessible/src/jsat/OutputGenerator.jsm | 10 +- accessible/src/jsat/Presentation.jsm | 47 +++++- accessible/src/jsat/Utils.jsm | 39 +++-- .../en-US/chrome/accessibility/AccessFu.properties | 4 + 6 files changed, 263 insertions(+), 35 deletions(-) --- accessible/src/jsat/AccessFu.jsm | 6 + accessible/src/jsat/EventManager.jsm | 192 ++++++++++++++++-- accessible/src/jsat/OutputGenerator.jsm | 10 +- accessible/src/jsat/Presentation.jsm | 47 ++++- accessible/src/jsat/Utils.jsm | 39 ++-- .../chrome/accessibility/AccessFu.properties | 4 + 6 files changed, 263 insertions(+), 35 deletions(-) diff --git a/accessible/src/jsat/AccessFu.jsm b/accessible/src/jsat/AccessFu.jsm index 71056aa9b7b..9527a5197c7 100644 --- a/accessible/src/jsat/AccessFu.jsm +++ b/accessible/src/jsat/AccessFu.jsm @@ -163,6 +163,9 @@ this.AccessFu = { Services.obs.removeObserver(this, 'Accessibility:LongPress'); Services.obs.removeObserver(this, 'Accessibility:MoveByGranularity'); + delete this._quicknavModesPref; + delete this._notifyOutputPref; + if (this.doneCallback) { this.doneCallback(); delete this.doneCallback; @@ -171,6 +174,9 @@ this.AccessFu = { _enableOrDisable: function _enableOrDisable() { try { + if (!this._activatePref) { + return; + } let activatePref = this._activatePref.value; if (activatePref == ACCESSFU_ENABLE || this._systemPref && activatePref == ACCESSFU_AUTO) diff --git a/accessible/src/jsat/EventManager.jsm b/accessible/src/jsat/EventManager.jsm index e6108694b57..4ed05e1652a 100644 --- a/accessible/src/jsat/EventManager.jsm +++ b/accessible/src/jsat/EventManager.jsm @@ -14,10 +14,15 @@ const EVENT_TEXT_CARET_MOVED = Ci.nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED; const EVENT_TEXT_INSERTED = Ci.nsIAccessibleEvent.EVENT_TEXT_INSERTED; const EVENT_TEXT_REMOVED = Ci.nsIAccessibleEvent.EVENT_TEXT_REMOVED; const EVENT_FOCUS = Ci.nsIAccessibleEvent.EVENT_FOCUS; +const EVENT_SHOW = Ci.nsIAccessibleEvent.EVENT_SHOW; +const EVENT_HIDE = Ci.nsIAccessibleEvent.EVENT_HIDE; const ROLE_INTERNAL_FRAME = Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME; const ROLE_DOCUMENT = Ci.nsIAccessibleRole.ROLE_DOCUMENT; const ROLE_CHROME_WINDOW = Ci.nsIAccessibleRole.ROLE_CHROME_WINDOW; +const ROLE_TEXT_LEAF = Ci.nsIAccessibleRole.ROLE_TEXT_LEAF; + +const TEXT_NODE = 3; Cu.import('resource://gre/modules/XPCOMUtils.jsm'); XPCOMUtils.defineLazyModuleGetter(this, 'Services', @@ -140,7 +145,11 @@ this.EventManager.prototype = { // Don't bother with non-content events in firefox. if (Utils.MozBuildApp == 'browser' && aEvent.eventType != EVENT_VIRTUALCURSOR_CHANGED && - aEvent.accessibleDocument.docType == 'window') { + // XXX Bug 442005 results in DocAccessible::getDocType returning + // NS_ERROR_FAILURE. Checking for aEvent.accessibleDocument.docType == + // 'window' does not currently work. + (aEvent.accessibleDocument.DOMDocument.doctype && + aEvent.accessibleDocument.DOMDocument.doctype.name === 'window')) { return; } @@ -219,28 +228,47 @@ this.EventManager.prototype = { this.editState = editState; break; } + case EVENT_SHOW: + { + let {liveRegion, isPolite} = this._handleLiveRegion(aEvent, + ['additions', 'all']); + // Only handle show if it is a relevant live region. + if (!liveRegion) { + break; + } + // Show for text is handled by the EVENT_TEXT_INSERTED handler. + if (aEvent.accessible.role === ROLE_TEXT_LEAF) { + break; + } + this._dequeueLiveEvent(EVENT_HIDE, liveRegion); + this.present(Presentation.liveRegion(liveRegion, isPolite, false)); + break; + } + case EVENT_HIDE: + { + let {liveRegion, isPolite} = this._handleLiveRegion( + aEvent.QueryInterface(Ci.nsIAccessibleHideEvent), + ['removals', 'all']); + // Only handle hide if it is a relevant live region. + if (!liveRegion) { + break; + } + // Hide for text is handled by the EVENT_TEXT_REMOVED handler. + if (aEvent.accessible.role === ROLE_TEXT_LEAF) { + break; + } + this._queueLiveEvent(EVENT_HIDE, liveRegion, isPolite); + break; + } case EVENT_TEXT_INSERTED: case EVENT_TEXT_REMOVED: { - if (aEvent.isFromUserInput) { - // XXX support live regions as well. - let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent); - let isInserted = event.isInserted; - let txtIface = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText); - - let text = ''; - try { - text = txtIface. - getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT); - } catch (x) { - // XXX we might have gotten an exception with of a - // zero-length text. If we did, ignore it (bug #749810). - if (txtIface.characterCount) - throw x; - } - this.present(Presentation.textChanged( - isInserted, event.start, event.length, - text, event.modifiedText)); + let {liveRegion, isPolite} = this._handleLiveRegion(aEvent, + ['text', 'all']); + if (aEvent.isFromUserInput || liveRegion) { + // Handle all text mutations coming from the user or if they happen + // on a live region. + this._handleText(aEvent, liveRegion, isPolite); } break; } @@ -258,6 +286,130 @@ this.EventManager.prototype = { } }, + _handleText: function _handleText(aEvent, aLiveRegion, aIsPolite) { + let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent); + let isInserted = event.isInserted; + let txtIface = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText); + + let text = ''; + try { + text = txtIface.getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT); + } catch (x) { + // XXX we might have gotten an exception with of a + // zero-length text. If we did, ignore it (bug #749810). + if (txtIface.characterCount) { + throw x; + } + } + // If there are embedded objects in the text, ignore them. + // Assuming changes to the descendants would already be handled by the + // show/hide event. + let modifiedText = event.modifiedText.replace(/\uFFFC/g, '').trim(); + if (!modifiedText) { + return; + } + if (aLiveRegion) { + if (aEvent.eventType === EVENT_TEXT_REMOVED) { + this._queueLiveEvent(EVENT_TEXT_REMOVED, aLiveRegion, aIsPolite, + modifiedText); + } else { + this._dequeueLiveEvent(EVENT_TEXT_REMOVED, aLiveRegion); + this.present(Presentation.liveRegion(aLiveRegion, aIsPolite, false, + modifiedText)); + } + } else { + this.present(Presentation.textChanged(isInserted, event.start, + event.length, text, modifiedText)); + } + }, + + _handleLiveRegion: function _handleLiveRegion(aEvent, aRelevant) { + if (aEvent.isFromUserInput) { + return {}; + } + let parseLiveAttrs = function parseLiveAttrs(aAccessible) { + let attrs = Utils.getAttributes(aAccessible); + if (attrs['container-live']) { + return { + live: attrs['container-live'], + relevant: attrs['container-relevant'] || 'additions text', + busy: attrs['container-busy'], + atomic: attrs['container-atomic'], + memberOf: attrs['member-of'] + }; + } + return null; + }; + // XXX live attributes are not set for hidden accessibles yet. Need to + // climb up the tree to check for them. + let getLiveAttributes = function getLiveAttributes(aEvent) { + let liveAttrs = parseLiveAttrs(aEvent.accessible); + if (liveAttrs) { + return liveAttrs; + } + let parent = aEvent.targetParent; + while (parent) { + liveAttrs = parseLiveAttrs(parent); + if (liveAttrs) { + return liveAttrs; + } + parent = parent.parent + } + return {}; + }; + let {live, relevant, busy, atomic, memberOf} = getLiveAttributes(aEvent); + // If container-live is not present or is set to |off| ignore the event. + if (!live || live === 'off') { + return {}; + } + // XXX: support busy and atomic. + + // Determine if the type of the mutation is relevant. Default is additions + // and text. + let isRelevant = Utils.matchAttributeValue(relevant, aRelevant); + if (!isRelevant) { + return {}; + } + return { + liveRegion: aEvent.accessible, + isPolite: live === 'polite' + }; + }, + + _dequeueLiveEvent: function _dequeueLiveEvent(aEventType, aLiveRegion) { + let domNode = aLiveRegion.DOMNode; + if (this._liveEventQueue && this._liveEventQueue.has(domNode)) { + let queue = this._liveEventQueue.get(domNode); + let nextEvent = queue[0]; + if (nextEvent.eventType === aEventType) { + Utils.win.clearTimeout(nextEvent.timeoutID); + queue.shift(); + if (queue.length === 0) { + this._liveEventQueue.delete(domNode) + } + } + } + }, + + _queueLiveEvent: function _queueLiveEvent(aEventType, aLiveRegion, aIsPolite, aModifiedText) { + if (!this._liveEventQueue) { + this._liveEventQueue = new WeakMap(); + } + let eventHandler = { + eventType: aEventType, + timeoutID: Utils.win.setTimeout(this.present.bind(this), + 20, // Wait for a possible EVENT_SHOW or EVENT_TEXT_INSERTED event. + Presentation.liveRegion(aLiveRegion, aIsPolite, true, aModifiedText)) + }; + + let domNode = aLiveRegion.DOMNode; + if (this._liveEventQueue.has(domNode)) { + this._liveEventQueue.get(domNode).push(eventHandler); + } else { + this._liveEventQueue.set(domNode, [eventHandler]); + } + }, + present: function present(aPresentationData) { this.sendMsgFunc("AccessFu:Present", aPresentationData); }, diff --git a/accessible/src/jsat/OutputGenerator.jsm b/accessible/src/jsat/OutputGenerator.jsm index 7e8470e8443..35ebe8306ca 100644 --- a/accessible/src/jsat/OutputGenerator.jsm +++ b/accessible/src/jsat/OutputGenerator.jsm @@ -116,7 +116,6 @@ this.OutputGenerator = { let extState = {}; aAccessible.getState(state, extState); let states = {base: state.value, ext: extState.value}; - return func.apply(this, [aAccessible, roleString, states, flags, aContext]); }, @@ -418,6 +417,15 @@ this.UtteranceGenerator = { return [gStringBundle.GetStringFromName(this.gActionMap[aActionName])]; }, + genForLiveRegion: function genForLiveRegion(aContext, aIsHide, aModifiedText) { + let utterance = []; + if (aIsHide) { + utterance.push(gStringBundle.GetStringFromName('hidden')); + } + return utterance.concat( + aModifiedText || this.genForContext(aContext).output); + }, + genForAnnouncement: function genForAnnouncement(aAnnouncement) { try { return [gStringBundle.GetStringFromName(aAnnouncement)]; diff --git a/accessible/src/jsat/Presentation.jsm b/accessible/src/jsat/Presentation.jsm index 24696229909..2cb324d261f 100644 --- a/accessible/src/jsat/Presentation.jsm +++ b/accessible/src/jsat/Presentation.jsm @@ -102,7 +102,19 @@ Presenter.prototype = { /** * Announce something. Typically an app state change. */ - announce: function announce(aAnnouncement) {} + announce: function announce(aAnnouncement) {}, + + + + /** + * Announce a live region. + * @param {PivotContext} aContext context object for an accessible. + * @param {boolean} aIsPolite A politeness level for a live region. + * @param {boolean} aIsHide An indicator of hide/remove event. + * @param {string} aModifiedText Optional modified text. + */ + liveRegion: function liveRegionShown(aContext, aIsPolite, aIsHide, + aModifiedText) {} }; /** @@ -409,6 +421,13 @@ AndroidPresenter.prototype = { fromIndex: 0 }] }; + }, + + liveRegion: function AndroidPresenter_liveRegion(aContext, aIsPolite, + aIsHide, aModifiedText) { + return this.announce( + UtteranceGenerator.genForLiveRegion(aContext, aIsHide, + aModifiedText).join(' ')); } }; @@ -451,6 +470,21 @@ SpeechPresenter.prototype = { ] } }; + }, + + liveRegion: function SpeechPresenter_liveRegion(aContext, aIsPolite, aIsHide, + aModifiedText) { + return { + type: this.type, + details: { + actions: [{ + method: 'speak', + data: UtteranceGenerator.genForLiveRegion(aContext, aIsHide, + aModifiedText).join(' '), + options: {enqueue: aIsPolite} + }] + } + }; } }; @@ -570,5 +604,16 @@ this.Presentation = { // but there really isn't a point here. return [p.announce(UtteranceGenerator.genForAnnouncement(aAnnouncement)[0]) for each (p in this.presenters)]; + }, + + liveRegion: function Presentation_liveRegion(aAccessible, aIsPolite, aIsHide, + aModifiedText) { + let context; + if (!aModifiedText) { + context = new PivotContext(aAccessible, null, -1, -1, true, + aIsHide ? true : false); + } + return [p.liveRegion(context, aIsPolite, aIsHide, aModifiedText) for ( + p of this.presenters)]; } }; diff --git a/accessible/src/jsat/Utils.jsm b/accessible/src/jsat/Utils.jsm index 9bbf9d84866..e885e8289bc 100644 --- a/accessible/src/jsat/Utils.jsm +++ b/accessible/src/jsat/Utils.jsm @@ -266,6 +266,15 @@ this.Utils = { return true; }, + matchAttributeValue: function matchAttributeValue(aAttributeValue, values) { + let attrSet = new Set(aAttributeValue.split(' ')); + for (let value of values) { + if (attrSet.has(value)) { + return value; + } + } + }, + getLandmarkName: function getLandmarkName(aAccessible) { const landmarks = [ 'banner', @@ -281,11 +290,7 @@ this.Utils = { } // Looking up a role that would match a landmark. - for (let landmark of landmarks) { - if (roles.indexOf(landmark) > -1) { - return landmark; - } - } + return this.matchAttributeValue(roles, landmarks); } }; @@ -422,12 +427,15 @@ this.Logger = { * for a given accessible and its relationship with another accessible. */ this.PivotContext = function PivotContext(aAccessible, aOldAccessible, - aStartOffset, aEndOffset) { + aStartOffset, aEndOffset, aIgnoreAncestry = false, + aIncludeInvisible = false) { this._accessible = aAccessible; this._oldAccessible = this._isDefunct(aOldAccessible) ? null : aOldAccessible; this.startOffset = aStartOffset; this.endOffset = aEndOffset; + this._ignoreAncestry = aIgnoreAncestry; + this._includeInvisible = aIncludeInvisible; } PivotContext.prototype = { @@ -497,7 +505,7 @@ PivotContext.prototype = { */ get oldAncestry() { if (!this._oldAncestry) { - if (!this._oldAccessible) { + if (!this._oldAccessible || this._ignoreAncestry) { this._oldAncestry = []; } else { this._oldAncestry = this._getAncestry(this._oldAccessible); @@ -512,7 +520,8 @@ PivotContext.prototype = { */ get currentAncestry() { if (!this._currentAncestry) { - this._currentAncestry = this._getAncestry(this._accessible); + this._currentAncestry = this._ignoreAncestry ? [] : + this._getAncestry(this._accessible); } return this._currentAncestry; }, @@ -524,7 +533,7 @@ PivotContext.prototype = { */ get newAncestry() { if (!this._newAncestry) { - this._newAncestry = [currentAncestor for ( + this._newAncestry = this._ignoreAncestry ? [] : [currentAncestor for ( [index, currentAncestor] of Iterator(this.currentAncestry)) if ( currentAncestor !== this.oldAncestry[index])]; } @@ -543,9 +552,14 @@ PivotContext.prototype = { } let child = aAccessible.firstChild; while (child) { - let state = {}; - child.getState(state, {}); - if (!(state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE)) { + let include; + if (this._includeInvisible) { + include = true; + } else { + let [state,] = Utils.getStates(child); + include = !(state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE); + } + if (include) { if (aPreorder) { yield child; [yield node for (node of this._traverse(child, aPreorder, aStop))]; @@ -703,7 +717,6 @@ PrefCache.prototype = { if (!this.type) { this.type = aBranch.getPrefType(this.name); } - switch (this.type) { case Ci.nsIPrefBranch.PREF_STRING: return aBranch.getCharPref(this.name); diff --git a/dom/locales/en-US/chrome/accessibility/AccessFu.properties b/dom/locales/en-US/chrome/accessibility/AccessFu.properties index 8c727c76399..e9e3eb5d56d 100644 --- a/dom/locales/en-US/chrome/accessibility/AccessFu.properties +++ b/dom/locales/en-US/chrome/accessibility/AccessFu.properties @@ -129,6 +129,10 @@ expandAction = expanded activateAction = activated cycleAction = cycled +# Live regions +# 'hidden' will be spoken when something disappears in a live region. +hidden = hidden + # Tab states tabLoading = loading tabLoaded = loaded From c4f221c0861cdd5ae2709555c6571d73d4ed3b76 Mon Sep 17 00:00:00 2001 From: Yura Zenevich Date: Wed, 21 Aug 2013 12:40:18 -0400 Subject: [PATCH 45/54] Bug 795957 - [PATCH 2/2][AccessFu] Tests for live region support. r=eeejay, marcoz --- accessible/tests/mochitest/jsat/Makefile.in | 1 + accessible/tests/mochitest/jsat/jsatcommon.js | 64 ++++ accessible/tests/mochitest/jsat/test_alive.html | 64 ++--- .../tests/mochitest/jsat/test_live_regions.html | 342 ++++++++++++++++++++ 4 files changed, 432 insertions(+), 39 deletions(-) create mode 100644 accessible/tests/mochitest/jsat/test_live_regions.html --- accessible/tests/mochitest/jsat/Makefile.in | 1 + accessible/tests/mochitest/jsat/jsatcommon.js | 64 ++++ .../tests/mochitest/jsat/test_alive.html | 64 ++-- .../mochitest/jsat/test_live_regions.html | 342 ++++++++++++++++++ 4 files changed, 432 insertions(+), 39 deletions(-) create mode 100644 accessible/tests/mochitest/jsat/test_live_regions.html diff --git a/accessible/tests/mochitest/jsat/Makefile.in b/accessible/tests/mochitest/jsat/Makefile.in index e281764d8ba..0fb627a9b62 100644 --- a/accessible/tests/mochitest/jsat/Makefile.in +++ b/accessible/tests/mochitest/jsat/Makefile.in @@ -18,6 +18,7 @@ test_alive.html \ test_braille.html \ test_explicit_names.html \ test_landmarks.html \ +test_live_regions.html \ test_tables.html \ test_utterance_order.html \ $(NULL) diff --git a/accessible/tests/mochitest/jsat/jsatcommon.js b/accessible/tests/mochitest/jsat/jsatcommon.js index 1842c1b5314..640989252b6 100644 --- a/accessible/tests/mochitest/jsat/jsatcommon.js +++ b/accessible/tests/mochitest/jsat/jsatcommon.js @@ -21,6 +21,68 @@ var AccessFuTest = { } }, + _registerListener: function AccessFuTest__registerListener(aWaitForMessage, aListenerFunc) { + var listener = { + observe: function observe(aMessage) { + // Ignore unexpected messages. + if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) { + return; + } + if (aMessage.message.indexOf(aWaitForMessage) < 0) { + return; + } + aListenerFunc.apply(listener); + } + }; + Services.console.registerListener(listener); + return listener; + }, + + on_log: function AccessFuTest_on_log(aWaitForMessage, aListenerFunc) { + return this._registerListener(aWaitForMessage, aListenerFunc); + }, + + off_log: function AccessFuTest_off_log(aListener) { + Services.console.unregisterListener(aListener); + }, + + once_log: function AccessFuTest_once_log(aWaitForMessage, aListenerFunc) { + return this._registerListener(aWaitForMessage, + function listenAndUnregister() { + Services.console.unregisterListener(this); + aListenerFunc(); + }); + }, + + _addObserver: function AccessFuTest__addObserver(aWaitForData, aListener) { + var listener = function listener(aSubject, aTopic, aData) { + var data = JSON.parse(aData)[1]; + // Ignore non-relevant outputs. + if (!data) { + return; + } + isDeeply(data.details.actions, aWaitForData, "Data is correct"); + aListener.apply(listener); + }; + Services.obs.addObserver(listener, 'accessfu-output', false); + return listener; + }, + + on: function AccessFuTest_on(aWaitForData, aListener) { + return this._addObserver(aWaitForData, aListener); + }, + + off: function AccessFuTest_off(aListener) { + Services.obs.removeObserver(aListener, 'accessfu-output'); + }, + + once: function AccessFuTest_once(aWaitForData, aListener) { + return this._addObserver(aWaitForData, function observerAndRemove() { + Services.obs.removeObserver(this, 'accessfu-output'); + aListener(); + }); + }, + _waitForExplicitFinish: false, waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() { @@ -38,6 +100,7 @@ var AccessFuTest = { SimpleTest.finish(); }; // Tear down accessibility and make AccessFu stop. + SpecialPowers.setIntPref("accessibility.accessfu.notify_output", 0); SpecialPowers.setIntPref("accessibility.accessfu.activate", 0); }, @@ -87,5 +150,6 @@ var AccessFuTest = { // Invoke the whole thing. SpecialPowers.setIntPref("accessibility.accessfu.activate", 1); + SpecialPowers.setIntPref("accessibility.accessfu.notify_output", 1); } }; diff --git a/accessible/tests/mochitest/jsat/test_alive.html b/accessible/tests/mochitest/jsat/test_alive.html index c381e468f2b..ee030aefec6 100644 --- a/accessible/tests/mochitest/jsat/test_alive.html +++ b/accessible/tests/mochitest/jsat/test_alive.html @@ -18,52 +18,38 @@ AccessFuTest.nextTest(); } - function makeEventManagerListener(waitForMessage, callback) { - return { - observe: function observe(aMessage) { - // Ignore unexpected messages. - if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) { - return; - } - if (aMessage.message.indexOf(waitForMessage) < 0) { - return; - } - Services.console.unregisterListener(this); - callback(); - } - }; + // Listen for 'EventManager.stop' and enable AccessFu again. + function onStop() { + ok(true, "EventManager was stopped."); + isnot(AccessFu._enabled, true, "AccessFu was disabled."); + AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest); + AccessFu._enable(); } - function testEventManagerStartStop() { - // Firs listen for initial 'EventManager.start' and disable AccessFu. - var initialStartListener = makeEventManagerListener("EventManager.start", - function () { - ok(true, "EventManager was started."); - Services.console.registerListener(stopListener); - AccessFu._disable(); - }); - // Listen for 'EventManager.stop' and enable AccessFu again. - var stopListener = makeEventManagerListener("EventManager.stop", - function () { - ok(true, "EventManager was stopped."); - isnot(AccessFu._enabled, true, "AccessFu was disabled."); - Services.console.registerListener(finalStartListener); - AccessFu._enable(); - }); - // Make sure EventManager is started again. - var finalStartListener = makeEventManagerListener("EventManager.start", - function () { - ok(true, "EventManager was started again."); - ok(AccessFu._enabled, "AccessFu was enabled again."); - AccessFuTest.finish(); - }); + // Make sure EventManager is started again. + function onFinalStart() { + ok(true, "EventManager was started again."); + ok(AccessFu._enabled, "AccessFu was enabled again."); + AccessFuTest.finish(); + } - Services.console.registerListener(initialStartListener); + // Listen for initial 'EventManager.start' and disable AccessFu. + function onInitialStart() { + ok(true, "EventManager was started."); + AccessFuTest.once_log("EventManager.stop", AccessFuTest.nextTest); + AccessFu._disable(); + } + + function init() { + AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest); } function doTest() { AccessFuTest.addFunc(confirmAccessFuStart); - AccessFuTest.addFunc(testEventManagerStartStop); + AccessFuTest.addFunc(init); + AccessFuTest.addFunc(onInitialStart); + AccessFuTest.addFunc(onStop); + AccessFuTest.addFunc(onFinalStart); AccessFuTest.waitForExplicitFinish(); AccessFuTest.runTests(); // Will call SimpleTest.finish(); } diff --git a/accessible/tests/mochitest/jsat/test_live_regions.html b/accessible/tests/mochitest/jsat/test_live_regions.html new file mode 100644 index 00000000000..5e518093a4b --- /dev/null +++ b/accessible/tests/mochitest/jsat/test_live_regions.html @@ -0,0 +1,342 @@ + + + + AccessFu tests for live regions support + + + + + + + + + + + Mozilla Bug 795957 + +
+

+ +

+
+    

I should not be announced 1

+

I should not be announced 2

+

I should not be announced 3

+

I will be hidden

+ +
+

I should not be announced 1

+
+
+

I should not be announced 2

+
+
+

I should not be announced 3

+
+
+

I will be hidden

+
+ + + + + + +
+ +
+
+ +
+
+ +
+
+ +
+ +
+

I will be hidden

+
+ + + + +
+

I am replaced

+
+ +

I am going to be replaced

+ +

+

Text Removed

+ +
+

+
+
+

Descendant Text Removed

+
+
+

+
+
+

Descendant Text Removed

+
+
+ + \ No newline at end of file From cf17ea703f438b15019687538fd7077c63285898 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Wed, 21 Aug 2013 12:46:48 -0400 Subject: [PATCH 46/54] Backed out changeset 5f8ae314d872 (bug 750932) for Werror bustage on a CLOSED TREE. --- netwerk/streamconv/converters/nsMultiMixedConv.cpp | 3 +-- netwerk/test/unit/xpcshell.ini | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/netwerk/streamconv/converters/nsMultiMixedConv.cpp b/netwerk/streamconv/converters/nsMultiMixedConv.cpp index bd8fa0249ac..024c057ce04 100644 --- a/netwerk/streamconv/converters/nsMultiMixedConv.cpp +++ b/netwerk/streamconv/converters/nsMultiMixedConv.cpp @@ -565,8 +565,7 @@ nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context, int32_t tokenLinefeed = 1; while ( (token = FindToken(cursor, bufLen)) ) { - if (((token + mTokenLen - cursor) < bufLen) && - (*(token + mTokenLen + 1) == '-')) { + if (*(token+mTokenLen+1) == '-') { // This was the last delimiter so we can stop processing rv = SendData(cursor, LengthToToken(cursor, token)); if (NS_FAILED(rv)) return rv; diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 171c09a5737..a577e083331 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -129,6 +129,8 @@ skip-if = bits != 32 [test_multipart_byteranges.js] [test_multipart_streamconv.js] [test_multipart_streamconv_missing_lead_boundary.js] +# Bug 750932: test fails under AddressSanitizer +fail-if = asan [test_nestedabout_serialize.js] [test_net_addr.js] # Bug 732363: test fails on windows for unknown reasons. From 30c0fa1d8aa0f83c2bfe0b5f13667b9ab3670ca6 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Wed, 21 Aug 2013 13:27:00 -0400 Subject: [PATCH 47/54] Backed out changeset d389df23ffc9 (bug 884281) for OSX crashes. CLOSED TREE --- xpcom/base/AvailableMemoryTracker.cpp | 14 +++++------ xpcom/base/nsDebugImpl.cpp | 7 +++--- xpcom/base/nsExceptionService.cpp | 14 +++++------ xpcom/base/nsExceptionService.h | 3 +-- xpcom/base/nsMemoryImpl.cpp | 6 ++--- xpcom/base/nsMemoryImpl.h | 4 +--- xpcom/base/nsMemoryInfoDumper.cpp | 6 ++--- xpcom/string/src/nsSubstring.cpp | 34 +++++++++++++-------------- xpcom/threads/TimerThread.cpp | 2 +- xpcom/threads/TimerThread.h | 3 +-- xpcom/threads/nsTimerImpl.cpp | 14 +++++------ 11 files changed, 49 insertions(+), 58 deletions(-) diff --git a/xpcom/base/AvailableMemoryTracker.cpp b/xpcom/base/AvailableMemoryTracker.cpp index b1985db99c4..75f31aea9b9 100644 --- a/xpcom/base/AvailableMemoryTracker.cpp +++ b/xpcom/base/AvailableMemoryTracker.cpp @@ -7,6 +7,7 @@ #include "mozilla/AvailableMemoryTracker.h" #include "prinrval.h" +#include "pratom.h" #include "prenv.h" #include "nsIMemoryReporter.h" @@ -18,7 +19,6 @@ #include "nsPrintfCString.h" #include "nsThread.h" -#include "mozilla/Atomics.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" @@ -128,9 +128,9 @@ uint32_t sLowCommitSpaceThreshold = 0; uint32_t sLowPhysicalMemoryThreshold = 0; uint32_t sLowMemoryNotificationIntervalMS = 0; -Atomic sNumLowVirtualMemEvents; -Atomic sNumLowCommitSpaceEvents; -Atomic sNumLowPhysicalMemEvents; +uint32_t sNumLowVirtualMemEvents = 0; +uint32_t sNumLowCommitSpaceEvents = 0; +uint32_t sNumLowPhysicalMemEvents = 0; WindowsDllInterceptor sKernel32Intercept; WindowsDllInterceptor sGdi32Intercept; @@ -212,19 +212,19 @@ void CheckMemAvailable() // notification. We'll probably crash if we run out of virtual memory, // so don't worry about firing this notification too often. LOG("Detected low virtual memory."); - ++sNumLowVirtualMemEvents; + PR_ATOMIC_INCREMENT(&sNumLowVirtualMemEvents); NS_DispatchEventualMemoryPressure(MemPressure_New); } else if (stat.ullAvailPageFile < sLowCommitSpaceThreshold * 1024 * 1024) { LOG("Detected low available page file space."); if (MaybeScheduleMemoryPressureEvent()) { - ++sNumLowCommitSpaceEvents; + PR_ATOMIC_INCREMENT(&sNumLowCommitSpaceEvents); } } else if (stat.ullAvailPhys < sLowPhysicalMemoryThreshold * 1024 * 1024) { LOG("Detected low physical memory."); if (MaybeScheduleMemoryPressureEvent()) { - ++sNumLowPhysicalMemEvents; + PR_ATOMIC_INCREMENT(&sNumLowPhysicalMemEvents); } } } diff --git a/xpcom/base/nsDebugImpl.cpp b/xpcom/base/nsDebugImpl.cpp index 16f243aa64f..e751d993b0d 100644 --- a/xpcom/base/nsDebugImpl.cpp +++ b/xpcom/base/nsDebugImpl.cpp @@ -6,8 +6,6 @@ // Chromium headers must come before Mozilla headers. #include "base/process_util.h" -#include "mozilla/Atomics.h" - #include "nsDebugImpl.h" #include "nsDebug.h" #ifdef MOZ_CRASHREPORTER @@ -22,6 +20,7 @@ #include "prerror.h" #include "prerr.h" #include "prenv.h" +#include "pratom.h" #ifdef ANDROID #include @@ -84,7 +83,7 @@ using namespace mozilla; static bool sIsMultiprocess = false; static const char *sMultiprocessDescription = NULL; -static Atomic gAssertionCount; +static int32_t gAssertionCount = 0; NS_IMPL_QUERY_INTERFACE2(nsDebugImpl, nsIDebug, nsIDebug2) @@ -391,7 +390,7 @@ NS_DebugBreak(uint32_t aSeverity, const char *aStr, const char *aExpr, } // Now we deal with assertions - gAssertionCount++; + PR_ATOMIC_INCREMENT(&gAssertionCount); switch (GetAssertBehavior()) { case NS_ASSERT_WARN: diff --git a/xpcom/base/nsExceptionService.cpp b/xpcom/base/nsExceptionService.cpp index 922ff5321ec..ab3cf4d66b8 100644 --- a/xpcom/base/nsExceptionService.cpp +++ b/xpcom/base/nsExceptionService.cpp @@ -52,7 +52,7 @@ public: nsExceptionManager *mNextThread; // not ref-counted. nsExceptionService *mService; // not ref-counted #ifdef DEBUG - static Atomic totalInstances; + static int32_t totalInstances; #endif private: @@ -61,7 +61,7 @@ private: #ifdef DEBUG -Atomic nsExceptionManager::totalInstances; +int32_t nsExceptionManager::totalInstances = 0; #endif // Note this object is single threaded - the service itself ensures @@ -76,7 +76,7 @@ nsExceptionManager::nsExceptionManager(nsExceptionService *svc) : { /* member initializers and constructor code */ #ifdef DEBUG - ++totalInstances; + PR_ATOMIC_INCREMENT(&totalInstances); #endif } @@ -84,7 +84,7 @@ nsExceptionManager::~nsExceptionManager() { /* destructor code */ #ifdef DEBUG - --totalInstances; + PR_ATOMIC_DECREMENT(&totalInstances); #endif // DEBUG } @@ -120,7 +120,7 @@ Mutex *nsExceptionService::sLock = nullptr; nsExceptionManager *nsExceptionService::firstThread = nullptr; #ifdef DEBUG -Atomic nsExceptionService::totalInstances; +int32_t nsExceptionService::totalInstances = 0; #endif NS_IMPL_ISUPPORTS3(nsExceptionService, @@ -132,7 +132,7 @@ nsExceptionService::nsExceptionService() : mProviders(4, true) /* small, thread-safe hashtable */ { #ifdef DEBUG - if (++totalInstances != 1) { + if (PR_ATOMIC_INCREMENT(&totalInstances)!=1) { NS_ERROR("The nsExceptionService is a singleton!"); } #endif @@ -157,7 +157,7 @@ nsExceptionService::~nsExceptionService() Shutdown(); /* destructor code */ #ifdef DEBUG - --totalInstances; + PR_ATOMIC_DECREMENT(&totalInstances); #endif } diff --git a/xpcom/base/nsExceptionService.h b/xpcom/base/nsExceptionService.h index e98e2b5b356..ee86f9a4285 100644 --- a/xpcom/base/nsExceptionService.h +++ b/xpcom/base/nsExceptionService.h @@ -6,7 +6,6 @@ #ifndef nsExceptionService_h__ #define nsExceptionService_h__ -#include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/Mutex.h" @@ -53,7 +52,7 @@ public: static unsigned tlsIndex; static void ThreadDestruct( void *data ); #ifdef DEBUG - static mozilla::Atomic totalInstances; + static int32_t totalInstances; #endif private: diff --git a/xpcom/base/nsMemoryImpl.cpp b/xpcom/base/nsMemoryImpl.cpp index d09a9136af8..a67372a6d2c 100644 --- a/xpcom/base/nsMemoryImpl.cpp +++ b/xpcom/base/nsMemoryImpl.cpp @@ -114,7 +114,7 @@ nsMemoryImpl::FlushMemory(const PRUnichar* aReason, bool aImmediate) } } - int32_t lastVal = sIsFlushing.exchange(1); + int32_t lastVal = PR_ATOMIC_SET(&sIsFlushing, 1); if (lastVal) return NS_OK; @@ -183,8 +183,8 @@ nsMemoryImpl::FlushEvent::Run() return NS_OK; } -mozilla::Atomic -nsMemoryImpl::sIsFlushing; +int32_t +nsMemoryImpl::sIsFlushing = 0; PRIntervalTime nsMemoryImpl::sLastFlushTime = 0; diff --git a/xpcom/base/nsMemoryImpl.h b/xpcom/base/nsMemoryImpl.h index ab27d01c199..c73b8e2e6ee 100644 --- a/xpcom/base/nsMemoryImpl.h +++ b/xpcom/base/nsMemoryImpl.h @@ -6,8 +6,6 @@ #ifndef nsMemoryImpl_h__ #define nsMemoryImpl_h__ -#include "mozilla/Atomics.h" - #include "nsIMemory.h" #include "nsIRunnable.h" #include "prtime.h" @@ -39,7 +37,7 @@ protected: const PRUnichar* mReason; }; - static mozilla::Atomic sIsFlushing; + static int32_t sIsFlushing; static FlushEvent sFlushEvent; static PRIntervalTime sLastFlushTime; }; diff --git a/xpcom/base/nsMemoryInfoDumper.cpp b/xpcom/base/nsMemoryInfoDumper.cpp index 728edf18aff..417e28b05ab 100644 --- a/xpcom/base/nsMemoryInfoDumper.cpp +++ b/xpcom/base/nsMemoryInfoDumper.cpp @@ -6,7 +6,6 @@ #include "mozilla/nsMemoryInfoDumper.h" -#include "mozilla/Atomics.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/FileUtils.h" #include "mozilla/Preferences.h" @@ -143,7 +142,7 @@ static int sGCAndCCDumpSignum; // SIGRTMIN + 2 // This is the write-end of a pipe that we use to notice when a // dump-about-memory signal occurs. -static Atomic sDumpAboutMemoryPipeWriteFd(-1); +static int sDumpAboutMemoryPipeWriteFd = -1; void DumpAboutMemorySignalHandler(int aSignum) @@ -330,7 +329,8 @@ public: // 2) open a new fd with the same number as sDumpAboutMemoryPipeWriteFd // had. // 3) receive a signal, then write to the fd. - int pipeWriteFd = sDumpAboutMemoryPipeWriteFd.exchange(-1); + int pipeWriteFd = sDumpAboutMemoryPipeWriteFd; + PR_ATOMIC_SET(&sDumpAboutMemoryPipeWriteFd, -1); close(pipeWriteFd); FdWatcher::StopWatching(); diff --git a/xpcom/string/src/nsSubstring.cpp b/xpcom/string/src/nsSubstring.cpp index 7f8943a2695..1a9a730cd20 100644 --- a/xpcom/string/src/nsSubstring.cpp +++ b/xpcom/string/src/nsSubstring.cpp @@ -8,7 +8,6 @@ #define ENABLE_STRING_STATS #endif -#include "mozilla/Atomics.h" #include "mozilla/MemoryReporting.h" #ifdef ENABLE_STRING_STATS @@ -21,12 +20,11 @@ #include "nsStringBuffer.h" #include "nsDependentString.h" #include "nsMemory.h" +#include "pratom.h" #include "prprf.h" #include "nsStaticAtom.h" #include "nsCOMPtr.h" -using mozilla::Atomic; - // --------------------------------------------------------------------------- static PRUnichar gNullChar = 0; @@ -52,31 +50,31 @@ class nsStringStats return; printf("nsStringStats\n"); - printf(" => mAllocCount: % 10d\n", (int)mAllocCount); - printf(" => mReallocCount: % 10d\n", (int)mReallocCount); - printf(" => mFreeCount: % 10d", (int)mFreeCount); + printf(" => mAllocCount: % 10d\n", mAllocCount); + printf(" => mReallocCount: % 10d\n", mReallocCount); + printf(" => mFreeCount: % 10d", mFreeCount); if (mAllocCount > mFreeCount) printf(" -- LEAKED %d !!!\n", mAllocCount - mFreeCount); else printf("\n"); - printf(" => mShareCount: % 10d\n", (int)mShareCount); - printf(" => mAdoptCount: % 10d\n", (int)mAdoptCount); - printf(" => mAdoptFreeCount: % 10d", (int)mAdoptFreeCount); + printf(" => mShareCount: % 10d\n", mShareCount); + printf(" => mAdoptCount: % 10d\n", mAdoptCount); + printf(" => mAdoptFreeCount: % 10d", mAdoptFreeCount); if (mAdoptCount > mAdoptFreeCount) printf(" -- LEAKED %d !!!\n", mAdoptCount - mAdoptFreeCount); else printf("\n"); } - Atomic mAllocCount; - Atomic mReallocCount; - Atomic mFreeCount; - Atomic mShareCount; - Atomic mAdoptCount; - Atomic mAdoptFreeCount; + int32_t mAllocCount; + int32_t mReallocCount; + int32_t mFreeCount; + int32_t mShareCount; + int32_t mAdoptCount; + int32_t mAdoptFreeCount; }; static nsStringStats gStringStats; -#define STRING_STAT_INCREMENT(_s) (gStringStats.m ## _s ## Count)++ +#define STRING_STAT_INCREMENT(_s) PR_ATOMIC_INCREMENT(&gStringStats.m ## _s ## Count) #else #define STRING_STAT_INCREMENT(_s) #endif @@ -150,7 +148,7 @@ class nsACStringAccessor : public nsACString void nsStringBuffer::AddRef() { - ++mRefCount; + PR_ATOMIC_INCREMENT(&mRefCount); STRING_STAT_INCREMENT(Share); NS_LOG_ADDREF(this, mRefCount, "nsStringBuffer", sizeof(*this)); } @@ -158,7 +156,7 @@ nsStringBuffer::AddRef() void nsStringBuffer::Release() { - int32_t count = --mRefCount; + int32_t count = PR_ATOMIC_DECREMENT(&mRefCount); NS_LOG_RELEASE(this, count, "nsStringBuffer"); if (count == 0) { diff --git a/xpcom/threads/TimerThread.cpp b/xpcom/threads/TimerThread.cpp index 68803aeeb94..9053f8f4118 100644 --- a/xpcom/threads/TimerThread.cpp +++ b/xpcom/threads/TimerThread.cpp @@ -84,7 +84,7 @@ nsresult TimerThread::Init() return NS_OK; } - if (mInitInProgress.exchange(1) == 0) { + if (PR_ATOMIC_SET(&mInitInProgress, 1) == 0) { // We hold on to mThread to keep the thread alive. nsresult rv = NS_NewThread(getter_AddRefs(mThread), this); if (NS_FAILED(rv)) { diff --git a/xpcom/threads/TimerThread.h b/xpcom/threads/TimerThread.h index aba1e4ba8ed..a9699c29f3c 100644 --- a/xpcom/threads/TimerThread.h +++ b/xpcom/threads/TimerThread.h @@ -15,7 +15,6 @@ #include "nsTArray.h" -#include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/Monitor.h" #include "mozilla/TimeStamp.h" @@ -53,7 +52,7 @@ public: private: ~TimerThread(); - mozilla::Atomic mInitInProgress; + int32_t mInitInProgress; bool mInitialized; // These two internal helper methods must be called while mLock is held. diff --git a/xpcom/threads/nsTimerImpl.cpp b/xpcom/threads/nsTimerImpl.cpp index bb59fc98bf1..0f1f1412902 100644 --- a/xpcom/threads/nsTimerImpl.cpp +++ b/xpcom/threads/nsTimerImpl.cpp @@ -12,13 +12,11 @@ #include "plarena.h" #include "pratom.h" #include "GeckoProfiler.h" -#include "mozilla/Atomics.h" -using mozilla::Atomic; using mozilla::TimeDuration; using mozilla::TimeStamp; -static Atomic gGenerator; +static int32_t gGenerator = 0; static TimerThread* gThread = nullptr; #ifdef DEBUG_TIMERS @@ -116,7 +114,7 @@ public: MOZ_ASSERT(gThread->IsOnTimerThread(), "nsTimer must always be allocated on the timer thread"); - sAllocatorUsers++; + PR_ATOMIC_INCREMENT(&sAllocatorUsers); } #ifdef DEBUG_TIMERS @@ -142,19 +140,19 @@ private: MOZ_ASSERT(!sCanDeleteAllocator || sAllocatorUsers > 0, "This will result in us attempting to deallocate the nsTimerEvent allocator twice"); - sAllocatorUsers--; + PR_ATOMIC_DECREMENT(&sAllocatorUsers); } nsRefPtr mTimer; int32_t mGeneration; static TimerEventAllocator* sAllocator; - static Atomic sAllocatorUsers; + static int32_t sAllocatorUsers; static bool sCanDeleteAllocator; }; TimerEventAllocator* nsTimerEvent::sAllocator = nullptr; -Atomic nsTimerEvent::sAllocatorUsers; +int32_t nsTimerEvent::sAllocatorUsers = 0; bool nsTimerEvent::sCanDeleteAllocator = false; namespace { @@ -344,7 +342,7 @@ nsresult nsTimerImpl::InitCommon(uint32_t aType, uint32_t aDelay) gThread->RemoveTimer(this); mCanceled = false; mTimeout = TimeStamp(); - mGeneration = gGenerator++; + mGeneration = PR_ATOMIC_INCREMENT(&gGenerator); mType = (uint8_t)aType; SetDelayInternal(aDelay); From 762fcc58bcd63868b44824e132f98ea034954989 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Wed, 21 Aug 2013 13:56:53 -0400 Subject: [PATCH 48/54] Backed out changeset 7da415c9c566 (bug 517765) for PGO bustage. CLOSED TREE --- js/src/Makefile.in | 36 +--------- js/src/configure.in | 8 --- js/src/make-source-package.sh | 123 ---------------------------------- 3 files changed, 2 insertions(+), 165 deletions(-) delete mode 100644 js/src/make-source-package.sh diff --git a/js/src/Makefile.in b/js/src/Makefile.in index cddaaf59310..5a736a6109a 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -342,17 +342,12 @@ endif endif DIST_GARBAGE = config.cache config.log config.status* \ - config/autoconf.mk config/emptyvars.mk \ - unallmakefiles $(JS_CONFIG_NAME) js-config.h js-confdefs.h \ - backend.mk config/backend.mk devtools/backend.mk editline/backend.mk \ - gdb/backend.mk jsapi-tests/backend.mk shell/backend.mk tests/backend.mk \ - backend.RecursiveMakeBackend.built backend.RecursiveMakeBackend.built.pp \ - devtools/rootAnalysis/Makefile + config/autoconf.mk \ + unallmakefiles $(JS_CONFIG_NAME) js-config.h js-confdefs.h distclean:: cat unallmakefiles | $(XARGS) rm -f $(RM) $(DIST_GARBAGE) - rm -rf intl _virtualenv DEFINES += -DEXPORT_JS_API @@ -676,30 +671,3 @@ endif # # END kludges for the Nitro assembler ############################################### - -############################################### -# Generating source package tarballs -# (only possible when tar is found) -ifneq (,$(TAR)) - -source-package: - SRCDIR=$(srcdir) \ - DIST=$(DIST) \ - MAKE=$(MAKE) \ - MKDIR=$(MKDIR) \ - TAR=$(TAR) \ - MOZJS_MAJOR_VERSION=$(MOZJS_MAJOR_VERSION) \ - MOZJS_MINOR_VERSION=$(MOZJS_MINOR_VERSION) \ - MOZJS_PATCH_VERSION=$(MOZJS_PATCH_VERSION) \ - MOZJS_ALPHA=$(MOZJS_ALPHA) \ - $(srcdir)/make-source-package.sh - -clean:: - DIST=$(DIST) \ - MOZJS_MAJOR_VERSION=$(MOZJS_MAJOR_VERSION) \ - MOZJS_MINOR_VERSION=$(MOZJS_MINOR_VERSION) \ - MOZJS_PATCH_VERSION=$(MOZJS_PATCH_VERSION) \ - MOZJS_ALPHA=$(MOZJS_ALPHA) \ - $(srcdir)/make-source-package.sh clean - -endif diff --git a/js/src/configure.in b/js/src/configure.in index 5f17b3a9ca0..f3e9aee6a7f 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -571,14 +571,6 @@ dnl ======================================================== AC_PROG_INSTALL AC_PROG_LN_S -AC_MSG_CHECKING([for tar archiver]) -AC_CHECK_PROGS(TAR, gnutar gtar tar, "") -if test -z "$TAR"; then - AC_MSG_WARN([no tar archiver found in \$PATH]) -fi -AC_MSG_RESULT([$TAR]) -AC_SUBST(TAR) - if test -z "$TINDERBOX_SKIP_PERL_VERSION_CHECK"; then AC_MSG_CHECKING([for minimum required perl version >= $PERL_VERSION]) _perl_version=`PERL_VERSION=$PERL_VERSION $PERL -e 'print "$]"; if ($] >= $ENV{PERL_VERSION}) { exit(0); } else { exit(1); }' 2>&5` diff --git a/js/src/make-source-package.sh b/js/src/make-source-package.sh deleted file mode 100644 index 2db12e3dfd4..00000000000 --- a/js/src/make-source-package.sh +++ /dev/null @@ -1,123 +0,0 @@ -#!/bin/sh -# need these environment vars: -echo "Environment:" -echo " MAKE = $MAKE" -echo " MKDIR = $MKDIR" -echo " TAR = $TAR" -echo " DIST = $DIST" -echo " SRCDIR = $SRCDIR" -echo " MOZJS_MAJOR_VERSION = $MOZJS_MAJOR_VERSION" -echo " MOZJS_MINOR_VERSION = $MOZJS_MINOR_VERSION" -echo " MOZJS_PATCH_VERSION = $MOZJS_PATCH_VERSION" -echo " MOZJS_ALPHA = $MOZJS_ALPHA" - -cmd=${1:-build} -pkg="mozjs-${MOZJS_MAJOR_VERSION}.${MOZJS_MINOR_VERSION}.${MOZJS_PATCH_VERSION:-${MOZJS_ALPHA:-0}}.tar.bz2" -pkgpath=${pkg%.tar*} -tgtpath=${DIST}/${pkgpath} -taropts="-jcf" - -case $cmd in -"clean") - echo "Cleaning ${pkg} and ${tgtpath} ..." - rm -rf ${pkg} ${tgtpath} - ;; -"build") - echo "Packaging source tarball ${pkg}..." - if [ -d ${tgtpath} ]; then - echo "WARNING - dist tree ${tgtpath} already exists!" - fi - ${MKDIR} -p ${tgtpath}/js/src - - # copy the embedded icu - ${MKDIR} -p ${tgtpath}/intl - cp -t ${tgtpath}/intl -dRp ${SRCDIR}/../../intl/icu - - # put in js itself - cp -t ${tgtpath} -dRp ${SRCDIR}/../../mfbt - cp -t ${tgtpath}/js -dRp ${SRCDIR}/../jsd ${SRCDIR}/../public - find ${SRCDIR} -mindepth 1 -maxdepth 1 -not -path ${DIST} -a -not -name ${pkg} \ - -exec cp -t ${tgtpath}/js/src -dRp {} + - - # distclean if necessary - if [ -e ${tgtpath}/js/src/Makefile ]; then - ${MAKE} -C ${tgtpath}/js/src distclean - fi - - # put in the virtualenv and supporting files if it doesnt already exist - if [ ! -e ${SRCDIR}/build/virtualenv ]; then - cp -t ${tgtpath}/js/src/build -dRp \ - ${SRCDIR}/../../build/virtualenv \ - ${SRCDIR}/../../build/buildconfig.py - fi - if [ ! -e ${SRCDIR}/python ]; then - cp -t ${tgtpath}/js/src -dRp \ - ${SRCDIR}/../../python - fi - if [ ! -e ${SRCDIR}/testing ]; then - ${MKDIR} -p ${tgtpath}/js/src/testing - cp -t ${tgtpath}/js/src/testing -dRp \ - ${SRCDIR}/../../testing/mozbase - fi - # end of virtualenv injection - - # remove *.pyc and *.pyo files if any - find ${tgtpath} -type f -name "*.pyc" -o -name "*.pyo" |xargs rm -f - - # copy or create INSTALL - if [ -e {DIST}/INSTALL ]; then - cp -t ${tgtpath} ${DIST}/INSTALL - else - cat <${tgtpath}/INSTALL -Full build documentation for SpiderMonkey is hosted on MDN: - https://developer.mozilla.org/en-US/docs/SpiderMonkey/Build_Documentation - -Note that the libraries produced by the build system include symbols, -causing the binaries to be extremely large. It is highly suggested that \`strip\` -be run over the binaries before deploying them. - -Building with default options may be performed as follows: - cd js/src - ./configure - make -INSTALL_EOF - fi - - # copy or create README - if [ -e ${DIST}/README ]; then - cp -t ${tgtpath} ${DIST}/README - else - cat <${tgtpath}/README -This directory contains SpiderMonkey ${MOZJS_MAJOR_VERSION}. - -This release is based on a revision of Mozilla ${MOZJS_MAJOR_VERSION}: - http://hg.mozilla.org/releases/ -The changes in the patches/ directory were applied. - -MDN hosts the latest SpiderMonkey ${MOZJS_MAJOR_VERSION} release notes: - https://developer.mozilla.org/en-US/docs/SpiderMonkey/${MOZJS_MAJOR_VERSION} -README_EOF - fi - - # copy LICENSE - if [ -e ${SRCDIR}/../../b2g/LICENSE ]; then - cp ${SRCDIR}/../../b2g/LICENSE ${tgtpath}/ - else - cp ${SRCDIR}/../../LICENSE ${tgtpath}/ - fi - - # copy patches dir, if it currently exists in DIST - if [ -d ${DIST}/patches ]; then - cp -t ${tgtpath} -dRp ${DIST}/patches - elif [ -d ${SRCDIR}/../../patches ]; then - cp -t ${tgtpath} -dRp ${SRCDIR}/../../patches - fi - - # Roll the tarball - ${TAR} $taropts ${DIST}/../${pkg} -C ${DIST} ${pkgpath} - echo "done." - ;; -*) - echo "Unrecognized command: $cmd" - ;; -esac From 553ff52128790f714a6b800112f216c99d19417d Mon Sep 17 00:00:00 2001 From: Shane Caraveo Date: Wed, 21 Aug 2013 11:32:46 -0700 Subject: [PATCH 49/54] bug 891216 multiple workers enabled, r=markh --- browser/base/content/aboutSocialError.xhtml | 5 +- browser/base/content/browser-social.js | 44 +++++--- browser/base/content/nsContextMenu.js | 5 +- browser/base/content/test/social/Makefile.in | 1 + .../test/social/browser_social_multiworker.js | 101 ++++++++++++++++++ browser/modules/Social.jsm | 60 ++++++----- toolkit/components/social/MozSocialAPI.jsm | 20 ++-- toolkit/components/social/SocialService.jsm | 19 +++- .../test/browser/browser_SocialProvider.js | 58 ++++++++-- .../test/xpcshell/test_SocialService.js | 42 ++++---- 10 files changed, 268 insertions(+), 87 deletions(-) create mode 100644 browser/base/content/test/social/browser_social_multiworker.js diff --git a/browser/base/content/aboutSocialError.xhtml b/browser/base/content/aboutSocialError.xhtml index 6bef2d7bdbf..8c5a856d2ea 100644 --- a/browser/base/content/aboutSocialError.xhtml +++ b/browser/base/content/aboutSocialError.xhtml @@ -112,10 +112,7 @@ } function reloadProvider() { - Social.enabled = false; - Services.tm.mainThread.dispatch(function() { - Social.enabled = true; - }, Components.interfaces.nsIThread.DISPATCH_NORMAL); + Social.provider.reload(); } parseQueryString(); diff --git a/browser/base/content/browser-social.js b/browser/base/content/browser-social.js index 514a46f1d45..f831f0a9cc2 100644 --- a/browser/base/content/browser-social.js +++ b/browser/base/content/browser-social.js @@ -36,6 +36,7 @@ SocialUI = { Services.obs.addObserver(this, "social:frameworker-error", false); Services.obs.addObserver(this, "social:provider-set", false); Services.obs.addObserver(this, "social:providers-changed", false); + Services.obs.addObserver(this, "social:provider-reload", false); Services.prefs.addObserver("social.sidebar.open", this, false); Services.prefs.addObserver("social.toast-notifications.enabled", this, false); @@ -60,6 +61,7 @@ SocialUI = { Services.obs.removeObserver(this, "social:frameworker-error"); Services.obs.removeObserver(this, "social:provider-set"); Services.obs.removeObserver(this, "social:providers-changed"); + Services.obs.removeObserver(this, "social:provider-reload"); Services.prefs.removeObserver("social.sidebar.open", this); Services.prefs.removeObserver("social.toast-notifications.enabled", this); @@ -74,6 +76,16 @@ SocialUI = { // manually :( try { switch (topic) { + case "social:provider-reload": + // if the reloaded provider is our current provider, fall through + // to social:provider-set so the ui will be reset + if (!Social.provider || Social.provider.origin != data) + return; + // be sure to unload the sidebar as it will not reload if the origin + // has not changed, it will be loaded in provider-set below. Other + // panels will be unloaded or handle reload. + SocialSidebar.unloadSidebar(); + // fall through to social:provider-set case "social:provider-set": // Social.provider has changed (possibly to null), update any state // which depends on it. @@ -142,7 +154,7 @@ SocialUI = { // Miscellaneous helpers showProfile: function SocialUI_showProfile() { - if (Social.haveLoggedInUser()) + if (Social.provider.haveLoggedInUser()) openUILinkIn(Social.provider.profile.profileURL, "tab"); else { // XXX Bug 789585 will implement an API for provider-specified login pages. @@ -976,22 +988,20 @@ SocialToolbar = { let toggleNotificationsCommand = document.getElementById("Social:ToggleNotifications"); toggleNotificationsCommand.setAttribute("hidden", !socialEnabled); - if (!Social.haveLoggedInUser() || !socialEnabled) { - let parent = document.getElementById("social-notification-panel"); - while (parent.hasChildNodes()) { - let frame = parent.firstChild; - SharedFrame.forgetGroup(frame.id); - parent.removeChild(frame); - } + let parent = document.getElementById("social-notification-panel"); + while (parent.hasChildNodes()) { + let frame = parent.firstChild; + SharedFrame.forgetGroup(frame.id); + parent.removeChild(frame); + } - let tbi = document.getElementById("social-toolbar-item"); - if (tbi) { - // SocialMark is the last button allways - let next = SocialMark.button.previousSibling; - while (next != this.button) { - tbi.removeChild(next); - next = SocialMark.button.previousSibling; - } + let tbi = document.getElementById("social-toolbar-item"); + if (tbi) { + // SocialMark is the last button allways + let next = SocialMark.button.previousSibling; + while (next != this.button) { + tbi.removeChild(next); + next = SocialMark.button.previousSibling; } } }, @@ -1035,7 +1045,7 @@ SocialToolbar = { // provider.profile == undefined means no response yet from the provider // to tell us whether the user is logged in or not. if (!SocialUI.enabled || - (!Social.haveLoggedInUser() && Social.provider.profile !== undefined)) { + (!Social.provider.haveLoggedInUser() && Social.provider.profile !== undefined)) { // Either no enabled provider, or there is a provider and it has // responded with a profile and the user isn't loggedin. The icons // etc have already been removed by updateButtonHiddenState, so we want diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 2497dd4462a..5aa48f6c3f1 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -905,10 +905,7 @@ nsContextMenu.prototype = { reload: function(event) { if (this.onSocial) { // full reload of social provider - Social.enabled = false; - Services.tm.mainThread.dispatch(function() { - Social.enabled = true; - }, Components.interfaces.nsIThread.DISPATCH_NORMAL); + Social.provider.reload(); } else { BrowserReloadOrDuplicate(event); } diff --git a/browser/base/content/test/social/Makefile.in b/browser/base/content/test/social/Makefile.in index 37c28c1dd02..ba9be15479b 100644 --- a/browser/base/content/test/social/Makefile.in +++ b/browser/base/content/test/social/Makefile.in @@ -30,6 +30,7 @@ MOCHITEST_BROWSER_FILES = \ browser_social_chatwindow_resize.js \ browser_social_chatwindowfocus.js \ browser_social_multiprovider.js \ + browser_social_multiworker.js \ browser_social_errorPage.js \ browser_social_window.js \ social_activate.html \ diff --git a/browser/base/content/test/social/browser_social_multiworker.js b/browser/base/content/test/social/browser_social_multiworker.js new file mode 100644 index 00000000000..1306a89da30 --- /dev/null +++ b/browser/base/content/test/social/browser_social_multiworker.js @@ -0,0 +1,101 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function test() { + waitForExplicitFinish(); + + Services.prefs.setBoolPref("social.allowMultipleWorkers", true); + runSocialTestWithProvider(gProviders, function (finishcb) { + runSocialTests(tests, undefined, undefined, function() { + Services.prefs.clearUserPref("social.allowMultipleWorkers"); + finishcb(); + }); + }); +} + +let gProviders = [ + { + name: "provider 1", + origin: "https://example.com", + sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?provider1", + workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js", + iconURL: "chrome://branding/content/icon48.png" + }, + { + name: "provider 2", + origin: "https://test1.example.com", + sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?provider2", + workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js", + iconURL: "chrome://branding/content/icon48.png" + } +]; + +var tests = { + testWorkersAlive: function(next) { + // verify we can get a message from all providers that are enabled + let messageReceived = 0; + function oneWorkerTest(provider) { + let port = provider.getWorkerPort(); + port.onmessage = function (e) { + let topic = e.data.topic; + switch (topic) { + case "test-init-done": + ok(true, "got message from provider " + provider.name); + port.close(); + messageReceived++; + break; + } + }; + port.postMessage({topic: "test-init"}); + } + + for (let p of Social.providers) { + oneWorkerTest(p); + } + + waitForCondition(function() messageReceived == Social.providers.length, + next, "received messages from all workers"); + }, + testWorkerDisabling: function(next) { + Social.enabled = false; + is(Social.providers.length, gProviders.length, "providers still available"); + for (let p of Social.providers) { + ok(!p.enabled, "provider disabled"); + ok(!p.getWorkerPort(), "worker disabled"); + } + next(); + }, + + testSingleWorkerEnabling: function(next) { + // test that only one worker is enabled when we limit workers + Services.prefs.setBoolPref("social.allowMultipleWorkers", false); + Social.enabled = true; + for (let p of Social.providers) { + if (p == Social.provider) { + ok(p.enabled, "primary provider enabled"); + let port = p.getWorkerPort(); + ok(port, "primary worker enabled"); + port.close(); + } else { + ok(!p.enabled, "secondary provider is not enabled"); + ok(!p.getWorkerPort(), "secondary worker disabled"); + } + } + next(); + }, + + testMultipleWorkerEnabling: function(next) { + // test that all workers are enabled when we allow multiple workers + Social.enabled = false; + Services.prefs.setBoolPref("social.allowMultipleWorkers", true); + Social.enabled = true; + for (let p of Social.providers) { + ok(p.enabled, "provider enabled"); + let port = p.getWorkerPort(); + ok(port, "worker enabled"); + port.close(); + } + next(); + } +} diff --git a/browser/modules/Social.jsm b/browser/modules/Social.jsm index 1abd13fb014..d01feba4f66 100644 --- a/browser/modules/Social.jsm +++ b/browser/modules/Social.jsm @@ -91,6 +91,11 @@ this.Social = { providers: [], _disabledForSafeMode: false, + get allowMultipleWorkers() { + return Services.prefs.prefHasUserValue("social.allowMultipleWorkers") && + Services.prefs.getBoolPref("social.allowMultipleWorkers"); + }, + get _currentProviderPref() { try { return Services.prefs.getComplexValue("social.provider.current", @@ -114,15 +119,14 @@ this.Social = { this._setProvider(val); }, - // Sets the current provider and enables it. Also disables the - // previously set provider, and notifies observers of the change. + // Sets the current provider and notifies observers of the change. _setProvider: function (provider) { if (this._provider == provider) return; - // Disable the previous provider, if any, since we want only one provider to - // be enabled at once. - if (this._provider) + // Disable the previous provider, if we are not allowing multiple workers, + // since we want only one provider to be enabled at once. + if (this._provider && !Social.allowMultipleWorkers) this._provider.enabled = false; this._provider = provider; @@ -134,7 +138,6 @@ this.Social = { let enabled = !!provider; if (enabled != SocialService.enabled) { SocialService.enabled = enabled; - Services.prefs.setBoolPref("social.enabled", enabled); } let origin = this._provider && this._provider.origin; @@ -159,31 +162,40 @@ this.Social = { if (SocialService.enabled) { // Retrieve the current set of providers, and set the current provider. SocialService.getOrderedProviderList(function (providers) { - this._updateProviderCache(providers); - }.bind(this)); + Social._updateProviderCache(providers); + Social._updateWorkerState(true); + }); } // Register an observer for changes to the provider list SocialService.registerProviderListener(function providerListener(topic, data) { - // An engine change caused by adding/removing a provider should notify + // An engine change caused by adding/removing a provider should notify. + // any providers we receive are enabled in the AddonsManager if (topic == "provider-added" || topic == "provider-removed") { - this._updateProviderCache(data); + Social._updateProviderCache(data); + Social._updateWorkerState(true); Services.obs.notifyObservers(null, "social:providers-changed", null); return; } if (topic == "provider-update") { - // a provider has self-updated its manifest, we need to update our - // cache and possibly reload if it was the current provider. + // a provider has self-updated its manifest, we need to update our cache + // and reload the provider. let provider = data; - // if we need a reload, do it now - if (provider.enabled) { - Social.enabled = false; - Services.tm.mainThread.dispatch(function() { - Social.enabled = true; - }, Components.interfaces.nsIThread.DISPATCH_NORMAL); - } + SocialService.getOrderedProviderList(function(providers) { + Social._updateProviderCache(providers); + provider.reload(); + Services.obs.notifyObservers(null, "social:providers-changed", null); + }); } - }.bind(this)); + }); + }, + + _updateWorkerState: function(enable) { + // ensure that our providers are all disabled, and enabled if we allow + // multiple workers + if (enable && !Social.allowMultipleWorkers) + return; + [p.enabled = enable for (p of Social.providers) if (p.enabled != enable)]; }, // Called to update our cache of providers and set the current provider @@ -203,6 +215,9 @@ this.Social = { set enabled(val) { // Setting .enabled is just a shortcut for setting the provider to either // the default provider or null... + + this._updateWorkerState(val); + if (val) { if (!this.provider) this.provider = this.defaultProvider; @@ -210,6 +225,7 @@ this.Social = { this.provider = null; } }, + get enabled() { return this.provider != null; }, @@ -229,10 +245,6 @@ this.Social = { Services.prefs.setBoolPref("social.toast-notifications.enabled", !prefValue); }, - haveLoggedInUser: function () { - return !!(this.provider && this.provider.profile && this.provider.profile.userName); - }, - setProviderByOrigin: function (origin) { this.provider = this._getProviderFromOrigin(origin); }, diff --git a/toolkit/components/social/MozSocialAPI.jsm b/toolkit/components/social/MozSocialAPI.jsm index c5de68d1757..0e21f1bcb73 100644 --- a/toolkit/components/social/MozSocialAPI.jsm +++ b/toolkit/components/social/MozSocialAPI.jsm @@ -49,7 +49,7 @@ function injectController(doc, topic, data) { return; } - var containingBrowser = window.QueryInterface(Ci.nsIInterfaceRequestor) + let containingBrowser = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIDocShell) .chromeEventHandler; @@ -67,7 +67,7 @@ function injectController(doc, topic, data) { } SocialService.getProvider(doc.nodePrincipal.origin, function(provider) { - if (provider && provider.workerURL && provider.enabled) { + if (provider && provider.enabled) { attachToWindow(provider, window); } }); @@ -88,7 +88,7 @@ function attachToWindow(provider, targetWindow) { return; } - var port = provider.getWorkerPort(targetWindow); + let port = provider.workerURL ? provider.getWorkerPort(targetWindow) : null; let mozSocialObj = { // Use a method for backwards compat with existing providers, but we @@ -206,12 +206,14 @@ function attachToWindow(provider, targetWindow) { return targetWindow.navigator.wrappedJSObject.mozSocial = contentObj; }); - targetWindow.addEventListener("unload", function () { - // We want to close the port, but also want the target window to be - // able to use the port during an unload event they setup - so we - // set a timer which will fire after the unload events have all fired. - schedule(function () { port.close(); }); - }); + if (port) { + targetWindow.addEventListener("unload", function () { + // We want to close the port, but also want the target window to be + // able to use the port during an unload event they setup - so we + // set a timer which will fire after the unload events have all fired. + schedule(function () { port.close(); }); + }); + } // We allow window.close() to close the panel, so add an event handler for // this, then cancel the event (so the window itself doesn't die) and diff --git a/toolkit/components/social/SocialService.jsm b/toolkit/components/social/SocialService.jsm index d60e95e7936..924783e0bc1 100644 --- a/toolkit/components/social/SocialService.jsm +++ b/toolkit/components/social/SocialService.jsm @@ -34,7 +34,13 @@ XPCOMUtils.defineLazyServiceGetter(this, "etld", // Internal helper methods and state let SocialServiceInternal = { - enabled: Services.prefs.getBoolPref("social.enabled"), + _enabled: Services.prefs.getBoolPref("social.enabled"), + get enabled() this._enabled, + set enabled(val) { + this._enabled = !!val; + Services.prefs.setBoolPref("social.enabled", !!val); + }, + get providerArray() { return [p for ([, p] of Iterator(this.providers))]; }, @@ -362,7 +368,6 @@ this.SocialService = { SocialServiceInternal.enabled = enable; MozSocialAPI.enabled = enable; - Services.obs.notifyObservers(null, "social:pref-changed", enable ? "enabled" : "disabled"); Services.telemetry.getHistogramById("SOCIAL_TOGGLED").add(enable); }, @@ -723,6 +728,12 @@ function SocialProvider(input) { } SocialProvider.prototype = { + reload: function() { + this._terminate(); + this._activate(); + Services.obs.notifyObservers(null, "social:provider-reload", this.origin); + }, + // Provider enabled/disabled state. Disabled providers do not have active // connections to their FrameWorkers. _enabled: false, @@ -868,6 +879,10 @@ SocialProvider.prototype = { closeAllChatWindows(this); }, + haveLoggedInUser: function () { + return !!(this.profile && this.profile.userName); + }, + // Called by the workerAPI to add/update a notification icon. setAmbientNotification: function(notification) { if (!this.profile.userName) diff --git a/toolkit/components/social/test/browser/browser_SocialProvider.js b/toolkit/components/social/test/browser/browser_SocialProvider.js index a642668fe99..81582f12061 100644 --- a/toolkit/components/social/test/browser/browser_SocialProvider.js +++ b/toolkit/components/social/test/browser/browser_SocialProvider.js @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +let provider; + function test() { waitForExplicitFinish(); @@ -13,10 +15,22 @@ function test() { ensureSocialEnabled(); - SocialService.addProvider(manifest, function (provider) { - // enable the provider - provider.enabled = true; + SocialService.addProvider(manifest, function (p) { + provider = p; + runTests(tests, undefined, undefined, function () { + SocialService.removeProvider(p.origin, function() { + ok(!provider.enabled, "removing an enabled provider should have disabled the provider"); + let port = provider.getWorkerPort(); + ok(!port, "should not be able to get a port after removing the provider"); + provider = null; + finish(); + }); + }); + }); +} +let tests = { + testSingleProvider: function(next) { ok(provider.enabled, "provider is initially enabled"); let port = provider.getWorkerPort(); ok(port, "should be able to get a port from enabled provider"); @@ -37,12 +51,38 @@ function test() { ok(port, "should be able to get a port from re-enabled provider"); port.close(); ok(provider.workerAPI, "should be able to get a workerAPI from re-enabled provider"); - - SocialService.removeProvider(provider.origin, function() { - ok(!provider.enabled, "removing an enabled provider should have disabled the provider"); + next(); + }, + testTwoProviders: function(next) { + // add another provider, test both workers + let manifest = { + origin: 'http://test2.example.com', + name: "Example Provider 2", + workerURL: "http://test2.example.com/browser/toolkit/components/social/test/browser/worker_social.js" + }; + SocialService.addProvider(manifest, function (provider2) { + ok(provider.enabled, "provider is initially enabled"); + ok(!provider2.enabled, "provider2 is not initially enabled"); + provider2.enabled = true; let port = provider.getWorkerPort(); - ok(!port, "should not be able to get a port after removing the provider"); - finish(); + let port2 = provider2.getWorkerPort(); + ok(port, "have port for provider"); + ok(port2, "have port for provider2"); + port.onmessage = function(e) { + if (e.data.topic == "test-initialization-complete") { + ok(true, "first provider initialized"); + port2.postMessage({topic: "test-initialization"}); + } + } + port2.onmessage = function(e) { + if (e.data.topic == "test-initialization-complete") { + ok(true, "second provider initialized"); + SocialService.removeProvider(provider2.origin, function() { + next(); + }); + } + } + port.postMessage({topic: "test-initialization"}); }); - }); + } } diff --git a/toolkit/components/social/test/xpcshell/test_SocialService.js b/toolkit/components/social/test/xpcshell/test_SocialService.js index 8ae1de8d1bc..b5555daebdb 100644 --- a/toolkit/components/social/test/xpcshell/test_SocialService.js +++ b/toolkit/components/social/test/xpcshell/test_SocialService.js @@ -78,38 +78,44 @@ function testGetProviderList(manifests, next) { } } + function testEnabled(manifests, next) { // Check that providers are disabled by default let providers = yield SocialService.getProviderList(next); do_check_true(providers.length >= manifests.length); - do_check_true(SocialService.enabled); + do_check_true(SocialService.enabled, "social enabled at test start"); providers.forEach(function (provider) { do_check_false(provider.enabled); }); - let notificationDisabledCorrect = false; - Services.obs.addObserver(function obs1(subj, topic, data) { - Services.obs.removeObserver(obs1, "social:pref-changed"); - notificationDisabledCorrect = data == "disabled"; - }, "social:pref-changed", false); + do_test_pending(); + function waitForEnableObserver(cb) { + Services.prefs.addObserver("social.enabled", function prefObserver(subj, topic, data) { + Services.prefs.removeObserver("social.enabled", prefObserver); + cb(); + }, false); + } // enable one of the added providers providers[providers.length-1].enabled = true; // now disable the service and check that it disabled that provider (and all others for good measure) + waitForEnableObserver(function() { + do_check_true(!SocialService.enabled); + providers.forEach(function (provider) { + do_check_false(provider.enabled); + }); + waitForEnableObserver(function() { + do_check_true(SocialService.enabled); + // Enabling the service should not enable providers + providers.forEach(function (provider) { + do_check_false(provider.enabled); + }); + do_test_finished(); + }); + SocialService.enabled = true; + }); SocialService.enabled = false; - do_check_true(notificationDisabledCorrect); - do_check_true(!SocialService.enabled); - providers.forEach(function (provider) { - do_check_false(provider.enabled); - }); - - SocialService.enabled = true; - do_check_true(SocialService.enabled); - // Enabling the service should not enable providers - providers.forEach(function (provider) { - do_check_false(provider.enabled); - }); } function testAddRemoveProvider(manifests, next) { From 9a243a50e96d648c849e725a335fb18d512f8ae0 Mon Sep 17 00:00:00 2001 From: Shane Caraveo Date: Wed, 21 Aug 2013 11:38:33 -0700 Subject: [PATCH 50/54] bug 891221 add tests for chat from multiple providers, r=markh --- .../test/social/browser_social_chatwindow.js | 126 ++++++++++++++---- browser/base/content/test/social/head.js | 2 +- 2 files changed, 98 insertions(+), 30 deletions(-) diff --git a/browser/base/content/test/social/browser_social_chatwindow.js b/browser/base/content/test/social/browser_social_chatwindow.js index 3d8b920273f..4c749de61a8 100644 --- a/browser/base/content/test/social/browser_social_chatwindow.js +++ b/browser/base/content/test/social/browser_social_chatwindow.js @@ -2,17 +2,71 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService; + +let manifests = [ + { + name: "provider@example.com", + origin: "https://example.com", + sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?example.com", + workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js", + iconURL: "chrome://branding/content/icon48.png" + }, + { + name: "provider@test1", + origin: "https://test1.example.com", + sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?test1", + workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js", + iconURL: "chrome://branding/content/icon48.png" + }, + { + name: "provider@test2", + origin: "https://test2.example.com", + sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar.html?test2", + workerURL: "https://test2.example.com/browser/browser/base/content/test/social/social_worker.js", + iconURL: "chrome://branding/content/icon48.png" + } +]; + +let chatId = 0; +function openChat(provider, callback) { + let chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html"; + let port = provider.getWorkerPort(); + port.onmessage = function(e) { + if (e.data.topic == "got-chatbox-message") { + port.close(); + callback(); + } + } + let url = chatUrl + "?" + (chatId++); + port.postMessage({topic: "test-init"}); + port.postMessage({topic: "test-worker-chat", data: url}); + gURLsNotRemembered.push(url); +} + +function waitPrefChange(cb) { + Services.prefs.addObserver("social.enabled", function prefObserver(subject, topic, data) { + Services.prefs.removeObserver("social.enabled", prefObserver); + executeSoon(cb); + }, false); +} + +function setWorkerMode(multiple, cb) { + waitPrefChange(function() { + if (multiple) + Services.prefs.setBoolPref("social.allowMultipleWorkers", true); + else + Services.prefs.clearUserPref("social.allowMultipleWorkers"); + waitPrefChange(cb); + Social.enabled = true; + }); + Social.enabled = false; +} + function test() { requestLongerTimeout(2); // only debug builds seem to need more time... waitForExplicitFinish(); - let manifest = { // normal provider - name: "provider 1", - origin: "https://example.com", - sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html", - workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js", - iconURL: "https://example.com/browser/browser/base/content/test/moz.png" - }; let oldwidth = window.outerWidth; // we futz with these, so we restore them let oldleft = window.screenX; window.moveTo(0, window.screenY) @@ -21,7 +75,7 @@ function test() { ok(chats.children.length == 0, "no chatty children left behind"); cb(); }; - runSocialTestWithProvider(manifest, function (finishcb) { + runSocialTestWithProvider(manifests, function (finishcb) { runSocialTests(tests, undefined, postSubTest, function() { window.moveTo(oldleft, window.screenY) window.resizeTo(oldwidth, window.outerHeight); @@ -147,7 +201,7 @@ var tests = { maybeOpenAnother(); }, testWorkerChatWindow: function(next) { - const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html"; + const chatUrl = Social.provider.origin + "/browser/browser/base/content/test/social/social_chat.html"; let chats = document.getElementById("pinnedchats"); let port = Social.provider.getWorkerPort(); ok(port, "provider has a port"); @@ -384,7 +438,7 @@ var tests = { testSecondTopLevelWindow: function(next) { // Bug 817782 - check chats work in new top-level windows. - const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html"; + const chatUrl = Social.provider.origin + "/browser/browser/base/content/test/social/social_chat.html"; let port = Social.provider.getWorkerPort(); let secondWindow; port.onmessage = function(e) { @@ -407,23 +461,9 @@ var tests = { testChatWindowChooser: function(next) { // Tests that when a worker creates a chat, it is opened in the correct // window. - const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html"; - let chatId = 1; - let port = Social.provider.getWorkerPort(); - port.postMessage({topic: "test-init"}); - - function openChat(callback) { - port.onmessage = function(e) { - if (e.data.topic == "got-chatbox-message") - callback(); - } - let url = chatUrl + "?" + (chatId++); - port.postMessage({topic: "test-worker-chat", data: url}); - } - // open a chat (it will open in the main window) ok(!window.SocialChatBar.hasChats, "first window should start with no chats"); - openChat(function() { + openChat(Social.provider, function() { ok(window.SocialChatBar.hasChats, "first window has the chat"); // create a second window - this will be the "most recent" and will // therefore be the window that hosts the new chat (see bug 835111) @@ -431,27 +471,55 @@ var tests = { secondWindow.addEventListener("load", function loadListener() { secondWindow.removeEventListener("load", loadListener); ok(!secondWindow.SocialChatBar.hasChats, "second window has no chats"); - openChat(function() { + openChat(Social.provider, function() { ok(secondWindow.SocialChatBar.hasChats, "second window now has chats"); is(window.SocialChatBar.chatbar.childElementCount, 1, "first window still has 1 chat"); window.SocialChatBar.chatbar.removeAll(); // now open another chat - it should still open in the second. - openChat(function() { + openChat(Social.provider, function() { ok(!window.SocialChatBar.hasChats, "first window has no chats"); ok(secondWindow.SocialChatBar.hasChats, "second window has a chat"); secondWindow.close(); - port.close(); next(); }); }); }) }); }, + testMultipleProviderChat: function(next) { + // while pref'd off, we need to set the worker mode to multiple providers + setWorkerMode(true, function() { + // test incomming chats from all providers + openChat(Social.providers[0], function() { + openChat(Social.providers[1], function() { + openChat(Social.providers[2], function() { + let chats = document.getElementById("pinnedchats"); + waitForCondition(function() chats.children.length == Social.providers.length, + function() { + ok(true, "one chat window per provider opened"); + // test logout of a single provider + let provider = Social.providers[0]; + let port = provider.getWorkerPort(); + port.postMessage({topic: "test-logout"}); + waitForCondition(function() chats.children.length == Social.providers.length - 1, + function() { + port.close(); + chats.removeAll(); + ok(!chats.selectedChat, "chats are all closed"); + setWorkerMode(false, next); + }, + "chat window didn't close"); + }, "chat windows did not open"); + }); + }); + }); + }); + }, // XXX - note this must be the last test until we restore the login state // between tests... testCloseOnLogout: function(next) { - const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html"; + const chatUrl = Social.provider.origin + "/browser/browser/base/content/test/social/social_chat.html"; let port = Social.provider.getWorkerPort(); ok(port, "provider has a port"); let opened = false; diff --git a/browser/base/content/test/social/head.js b/browser/base/content/test/social/head.js index 33b8a448341..f6a00e8df29 100644 --- a/browser/base/content/test/social/head.js +++ b/browser/base/content/test/social/head.js @@ -417,8 +417,8 @@ function get3ChatsForCollapsing(mode, cb) { function makeChat(mode, uniqueid, cb) { info("making a chat window '" + uniqueid +"'"); - const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html"; let provider = Social.provider; + const chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html"; let isOpened = window.SocialChatBar.openChat(provider, chatUrl + "?id=" + uniqueid, function(chat) { info("chat window has opened"); // we can't callback immediately or we might close the chat during From 2716baa06c0e87e9472ec5f2b233230d22dd066f Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Wed, 21 Aug 2013 11:33:43 -0700 Subject: [PATCH 51/54] Bug 906718 - Pressing BACK should move the user up in the bookmark folder hierarchy. r=sriram --- .../base/home/BookmarksListAdapter.java | 13 +++++--- .../android/base/home/BookmarksListView.java | 32 ++++++++++++++----- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/mobile/android/base/home/BookmarksListAdapter.java b/mobile/android/base/home/BookmarksListAdapter.java index b139e1fa293..bf2018a29d1 100644 --- a/mobile/android/base/home/BookmarksListAdapter.java +++ b/mobile/android/base/home/BookmarksListAdapter.java @@ -60,13 +60,18 @@ class BookmarksListAdapter extends MultiTypeCursorAdapter { /** * Moves to parent folder, if one exists. + * + * @return Whether the adapter successfully moved to a parent folder. */ - public void moveToParentFolder() { + public boolean moveToParentFolder() { // If we're already at the root, we can't move to a parent folder - if (mParentStack.size() != 1) { - mParentStack.removeFirst(); - refreshCurrentFolder(); + if (mParentStack.size() == 1) { + return false; } + + mParentStack.removeFirst(); + refreshCurrentFolder(); + return true; } /** diff --git a/mobile/android/base/home/BookmarksListView.java b/mobile/android/base/home/BookmarksListView.java index c3246aede8e..ab0df636228 100644 --- a/mobile/android/base/home/BookmarksListView.java +++ b/mobile/android/base/home/BookmarksListView.java @@ -13,6 +13,7 @@ import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; import android.content.Context; import android.database.Cursor; import android.util.AttributeSet; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; @@ -56,6 +57,17 @@ public class BookmarksListView extends HomeListView super.onAttachedToWindow(); setOnItemClickListener(this); + + setOnKeyListener(new View.OnKeyListener() { + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + // If the user hit the BACK key, try to move to the parent folder. + if (keyCode == KeyEvent.KEYCODE_BACK) { + return getBookmarksListAdapter().moveToParentFolder(); + } + return false; + } + }); } @Override @@ -101,14 +113,7 @@ public class BookmarksListView extends HomeListView // Absolute position for the adapter. position -= headerCount; - BookmarksListAdapter adapter; - ListAdapter listAdapter = getAdapter(); - if (listAdapter instanceof HeaderViewListAdapter) { - adapter = (BookmarksListAdapter) ((HeaderViewListAdapter) listAdapter).getWrappedAdapter(); - } else { - adapter = (BookmarksListAdapter) listAdapter; - } - + final BookmarksListAdapter adapter = getBookmarksListAdapter(); if (adapter.isShowingChildFolder()) { if (position == 0) { // If we tap on an opened folder, move back to parent folder. @@ -141,4 +146,15 @@ public class BookmarksListView extends HomeListView getOnUrlOpenListener().onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB)); } } + + private BookmarksListAdapter getBookmarksListAdapter() { + BookmarksListAdapter adapter; + ListAdapter listAdapter = getAdapter(); + if (listAdapter instanceof HeaderViewListAdapter) { + adapter = (BookmarksListAdapter) ((HeaderViewListAdapter) listAdapter).getWrappedAdapter(); + } else { + adapter = (BookmarksListAdapter) listAdapter; + } + return adapter; + } } From d19d8a0df60ad44c44c7212f6c1f030b28ab53e2 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Wed, 21 Aug 2013 11:33:58 -0700 Subject: [PATCH 52/54] Bug 885084 - Only return top bookmarks for bookmarks page thumbnails. r=wesj --- mobile/android/base/db/BrowserDB.java | 14 +++++++------- mobile/android/base/db/LocalBrowserDB.java | 10 +++++++--- mobile/android/base/home/BookmarksPage.java | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/mobile/android/base/db/BrowserDB.java b/mobile/android/base/db/BrowserDB.java index 34f254b3309..a076dc7cada 100644 --- a/mobile/android/base/db/BrowserDB.java +++ b/mobile/android/base/db/BrowserDB.java @@ -38,9 +38,9 @@ public class BrowserDB { public Cursor filter(ContentResolver cr, CharSequence constraint, int limit); - // This should onlyl return frecent sites, BrowserDB.getTopSites will do the + // This should only return frecent bookmarks, BrowserDB.getTopBookmarks will do the // work to combine that list with the pinned sites list - public Cursor getTopSites(ContentResolver cr, int limit); + public Cursor getTopBookmarks(ContentResolver cr, int limit); public void updateVisitedHistory(ContentResolver cr, String uri); @@ -137,12 +137,12 @@ public class BrowserDB { return sDb.filter(cr, constraint, limit); } - public static Cursor getTopSites(ContentResolver cr, int limit) { - // Note this is not a single query anymore, but actually returns a mixture of two queries, one for topSites - // and one for pinned sites - Cursor topSites = sDb.getTopSites(cr, limit); + public static Cursor getTopBookmarks(ContentResolver cr, int limit) { + // Note this is not a single query anymore, but actually returns a mixture of two queries, + // one for top bookmarks, and one for pinned sites (which are actually bookmarks as well). + Cursor topBookmarks = sDb.getTopBookmarks(cr, limit); Cursor pinnedSites = sDb.getPinnedSites(cr, limit); - return new TopSitesCursorWrapper(pinnedSites, topSites, limit); + return new TopSitesCursorWrapper(pinnedSites, topBookmarks, limit); } public static void updateVisitedHistory(ContentResolver cr, String uri) { diff --git a/mobile/android/base/db/LocalBrowserDB.java b/mobile/android/base/db/LocalBrowserDB.java index 947685876d6..87cec8073ba 100644 --- a/mobile/android/base/db/LocalBrowserDB.java +++ b/mobile/android/base/db/LocalBrowserDB.java @@ -231,9 +231,13 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface { } @Override - public Cursor getTopSites(ContentResolver cr, int limit) { - // Filter out sites that are pinned - String selection = DBUtils.concatenateWhere("", Combined.URL + " NOT IN (SELECT " + + public Cursor getTopBookmarks(ContentResolver cr, int limit) { + // Only select bookmarks. Unfortunately, we need to query the combined view, + // instead of just the bookmarks table, in order to do the frecency calculation. + String selection = Combined.BOOKMARK_ID + " IS NOT NULL"; + + // Filter out sites that are pinned. + selection = DBUtils.concatenateWhere(selection, Combined.URL + " NOT IN (SELECT " + Bookmarks.URL + " FROM bookmarks WHERE " + DBUtils.qualifyColumn("bookmarks", Bookmarks.PARENT) + " == ? AND " + DBUtils.qualifyColumn("bookmarks", Bookmarks.IS_DELETED) + " == 0)"); diff --git a/mobile/android/base/home/BookmarksPage.java b/mobile/android/base/home/BookmarksPage.java index ac5d247a65a..b22057dd49c 100644 --- a/mobile/android/base/home/BookmarksPage.java +++ b/mobile/android/base/home/BookmarksPage.java @@ -362,7 +362,7 @@ public class BookmarksPage extends HomeFragment { @Override public Cursor loadCursor() { final int max = getContext().getResources().getInteger(R.integer.number_of_top_sites); - return BrowserDB.getTopSites(getContext().getContentResolver(), max); + return BrowserDB.getTopBookmarks(getContext().getContentResolver(), max); } } From 283b3c54efbda52add040255c4000c9f3c8d2c78 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Wed, 21 Aug 2013 11:34:06 -0700 Subject: [PATCH 53/54] Bug 897772 - Actually get a favicon when creating a homescreen shortcut from the about:home context menu. r=wesj --- mobile/android/base/home/HomeFragment.java | 38 ++++++++++++++++++---- mobile/android/base/home/HomeListView.java | 8 ----- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/mobile/android/base/home/HomeFragment.java b/mobile/android/base/home/HomeFragment.java index dc87504d58d..9920b373272 100644 --- a/mobile/android/base/home/HomeFragment.java +++ b/mobile/android/base/home/HomeFragment.java @@ -6,6 +6,7 @@ package org.mozilla.gecko.home; import org.mozilla.gecko.EditBookmarkDialog; +import org.mozilla.gecko.Favicons; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoEvent; import org.mozilla.gecko.R; @@ -132,14 +133,8 @@ abstract class HomeFragment extends Fragment { return false; } - // FIXME: bug 897772 - Bitmap bitmap = null; - if (info.favicon != null) { - bitmap = BitmapUtils.decodeByteArray(info.favicon); - } - String shortcutTitle = TextUtils.isEmpty(info.title) ? info.url.replaceAll(REGEX_URL_TO_TITLE, "") : info.title; - GeckoAppShell.createShortcut(shortcutTitle, info.url, bitmap, ""); + new AddToLauncherTask(info.url, shortcutTitle).execute(); return true; } @@ -225,6 +220,35 @@ abstract class HomeFragment extends Fragment { } } + private static class AddToLauncherTask extends UiAsyncTask { + private final String mUrl; + private final String mTitle; + + public AddToLauncherTask(String url, String title) { + super(ThreadUtils.getBackgroundHandler()); + + mUrl = url; + mTitle = title; + } + + @Override + public String doInBackground(Void... params) { + return Favicons.getInstance().getFaviconUrlForPageUrl(mUrl); + } + + @Override + public void onPostExecute(String faviconUrl) { + Favicons.OnFaviconLoadedListener listener = new Favicons.OnFaviconLoadedListener() { + @Override + public void onFaviconLoaded(String url, Bitmap favicon) { + GeckoAppShell.createShortcut(mTitle, mUrl, favicon, ""); + } + }; + + Favicons.getInstance().loadFavicon(mUrl, faviconUrl, 0, listener); + } + } + private static class RemoveBookmarkTask extends UiAsyncTask { private final Context mContext; private final int mId; diff --git a/mobile/android/base/home/HomeListView.java b/mobile/android/base/home/HomeListView.java index f8968ca2e11..d2a4a1605b1 100644 --- a/mobile/android/base/home/HomeListView.java +++ b/mobile/android/base/home/HomeListView.java @@ -113,7 +113,6 @@ public class HomeListView extends ListView public int bookmarkId; public int historyId; public String url; - public byte[] favicon; public String title; public int display; public boolean isFolder; @@ -165,13 +164,6 @@ public class HomeListView extends ListView historyId = -1; } - final int faviconCol = cursor.getColumnIndex(Combined.FAVICON); - if (faviconCol != -1) { - favicon = cursor.getBlob(faviconCol); - } else { - favicon = null; - } - // We only have the parent column in cursors from getBookmarksInFolder. final int parentCol = cursor.getColumnIndex(Bookmarks.PARENT); if (parentCol != -1) { From e52efd6ee671c50911bb8a8672e2ad24abd465c1 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Wed, 21 Aug 2013 11:34:16 -0700 Subject: [PATCH 54/54] Bug 907172 - Periodically invalidate the cached return value for desktopBookmarksExist(). r=lucasr --- mobile/android/base/home/BookmarksPage.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mobile/android/base/home/BookmarksPage.java b/mobile/android/base/home/BookmarksPage.java index b22057dd49c..8c08dbe367e 100644 --- a/mobile/android/base/home/BookmarksPage.java +++ b/mobile/android/base/home/BookmarksPage.java @@ -149,6 +149,10 @@ public class BookmarksPage extends HomeFragment { }); mList.setAdapter(mListAdapter); + // Invalidate the cached value that keeps track of whether or + // not desktop bookmarks (or reading list items) exist. + BrowserDB.invalidateCachedState(); + // Create callbacks before the initial loader is started. mLoaderCallbacks = new CursorLoaderCallbacks(activity, getLoaderManager()); mThumbnailsLoaderCallbacks = new ThumbnailsLoaderCallbacks();