Merge m-c to b2ginbound, a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 17 Feb 2016 11:15:36 -0800
changeset 333048 0c04a9efadab6cbcc8f3d84993462bae0088b1be
parent 333047 37d922849ba673847633d23b301ff7145cb6d0f0 (current diff)
parent 331663 0629918a09ae87808efdda432d7852371ba37db6 (diff)
child 333049 c1f6e88f923e2aa5d5e0f76029c74a66986a62e1
push id11256
push userjlorenzo@mozilla.com
push dateMon, 22 Feb 2016 16:16:37 +0000
reviewersmerge
milestone47.0a1
Merge m-c to b2ginbound, a=merge
browser/base/content/test/general/browser_contextSearchTabPosition.js
browser/base/content/test/general/browser_visibleLabel.js
browser/base/content/test/general/file_bug550565_favicon.ico
browser/base/content/test/general/file_bug550565_popup.html
browser/extensions/loop/chrome/content/shared/test/models_test.js
build/msys-perl-wrapper
build/unix/build-clang/README
build/unix/build-clang/build-clang.py
build/unix/build-clang/clang-static-analysis-linux64-centos6.json
build/unix/build-clang/clang-static-analysis-linux64.json
build/unix/build-clang/clang-static-analysis-macosx64.json
build/unix/build-clang/llvm-debug-frame.patch
build/unix/build-clang/query-selector-visibility.patch
build/unix/build-clang/return-empty-string-non-mangled.patch
config/makefiles/rcs.mk
devtools/client/performance/test/browser_perf-jit-view-01.js
devtools/client/performance/test/browser_perf-jit-view-02.js
devtools/client/shared/DeveloperToolbar.jsm
devtools/server/tests/browser/browser_animation_name.js
dom/base/nsDOMSettableTokenList.cpp
dom/base/nsDOMSettableTokenList.h
dom/webidl/DOMSettableTokenList.webidl
gfx/2d/DrawTargetD2D.cpp
gfx/2d/DrawTargetD2D.h
gfx/2d/SourceSurfaceD2D.cpp
gfx/2d/SourceSurfaceD2D.h
gfx/2d/SourceSurfaceD2DTarget.cpp
gfx/2d/SourceSurfaceD2DTarget.h
gfx/skia/skia/include/core/SkDither.h
gfx/skia/skia/include/core/SkTDict.h
gfx/skia/skia/include/core/SkTSearch.h
gfx/skia/skia/include/effects/SkModeColorFilter.h
gfx/skia/skia/include/effects/SkRectShaderImageFilter.h
gfx/skia/skia/include/gpu/GrRect.h
gfx/skia/skia/include/pipe/SkGPipe.h
gfx/skia/skia/include/private/SkUtility.h
gfx/skia/skia/include/utils/SkCubicInterval.h
gfx/skia/skia/include/utils/SkCullPoints.h
gfx/skia/skia/include/utils/SkParsePaint.h
gfx/skia/skia/src/codec/SkCodec_libgif.cpp
gfx/skia/skia/src/codec/SkCodec_libgif.h
gfx/skia/skia/src/codec/SkCodec_libico.cpp
gfx/skia/skia/src/codec/SkCodec_libico.h
gfx/skia/skia/src/codec/SkCodec_wbmp.cpp
gfx/skia/skia/src/codec/SkCodec_wbmp.h
gfx/skia/skia/src/core/SkBitmapFilter.cpp
gfx/skia/skia/src/core/SkFilterShader.cpp
gfx/skia/skia/src/core/SkFilterShader.h
gfx/skia/skia/src/core/SkPx.h
gfx/skia/skia/src/effects/SkColorFilters.cpp
gfx/skia/skia/src/effects/SkRectShaderImageFilter.cpp
gfx/skia/skia/src/effects/gradients/SkRadialGradient_Table.h
gfx/skia/skia/src/gpu/GrAtlasTextBlob.cpp
gfx/skia/skia/src/gpu/GrAtlasTextBlob.h
gfx/skia/skia/src/gpu/GrAtlasTextContext.cpp
gfx/skia/skia/src/gpu/GrAtlasTextContext.h
gfx/skia/skia/src/gpu/GrBatchFontCache.cpp
gfx/skia/skia/src/gpu/GrBatchFontCache.h
gfx/skia/skia/src/gpu/GrFontScaler.cpp
gfx/skia/skia/src/gpu/GrFontScaler.h
gfx/skia/skia/src/gpu/GrStencilAndCoverTextContext.cpp
gfx/skia/skia/src/gpu/GrStencilAndCoverTextContext.h
gfx/skia/skia/src/gpu/GrTextBlobCache.cpp
gfx/skia/skia/src/gpu/GrTextBlobCache.h
gfx/skia/skia/src/gpu/GrTextContext.cpp
gfx/skia/skia/src/gpu/GrTextContext.h
gfx/skia/skia/src/gpu/gl/GrGLNameAllocator.cpp
gfx/skia/skia/src/gpu/gl/GrGLNameAllocator.h
gfx/skia/skia/src/opts/SkPx_neon.h
gfx/skia/skia/src/opts/SkPx_none.h
gfx/skia/skia/src/opts/SkPx_sse.h
gfx/skia/skia/src/pipe/SkGPipePriv.h
gfx/skia/skia/src/pipe/SkGPipeRead.cpp
gfx/skia/skia/src/pipe/SkGPipeWrite.cpp
gfx/skia/skia/src/pipe/utils/SamplePipeControllers.cpp
gfx/skia/skia/src/pipe/utils/SamplePipeControllers.h
gfx/skia/skia/src/ports/SkGlobalInitialization_chromium.cpp
gfx/skia/skia/src/utils/SkCubicInterval.cpp
gfx/skia/skia/src/utils/SkCullPoints.cpp
gfx/skia/skia/src/utils/android/SkAndroidSDKCanvas.cpp
gfx/skia/skia/src/utils/android/SkAndroidSDKCanvas.h
js/src/jit-test/tests/gc/withStatementOffThread.js
js/src/jit-test/tests/proxy/testDirectProxyEnumerate2.js
js/src/jit-test/tests/proxy/testDirectProxyEnumerate3.js
js/src/jit-test/tests/proxy/testDirectProxyEnumerate4.js
layout/reftests/bidi/83958-2c.html
mfbt/decimal/LICENSE-APPLE
mfbt/decimal/LICENSE-LGPL-2
mfbt/decimal/LICENSE-LGPL-2.1
mfbt/decimal/floor-ceiling.patch
mobile/android/base/resources/drawable-large-v11/site_security_level.xml
mobile/android/base/resources/drawable-large-v11/site_security_unknown.xml
mobile/android/base/resources/drawable/find_matchcase_selector.xml
mobile/android/gradle/m2repo/com/stanfy/spoon/spoon-gradle-plugin/1.0.3-SNAPSHOT/maven-metadata-local.xml
mobile/android/gradle/m2repo/com/stanfy/spoon/spoon-gradle-plugin/1.0.3-SNAPSHOT/spoon-gradle-plugin-1.0.3-SNAPSHOT-javadoc.jar
mobile/android/gradle/m2repo/com/stanfy/spoon/spoon-gradle-plugin/1.0.3-SNAPSHOT/spoon-gradle-plugin-1.0.3-SNAPSHOT-sources.jar
mobile/android/gradle/m2repo/com/stanfy/spoon/spoon-gradle-plugin/1.0.3-SNAPSHOT/spoon-gradle-plugin-1.0.3-SNAPSHOT.jar
mobile/android/gradle/m2repo/com/stanfy/spoon/spoon-gradle-plugin/1.0.3-SNAPSHOT/spoon-gradle-plugin-1.0.3-SNAPSHOT.pom
mobile/android/gradle/m2repo/com/stanfy/spoon/spoon-gradle-plugin/maven-metadata-local.xml
mobile/android/modules/MulticastDNS.jsm
testing/eslint-plugin-mozilla/docs/components-imports.rst
testing/eslint-plugin-mozilla/docs/import-globals-from.rst
testing/eslint-plugin-mozilla/docs/this-top-level-scope.rst
testing/eslint-plugin-mozilla/lib/rules/components-imports.js
testing/eslint-plugin-mozilla/lib/rules/import-globals-from.js
testing/eslint-plugin-mozilla/lib/rules/this-top-level-scope.js
testing/mochitest/b2g_start_script.js
testing/mozharness/configs/single_locale/mozilla-aurora_android-api-11.py
testing/mozharness/configs/single_locale/mozilla-aurora_android.py
testing/mozharness/configs/single_locale/mozilla-central_android-api-11.py
testing/mozharness/configs/single_locale/mozilla-central_android.py
testing/mozharness/configs/single_locale/release_mozilla-beta_android.py
testing/mozharness/configs/single_locale/release_mozilla-release_android.py
testing/mozharness/configs/single_locale/staging_release_mozilla-beta_android.py
testing/mozharness/configs/single_locale/staging_release_mozilla-release_android.py
testing/mozharness/scripts/merge_day/b2g_tag.py
testing/web-platform/meta/html/browsers/the-window-object/window-named-properties.html.ini
testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-001a.html.ini
testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002a.html.ini
testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-004a.html.ini
testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-005a.html.ini
testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006a.html.ini
testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-008a.html.ini
testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009c.html.ini
toolkit/components/satchel/test/browser/head.js
toolkit/crashreporter/Makefile.in
toolkit/crashreporter/breakpad-logging/BreakpadLogging.cpp
toolkit/crashreporter/breakpad-logging/BreakpadLogging.h
toolkit/crashreporter/breakpad-logging/moz.build
toolkit/crashreporter/breakpad-patches/00-module-api-extras.patch
toolkit/crashreporter/breakpad-patches/02-cfi-rule-repr.patch
toolkit/crashreporter/breakpad-patches/03-unique-string.patch
toolkit/crashreporter/breakpad-patches/04-uniquestringmap.patch
toolkit/crashreporter/breakpad-patches/08-dwarf2reader-dynamic-cast.patch
toolkit/crashreporter/breakpad-patches/09-bug779291.patch
toolkit/crashreporter/breakpad-patches/10-logging.patch
toolkit/crashreporter/breakpad-patches/11-readsymboldatainternal-proto.patch
toolkit/crashreporter/breakpad-patches/12-bug863475.patch
toolkit/crashreporter/breakpad-patches/13-bug836829.patch
toolkit/crashreporter/breakpad-patches/14-bug883126.patch
toolkit/crashreporter/breakpad-patches/15-bug859745.patch
toolkit/crashreporter/breakpad-patches/16-sht-arm-exidx-define.patch
toolkit/crashreporter/breakpad-patches/17-bug942407-usersig.patch
toolkit/crashreporter/breakpad-patches/18-bug945498-expose-handle-signal.patch
toolkit/crashreporter/breakpad-patches/19-bug942290-breakpad-exidx-merge.patch
toolkit/crashreporter/breakpad-patches/20-bug1068410-no-procfs-search.patch
toolkit/crashreporter/breakpad-patches/21-bug1068410-crash-server-pipe.patch
toolkit/crashreporter/google-breakpad/COPYING
toolkit/crashreporter/google-breakpad/README
toolkit/crashreporter/google-breakpad/SVN-INFO
toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/moz.build
toolkit/crashreporter/google-breakpad/src/client/linux/handler/moz.build
toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/moz.build
toolkit/crashreporter/google-breakpad/src/client/mac/tests/SimpleStringDictionaryTest.h
toolkit/crashreporter/google-breakpad/src/client/mac/tests/SimpleStringDictionaryTest.mm
toolkit/crashreporter/google-breakpad/src/client/windows/build/common.gypi
toolkit/crashreporter/google-breakpad/src/client/windows/build/external_code.gypi
toolkit/crashreporter/google-breakpad/src/client/windows/build/internal/release_defaults.gypi
toolkit/crashreporter/google-breakpad/src/client/windows/build/internal/release_impl.gypi
toolkit/crashreporter/google-breakpad/src/client/windows/build/internal/release_impl_official.gypi
toolkit/crashreporter/google-breakpad/src/client/windows/build/release.gypi
toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/minidump_generator.h
toolkit/crashreporter/google-breakpad/src/client/windows/unittests/exception_handler_nesting_test.cc
toolkit/crashreporter/google-breakpad/src/client/windows/unittests/exception_handler_test.h
toolkit/crashreporter/google-breakpad/src/common/android/include/sys/stat.h
toolkit/crashreporter/google-breakpad/src/common/android/include/sys/ucontext.h
toolkit/crashreporter/google-breakpad/src/common/logging.cc
toolkit/crashreporter/google-breakpad/src/common/logging.h
toolkit/crashreporter/google-breakpad/src/common/mac/GTMGarbageCollection.h
toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.h
toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm
toolkit/crashreporter/google-breakpad/src/common/pathname_stripper.cc
toolkit/crashreporter/google-breakpad/src/common/pathname_stripper.h
toolkit/crashreporter/google-breakpad/src/common/pathname_stripper_unittest.cc
toolkit/crashreporter/google-breakpad/src/common/unique_string.cc
toolkit/crashreporter/google-breakpad/src/common/unique_string.h
toolkit/crashreporter/google-breakpad/src/processor/binarystream.cc
toolkit/crashreporter/google-breakpad/src/processor/binarystream.h
toolkit/crashreporter/google-breakpad/src/processor/binarystream_unittest.cc
toolkit/crashreporter/google-breakpad/src/processor/minidump.cc
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_read_av.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_read_av_block_write.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_read_av_clobber_write.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_read_av_conditional.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_read_av_then_jmp.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_read_av_xchg_write.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_write_av.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_write_av_arg_to_call.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/exec_av_on_stack.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/linux_test_app.cc
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.dump.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.machine_readable.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/module0.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/module1.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/module2.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/module3_bad.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/module4_bad.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/null_read_av.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/null_write_av.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/read_av_clobber_write.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/read_av_conditional.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/read_av_non_null.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/stack_exhaustion.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542/kernel32.sym
toolkit/crashreporter/google-breakpad/src/processor/testdata/symbols/test_app.pdb/5A9832E5287241C1838ED98914E9B7FF1/test_app.sym
toolkit/crashreporter/google-breakpad/src/processor/testdata/test_app.cc
toolkit/crashreporter/google-breakpad/src/processor/testdata/write_av_non_null.dmp
toolkit/crashreporter/google-breakpad/src/third_party/glog/AUTHORS
toolkit/crashreporter/google-breakpad/src/third_party/glog/COPYING
toolkit/crashreporter/google-breakpad/src/third_party/glog/ChangeLog
toolkit/crashreporter/google-breakpad/src/third_party/glog/INSTALL
toolkit/crashreporter/google-breakpad/src/third_party/glog/Makefile.am
toolkit/crashreporter/google-breakpad/src/third_party/glog/NEWS
toolkit/crashreporter/google-breakpad/src/third_party/glog/README
toolkit/crashreporter/google-breakpad/src/third_party/glog/README.windows
toolkit/crashreporter/google-breakpad/src/third_party/glog/aclocal.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/compile
toolkit/crashreporter/google-breakpad/src/third_party/glog/config.guess
toolkit/crashreporter/google-breakpad/src/third_party/glog/config.sub
toolkit/crashreporter/google-breakpad/src/third_party/glog/configure
toolkit/crashreporter/google-breakpad/src/third_party/glog/configure.ac
toolkit/crashreporter/google-breakpad/src/third_party/glog/depcomp
toolkit/crashreporter/google-breakpad/src/third_party/glog/doc/designstyle.css
toolkit/crashreporter/google-breakpad/src/third_party/glog/doc/glog.html
toolkit/crashreporter/google-breakpad/src/third_party/glog/google-glog.sln
toolkit/crashreporter/google-breakpad/src/third_party/glog/install-sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/libglog.pc.in
toolkit/crashreporter/google-breakpad/src/third_party/glog/ltmain.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ac_have_attribute.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ac_have_builtin_expect.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ac_have_sync_val_compare_and_swap.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ac_rwlock.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/acx_pthread.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/google_namespace.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/libtool.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ltoptions.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ltsugar.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ltversion.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/lt~obsolete.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/namespaces.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/pc_from_ucontext.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/stl_namespace.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/using_operator.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/missing
toolkit/crashreporter/google-breakpad/src/third_party/glog/mkinstalldirs
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/README
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/changelog
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/compat
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/control
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/copyright
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/docs
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/libgoogle-glog-dev.dirs
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/libgoogle-glog-dev.install
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/libgoogle-glog0.dirs
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/libgoogle-glog0.install
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/rules
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/rpm.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/rpm/rpm.spec
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/base/commandlineflags.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/base/googleinit.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/base/mutex.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/config.h.in
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/config_for_unittests.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/demangle.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/demangle.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/demangle_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/demangle_unittest.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/demangle_unittest.txt
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/glog/log_severity.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/glog/logging.h.in
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/glog/raw_logging.h.in
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/glog/stl_logging.h.in
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/glog/vlog_is_on.h.in
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/googletest.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging_striplog_test.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging_striptest10.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging_striptest2.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging_striptest_main.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging_unittest.err
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/mock-log.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/mock-log_test.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/raw_logging.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/signalhandler.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/signalhandler_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/signalhandler_unittest.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace_generic-inl.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace_libunwind-inl.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace_powerpc-inl.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace_x86-inl.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace_x86_64-inl.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stl_logging_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/symbolize.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/symbolize.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/symbolize_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/utilities.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/utilities.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/utilities_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/vlog_is_on.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/config.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/glog/log_severity.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/glog/logging.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/glog/raw_logging.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/glog/stl_logging.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/glog/vlog_is_on.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/port.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/port.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/preprocess.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/vsprojects/libglog/libglog.vcproj
toolkit/crashreporter/google-breakpad/src/third_party/glog/vsprojects/libglog_static/libglog_static.vcproj
toolkit/crashreporter/google-breakpad/src/third_party/glog/vsprojects/logging_unittest/logging_unittest.vcproj
toolkit/crashreporter/google-breakpad/src/third_party/glog/vsprojects/logging_unittest_static/logging_unittest_static.vcproj
toolkit/crashreporter/google-breakpad/src/tools/windows/dump_syms/testdata/dump_syms_regtest.cc
toolkit/crashreporter/google-breakpad/src/tools/windows/dump_syms/testdata/dump_syms_regtest.pdb
toolkit/crashreporter/google-breakpad/src/tools/windows/dump_syms/testdata/dump_syms_regtest.sym
toolkit/crashreporter/google-breakpad/src/tools/windows/symupload/symupload.vcproj
toolkit/xre/make-platformini.py
tools/profiler/core/shim_mac_dump_syms.h
tools/profiler/core/shim_mac_dump_syms.mm
tools/profiler/gecko/local_debug_info_symbolizer.cc
tools/profiler/gecko/local_debug_info_symbolizer.h
webapprt/Makefile.in
xpcom/glue/nsProxyRelease.cpp
xpcom/tests/bug656331_component/Makefile.in
xpcom/tests/component/Makefile.in
xulrunner/README.xulrunner
xulrunner/app.mozbuild
xulrunner/app/Makefile.in
xulrunner/app/default16.png
xulrunner/app/default32.png
xulrunner/app/default48.png
xulrunner/app/document.ico
xulrunner/app/install_app.py
xulrunner/app/macbuild/Info.plist.in
xulrunner/app/macbuild/InfoPlist.strings
xulrunner/app/moz.build
xulrunner/app/nsXULRunnerApp.cpp
xulrunner/app/splash.rc
xulrunner/app/xulrunner.exe.manifest
xulrunner/app/xulrunner.ico
xulrunner/app/xulrunner.js
xulrunner/build.mk
xulrunner/config/mozconfig
xulrunner/config/mozconfigs/common
xulrunner/config/mozconfigs/common.override
xulrunner/config/mozconfigs/linux32/xulrunner
xulrunner/config/mozconfigs/linux32/xulrunner-qt
xulrunner/config/mozconfigs/linux64/xulrunner
xulrunner/config/mozconfigs/macosx-universal/xulrunner
xulrunner/config/mozconfigs/win32/xulrunner
xulrunner/config/mozconfigs/win64/xulrunner
xulrunner/confvars.sh
xulrunner/examples/moz.build
xulrunner/examples/simple/application.ini
xulrunner/examples/simple/components/moz.build
xulrunner/examples/simple/components/public/moz.build
xulrunner/examples/simple/components/public/nsISimpleTest.idl
xulrunner/examples/simple/components/src/SimpleTest.cpp
xulrunner/examples/simple/components/src/SimpleTest.js
xulrunner/examples/simple/components/src/SimpleTest.manifest
xulrunner/examples/simple/components/src/moz.build
xulrunner/examples/simple/content/contents.rdf
xulrunner/examples/simple/content/simple.js
xulrunner/examples/simple/content/simple.xul
xulrunner/examples/simple/icons/simple.ico
xulrunner/examples/simple/jar.mn
xulrunner/examples/simple/locale/simple.dtd
xulrunner/examples/simple/moz.build
xulrunner/examples/simple/simple-prefs.js
xulrunner/installer/Makefile.in
xulrunner/installer/debian/changelog.in
xulrunner/installer/debian/compat
xulrunner/installer/debian/control
xulrunner/installer/debian/icon_base64
xulrunner/installer/debian/menu
xulrunner/installer/debian/postinst.in
xulrunner/installer/debian/prerm.in
xulrunner/installer/debian/xulrunner.links.in
xulrunner/installer/debian/xulrunner.service.in
xulrunner/installer/libxul-embedding.pc.in
xulrunner/installer/libxul.pc.in
xulrunner/installer/moz.build
xulrunner/installer/mozilla-js.pc.in
xulrunner/installer/mozilla-nspr.pc.in
xulrunner/installer/mozilla-nss.pc.in
xulrunner/installer/mozilla-plugin.pc.in
xulrunner/locales/all-locales
xulrunner/moz.build
xulrunner/stub/Makefile.in
xulrunner/stub/moz.build
xulrunner/stub/nsXULStub.cpp
xulrunner/stub/xulrunner-stub.exe.manifest
xulrunner/stub/xulrunner-stub.rc
xulrunner/tools/redit/Makefile.in
xulrunner/tools/redit/moz.build
xulrunner/tools/redit/redit.cpp
--- a/.eslintignore
+++ b/.eslintignore
@@ -59,17 +59,16 @@ b2g/locales/en-US/b2g-l10n.js
 browser/app/**
 browser/base/content/browser-social.js
 browser/base/content/nsContextMenu.js
 browser/base/content/sanitizeDialog.js
 browser/base/content/test/**
 browser/base/content/newtab/**
 browser/components/downloads/**
 browser/components/feeds/**
-browser/components/migration/**
 browser/components/pocket/**
 browser/components/preferences/**
 browser/components/privatebrowsing/**
 browser/components/sessionstore/**
 browser/components/shell/**
 browser/components/tabview/**
 browser/components/translation/**
 browser/extensions/pdfjs/**
@@ -87,17 +86,16 @@ devtools/client/canvasdebugger/**
 devtools/client/commandline/**
 devtools/client/debugger/**
 devtools/client/eyedropper/**
 devtools/client/framework/**
 # devtools/client/inspector/shared/*.js files are eslint-clean, so they aren't
 # included in the ignore list.
 devtools/client/inspector/computed/**
 devtools/client/inspector/fonts/**
-devtools/client/inspector/layout/**
 devtools/client/inspector/markup/test/**
 devtools/client/inspector/rules/**
 devtools/client/inspector/shared/test/**
 devtools/client/inspector/test/**
 devtools/client/inspector/*.js
 devtools/client/jsonview/**
 devtools/client/memory/**
 devtools/client/netmonitor/**
@@ -184,18 +182,18 @@ toolkit/components/workerloader/tests/mo
 # Tests old non-star function generators
 toolkit/modules/tests/xpcshell/test_task.js
 
 # Not yet updated
 toolkit/components/osfile/**
 toolkit/components/passwordmgr/**
 
 # Uses preprocessing
-toolkit/content/contentAreaUtils.js
 toolkit/content/widgets/videocontrols.xml
+toolkit/content/widgets/wizard.xml
 toolkit/components/jsdownloads/src/DownloadIntegration.jsm
 toolkit/components/search/nsSearchService.js
 toolkit/components/url-classifier/**
 toolkit/components/urlformatter/nsURLFormatter.js
 toolkit/identity/FirefoxAccounts.jsm
 toolkit/modules/AppConstants.jsm
 toolkit/mozapps/downloads/nsHelperAppDlg.js
 toolkit/mozapps/extensions/internal/AddonConstants.jsm
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,14 +1,12 @@
 {
   // When adding items to this file please check for effects on sub-directories.
   "plugins": [
     "mozilla"
   ],
   "rules": {
-    "mozilla/components-imports": 1,
-    "mozilla/import-globals-from": 1,
-    "mozilla/this-top-level-scope": 1,
+    "mozilla/import-globals": 1,
   },
   "env": {
     "es6": true
   },
 }
--- a/.gitignore
+++ b/.gitignore
@@ -49,16 +49,26 @@ js/src/tests/results-*.txt
 parser/html/java/htmlparser/
 parser/html/java/javaparser/
 
 # Ignore the files and directory that Eclipse IDE creates
 .project
 .cproject
 .settings/
 
+# Ignore the files and directory that JetBrains IDEs create.
+/.idea/
+*.iml
+
+# Gradle cache.
+/.gradle/
+
+# Local Gradle configuration properties.
+/local.properties
+
 # Python virtualenv artifacts.
 python/psutil/**/*.so
 python/psutil/**/*.pyd
 python/psutil/build/
 
 # Ignore chrome.manifest files from the devtools loader
 devtools/client/chrome.manifest
 devtools/shared/chrome.manifest
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,10 +17,10 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1237983 - Investigate and remove the Bagheera Client implementation.
+Bug 1246756 - Update Skia to m49 branch.
 
--- a/Makefile.in
+++ b/Makefile.in
@@ -10,29 +10,35 @@ make_min_ver := 3.81
 ifneq ($(make_min_ver),$(firstword $(sort $(make_min_ver) $(MAKE_VERSION))))
 $(error GNU Make $(make_min_ver) or higher is required)
 endif
 
 export TOPLEVEL_BUILD := 1
 
 default::
 
+ifndef TEST_MOZBUILD
 ifdef MOZ_BUILD_APP
 include $(topsrcdir)/$(MOZ_BUILD_APP)/build.mk
 endif
+endif
 
 include $(topsrcdir)/config/config.mk
 
 GARBAGE_DIRS += _javagen _profile staticlib
 DIST_GARBAGE = config.cache config.log config.status* config-defs.h \
    config/autoconf.mk \
    mozilla-config.h \
    netwerk/necko-config.h xpcom/xpcom-config.h xpcom/xpcom-private.h \
    .mozconfig.mk
 
+ifndef MOZ_PROFILE_USE
+buildid.h source-repo.h: FORCE
+endif
+
 ifdef JS_STANDALONE
 configure_dir = $(topsrcdir)/js/src
 else
 configure_dir = $(topsrcdir)
 endif
 
 BUILD_BACKEND_FILES := $(addprefix backend.,$(addsuffix Backend,$(BUILD_BACKENDS)))
 
@@ -304,22 +310,16 @@ endif # MOZ_CRASHREPORTER
 
 uploadsymbols:
 ifdef MOZ_CRASHREPORTER
 ifdef SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE
 	$(PYTHON) -u $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.py '$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
 else
 	$(SHELL) $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.sh $(SYMBOL_INDEX_NAME) '$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
 endif
-
-# MOZ_SOURCE_STAMP is defined in package-name.mk with a deferred assignment.
-# exporting it makes make run its $(shell) command for each invoked submake,
-# so transform it to an immediate assignment.
-MOZ_SOURCE_STAMP := $(MOZ_SOURCE_STAMP)
-export MOZ_SOURCE_STAMP
 endif
 
 .PHONY: update-packaging
 update-packaging:
 	$(MAKE) -C tools/update-packaging
 
 .PHONY: pretty-package
 pretty-package:
--- a/accessible/atk/moz.build
+++ b/accessible/atk/moz.build
@@ -47,11 +47,12 @@ if CONFIG['MOZ_ENABLE_GTK']:
     CFLAGS += CONFIG['TK_CFLAGS']
     CXXFLAGS += CONFIG['TK_CFLAGS']
 
 if CONFIG['MOZ_ENABLE_DBUS']:
     CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
-if CONFIG['CLANG_CXX']:
-    # Suppress clang warning about unused function from gobject's RTTI macros.
-    CXXFLAGS += ['-Wno-unused-function']
+if CONFIG['CLANG_CXX'] or CONFIG['GNU_CXX']:
+    # Used in G_DEFINE_TYPE_EXTENDED macro, probably fixed in newer glib /
+    # gobject headers. See bug 1243331 comment 3.
+    CXXFLAGS += ['-Wno-unused-local-typedefs']
--- a/accessible/base/TreeWalker.cpp
+++ b/accessible/base/TreeWalker.cpp
@@ -75,21 +75,19 @@ TreeWalker::NextChild()
   nsINode* contextNode = mContext->GetNode();
   while (mAnchorNode != contextNode) {
     nsINode* parentNode = mAnchorNode->GetFlattenedTreeParent();
     if (!parentNode || !parentNode->IsElement())
       return nullptr;
 
     nsIContent* parent = parentNode->AsElement();
     top = PushState(parent);
-    while (nsIContent* childNode = Next(top)) {
-      if (childNode == mAnchorNode) {
-        mAnchorNode = parent;
-        return NextChild();
-      }
+    if (top->mDOMIter.Seek(mAnchorNode)) {
+      mAnchorNode = parent;
+      return NextChild();
     }
 
     // XXX We really should never get here, it means we're trying to find an
     // accessible for a dom node where iterating over its parent's children
     // doesn't return it. However this sometimes happens when we're asked for
     // the nearest accessible to place holder content which we ignore.
     mAnchorNode = parent;
   }
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -816,19 +816,27 @@ Accessible::XULElmName(DocAccessible* aD
   aName.CompressWhitespace();
   if (!aName.IsEmpty())
     return;
 
   // Can get text from title of <toolbaritem> if we're a child of a <toolbaritem>
   nsIContent *bindingParent = aElm->GetBindingParent();
   nsIContent* parent =
     bindingParent? bindingParent->GetParent() : aElm->GetParent();
+  nsAutoString ancestorTitle;
   while (parent) {
     if (parent->IsXULElement(nsGkAtoms::toolbaritem) &&
-        parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
+        parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, ancestorTitle)) {
+      // Before returning this, check if the element itself has a tooltip:
+      if (aElm->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
+        aName.CompressWhitespace();
+        return;
+      }
+
+      aName.Assign(ancestorTitle);
       aName.CompressWhitespace();
       return;
     }
     parent = parent->GetParent();
   }
 }
 
 nsresult
--- a/accessible/jsat/Gestures.jsm
+++ b/accessible/jsat/Gestures.jsm
@@ -74,16 +74,20 @@ const TAP_MAX_RADIUS = 0.2;
 // consequent pointer move lines.
 const DIRECTNESS_COEFF = 1.44;
 // The virtual touch ID generated by a mouse event.
 const MOUSE_ID = 'mouse';
 // Amount in inches from the edges of the screen for it to be an edge swipe
 const EDGE = 0.1;
 // Multiply timeouts by this constant, x2 works great too for slower users.
 const TIMEOUT_MULTIPLIER = 1;
+// A single pointer down/up sequence periodically precedes the tripple swipe
+// gesture on Android. This delay acounts for that.
+const IS_ANDROID = Utils.MozBuildApp === 'mobile/android' &&
+  Utils.AndroidSdkVersion >= 14;
 
 /**
  * A point object containing distance travelled data.
  * @param {Object} aPoint A point object that looks like: {
  *   x: x coordinate in pixels,
  *   y: y coordinate in pixels
  * }
  */
@@ -197,23 +201,23 @@ this.GestureTracker = { // jshint ignore
 
   /**
    * Create a new gesture object and attach resolution handler to it as well as
    * handle the incoming pointer event.
    * @param  {Object} aDetail A new pointer event detail.
    * @param  {Number} aTimeStamp A new pointer event timeStamp.
    * @param  {Function} aGesture A gesture constructor (default: Tap).
    */
-  _init: function GestureTracker__init(aDetail, aTimeStamp, aGesture = Tap) {
+  _init: function GestureTracker__init(aDetail, aTimeStamp, aGesture) {
     // Only create a new gesture on |pointerdown| event.
     if (aDetail.type !== 'pointerdown') {
       return;
     }
     let points = aDetail.points;
-    let GestureConstructor = aGesture;
+    let GestureConstructor = aGesture || (IS_ANDROID ? DoubleTap : Tap);
     this._create(GestureConstructor);
     this._update(aDetail, aTimeStamp);
   },
 
   /**
    * Handle the incoming pointer event with the existing gesture object(if
    * present) or with the newly created one.
    * @param  {Object} aDetail A new pointer event detail.
--- a/accessible/tests/mochitest/name/a11y.ini
+++ b/accessible/tests/mochitest/name/a11y.ini
@@ -8,9 +8,10 @@ support-files =
 [test_browserui.xul]
 [test_counterstyle.html]
 [test_general.html]
 [test_general.xul]
 [test_link.html]
 [test_list.html]
 [test_markup.html]
 [test_svg.html]
+[test_toolbaritem.xul]
 [test_tree.xul]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/name/test_toolbaritem.xul
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<?xml-stylesheet href="general.css"
+                 type="text/css"?>
+
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Accessibility Name Calculating Test.">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../name.js"></script>
+  <script type="application/javascript">
+  <![CDATA[
+    var gQueue = null;
+    function doTest() {
+      let ids = [];
+      for (let item of ["button", "textbox"]) {
+        ids.push(item + "withtooltip");
+        ids.push(item + "withouttooltip");
+        ids.push("nested" + item + "withtooltip");
+        ids.push("nested" + item + "withouttooltip");
+      }
+
+      for (let id of ids) {
+        if (id.endsWith("withtooltip")) {
+          testName(id, id, id + " should have individual name from its tooltip - ");
+        } else {
+          testName(id, "Toolbaritem title", id + " should have toolbaritem's title for a name - ");
+        }
+      }
+      SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  ]]>
+  </script>
+
+  <hbox flex="1" style="overflow: auto;">
+
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <a target="_blank"
+       href="https://bugzilla.mozilla.org/show_bug.cgi?id=1216478"
+       title="Items with tooltips inside items with a label should use their own tooltip as an accessible name, not the ancestor's label">
+      Mozilla Bug 1216478
+    </a>
+    <p id="display"></p>
+    <div id="content" style="display: none">
+    </div>
+    <pre id="test">
+    </pre>
+  </body>
+
+  <vbox flex="1">
+    <toolbox>
+      <toolbar>
+        <toolbaritem title="Toolbaritem title">
+          <toolbarbutton id="buttonwithtooltip" tooltiptext="buttonwithtooltip"/>
+          <toolbarbutton id="buttonwithouttooltip"/>
+          <textbox id="textboxwithtooltip" tooltiptext="textboxwithtooltip"/>
+          <textbox id="textboxwithouttooltip"/>
+          <vbox>
+            <toolbarbutton id="nestedbuttonwithtooltip" tooltiptext="nestedbuttonwithtooltip"/>
+            <toolbarbutton id="nestedbuttonwithouttooltip"/>
+            <textbox id="nestedtextboxwithtooltip" tooltiptext="nestedtextboxwithtooltip"/>
+            <textbox id="nestedtextboxwithouttooltip"/>
+          </vbox>
+        </toolbaritem>
+      </toolbar>
+    </toolbox>
+
+
+  </vbox> <!-- close tests area -->
+  </hbox> <!-- close main area -->
+</window>
--- a/accessible/tests/mochitest/relations/test_embeds.xul
+++ b/accessible/tests/mochitest/relations/test_embeds.xul
@@ -94,33 +94,33 @@
       {
         return "load uri '" + aURI + "' in new tab";
       }
     }
 
     ////////////////////////////////////////////////////////////////////////////
     // Testing
 
-    //gA11yEventDumpToConsole = true; // debug
+    gA11yEventDumpToConsole = true; // debug
 
     var gQueue = null;
     function doTests()
     {
       testRelation(browserDocument(), RELATION_EMBEDS,
                    getAccessible(currentTabDocument()));
 
-      //enableLogging("docload");
+      enableLogging("docload");
       gQueue = new eventQueue();
 
       gQueue.push(new loadURI("about:about"));
       gQueue.push(new loadOneTab("about:mozilla"));
 
       gQueue.onFinish = function()
       {
-        //disableLogging();
+        disableLogging();
         closeBrowserWindow();
       }
       gQueue.invoke();
     }
 
     SimpleTest.waitForExplicitFinish();
     openBrowserWindow(doTests, "about:");
   ]]>
--- a/addon-sdk/source/lib/sdk/io/fs.js
+++ b/addon-sdk/source/lib/sdk/io/fs.js
@@ -116,18 +116,18 @@ function remove(path, recursive) {
   else {
     throw FSError("remove", "ENOENT", 34, path);
   }
 }
 
 /**
  * Utility function to convert either an octal number or string
  * into an octal number
- * 0777 => 0777
- * "0644" => 0644
+ * 0777 => 0o777
+ * "0644" => 0o644
  */
 function Mode(mode, fallback) {
   return isString(mode) ? parseInt(mode, 8) : mode || fallback;
 }
 function Flags(flag) {
   return !isString(flag) ? flag :
          FLAGS[flag] || Error("Unknown file open flag: " + flag);
 }
--- a/addon-sdk/source/lib/sdk/loader/cuddlefish.js
+++ b/addon-sdk/source/lib/sdk/loader/cuddlefish.js
@@ -21,17 +21,17 @@ const { classes: Cc, Constructor: CC, in
 // `loadSandbox` is exposed by bootstrap.js
 const loaderURI = module.uri.replace("sdk/loader/cuddlefish.js",
                                      "toolkit/loader.js");
 const xulappURI = module.uri.replace("loader/cuddlefish.js",
                                      "system/xul-app.jsm");
 // We need to keep a reference to the sandbox in order to unload it in
 // bootstrap.js
 
-const loaderSandbox = loadSandbox(loaderURI);
+var loaderSandbox = loadSandbox(loaderURI);
 const loaderModule = loaderSandbox.exports;
 
 const { incompatibility } = Cu.import(xulappURI, {}).XulApp;
 
 const { override, load } = loaderModule;
 
 function CuddlefishLoader(options) {
   let { manifest } = options;
--- a/addon-sdk/source/lib/sdk/places/host/host-query.js
+++ b/addon-sdk/source/lib/sdk/places/host/host-query.js
@@ -42,28 +42,42 @@ const MANUAL_QUERY_PROPERTIES = [
 const PLACES_PROPERTIES = [
   'uri', 'title', 'accessCount', 'time'
 ];
 
 function execute (queries, options) {
   return new Promise(resolve => {
     let root = historyService
         .executeQueries(queries, queries.length, options).root;
-    resolve(collect([], root));
+    // Let's extract an eventual uri wildcard, if both domain and uri are set.
+    // See utils.js::urlQueryParser() for more details.
+    // In case of multiple queries, we only retain the first found wildcard.
+    let uriWildcard = queries.reduce((prev, query) => {
+      if (query.uri && query.domain) {
+        if (!prev)
+          prev = query.uri.spec;
+        query.uri = null;
+      }
+      return prev;
+    }, "");
+    resolve(collect([], root, uriWildcard));
   });
 }
 
-function collect (acc, node) {
+function collect (acc, node, uriWildcard) {
   node.containerOpen = true;
   for (let i = 0; i < node.childCount; i++) {
     let child = node.getChild(i);
-    acc.push(child);
+
+    if (!uriWildcard || child.uri.startsWith(uriWildcard)) {
+      acc.push(child);
+    }
     if (child.type === child.RESULT_TYPE_FOLDER) {
       let container = child.QueryInterface(Ci.nsINavHistoryContainerResultNode);
-      collect(acc, container);
+      collect(acc, container, uriWildcard);
     }
   }
   node.containerOpen = false;
   return acc;
 }
 
 function query (queries, options) {
   return new Promise((resolve, reject) => {
--- a/addon-sdk/source/lib/sdk/places/utils.js
+++ b/addon-sdk/source/lib/sdk/places/utils.js
@@ -7,26 +7,28 @@
 module.metadata = {
   "stability": "experimental",
   "engines": {
     "Firefox": "*",
     "SeaMonkey": "*"
   }
 };
 
-const { Cc, Ci } = require('chrome');
+const { Cc, Ci, Cu } = require('chrome');
 const { Class } = require('../core/heritage');
 const { method } = require('../lang/functional');
 const { defer, promised, all } = require('../core/promise');
 const { send } = require('../addon/events');
 const { EventTarget } = require('../event/target');
 const { merge } = require('../util/object');
 const bmsrv = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
                 getService(Ci.nsINavBookmarksService);
 
+Cu.importGlobalProperties(["URL"]);
+
 /*
  * TreeNodes are used to construct dependency trees
  * for BookmarkItems
  */
 var TreeNode = Class({
   initialize: function (value) {
     this.value = value;
     this.children = [];
@@ -123,29 +125,42 @@ exports.isRootGroup = isRootGroup;
 /*
  * Merges appropriate options into query based off of url
  * 4 scenarios:
  *
  * 'moz.com' // domain: moz.com, domainIsHost: true
  *    --> 'http://moz.com', 'http://moz.com/thunderbird'
  * '*.moz.com' // domain: moz.com, domainIsHost: false
  *    --> 'http://moz.com', 'http://moz.com/index', 'http://ff.moz.com/test'
- * 'http://moz.com' // url: http://moz.com/, urlIsPrefix: false
+ * 'http://moz.com' // uri: http://moz.com/
  *    --> 'http://moz.com/'
- * 'http://moz.com/*' // url: http://moz.com/, urlIsPrefix: true
+ * 'http://moz.com/*' // uri: http://moz.com/, domain: moz.com, domainIsHost: true
  *    --> 'http://moz.com/', 'http://moz.com/thunderbird'
  */
 
 function urlQueryParser (query, url) {
   if (!url) return;
   if (/^https?:\/\//.test(url)) {
     query.uri = url.charAt(url.length - 1) === '/' ? url : url + '/';
     if (/\*$/.test(url)) {
-      query.uri = url.replace(/\*$/, '');
-      query.uriIsPrefix = true;
+      // Wildcard searches on URIs are not supported, so try to extract a
+      // domain and filter the data later.
+      url = url.replace(/\*$/, '');
+      try {
+        query.domain = new URL(url).hostname;
+        query.domainIsHost = true;
+        // Unfortunately here we cannot use an expando to store the wildcard,
+        // cause the query is a wrapped native XPCOM object, so we reuse uri.
+        // We clearly don't want to query for both uri and domain, thus we'll
+        // have to handle this in host-query.js::execute()
+        query.uri = url;
+      } catch (ex) {
+        // Cannot extract an host cause it's not a valid uri, the query will
+        // just return nothing.
+      }
     }
   } else {
     if (/^\*/.test(url)) {
       query.domain = url.replace(/^\*\./, '');
       query.domainIsHost = false;
     } else {
       query.domain = url;
       query.domainIsHost = true;
--- a/addon-sdk/source/lib/sdk/preferences/service.js
+++ b/addon-sdk/source/lib/sdk/preferences/service.js
@@ -32,19 +32,16 @@ const Branch = function(branchName) {
     getOwnPropertyDescriptor(target, name, receiver) {
       return {
         configurable: true,
         enumerable: true,
         writable: false,
         value: this.get(target, name, receiver)
       };
     },
-    enumerate(target) {
-      return branchKeys(branchName)[Symbol.iterator]();
-    },
     ownKeys(target) {
       return branchKeys(branchName);
     },
     get(target, name, receiver) {
       return get(`${branchName}${name}`);
     },
     set(target, name, value, receiver) {
       set(`${branchName}${name}`, value);
--- a/b2g/app/Makefile.in
+++ b/b2g/app/Makefile.in
@@ -1,15 +1,12 @@
 # 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/.
 
-USE_RCS_MK := 1
-include $(topsrcdir)/config/makefiles/rcs.mk
-
 # Make sure the standalone glue doesn't try to get libxpcom.so from b2g/app.
 NSDISTMODE = copy
 
 include $(topsrcdir)/config/rules.mk
 
 APP_ICON = app
 
 APP_BINARY = $(MOZ_APP_NAME)$(BIN_SUFFIX)
@@ -56,15 +53,14 @@ tools repackage:: $(libs-preqs)
 else # MOZ_WIDGET_TOOLKIT != cocoa
 
 libs::
 	$(NSINSTALL) -D $(DIST)/bin/chrome/icons/default
 
 # Copy the app icon for b2g-desktop
 ifeq ($(OS_ARCH),WINNT)
 	cp $(DIST)/branding/$(APP_ICON).ico $(DIST)/bin/chrome/icons/default/$(APP_ICON).ico
-	$(DIST)/bin/redit$(HOST_BIN_SUFFIX) $(DIST)/bin/$(APP_BINARY) $(DIST)/branding/$(APP_ICON).ico
 	cp $(DIST)/branding/$(APP_ICON).ico $(DIST)/bin/chrome/icons/default/default.ico
 else ifneq (gonk,$(MOZ_WIDGET_TOOLKIT))
 	cp $(DIST)/branding/default.png $(DIST)/bin/chrome/icons/default/default.png
 endif
 
 endif
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1010,17 +1010,16 @@ pref("dom.downloads.max_retention_days",
 //
 // To prevent SD card DoS attacks via downloads we disable background handling.
 //
 pref("security.exthelperapp.disable_background_handling", true);
 
 // Inactivity time in milliseconds after which we shut down the OS.File worker.
 pref("osfile.reset_worker_delay", 5000);
 
-pref("apz.displayport_expiry_ms", 0);
 // APZ physics settings, tuned by UX designers
 pref("apz.axis_lock.mode", 2); // Use "sticky" axis locking
 pref("apz.fling_curve_function_x1", "0.41");
 pref("apz.fling_curve_function_y1", "0.0");
 pref("apz.fling_curve_function_x2", "0.80");
 pref("apz.fling_curve_function_y2", "1.0");
 pref("apz.fling_curve_threshold_inches_per_ms", "0.01");
 pref("apz.fling_friction", "0.0019");
--- a/b2g/components/HelperAppDialog.js
+++ b/b2g/components/HelperAppDialog.js
@@ -84,28 +84,28 @@ HelperAppLauncherDialog.prototype = {
           else
             aLocalFile.leafName = aLocalFile.leafName.replace(/(\.[^\.]*)?$/, "(2)$&");
         }
         else {
           // replace the last (n) in the filename with (n+1)
           aLocalFile.leafName = aLocalFile.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount+1) + ")");
         }
       }
-      aLocalFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
+      aLocalFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
     }
     catch (e) {
       dump("*** exception in makeFileUnique: " + e + "\n");
 
       if (e.result == Cr.NS_ERROR_FILE_ACCESS_DENIED)
         throw e;
 
       if (aLocalFile.leafName == "" || aLocalFile.isDirectory()) {
         aLocalFile.append("unnamed");
         if (aLocalFile.exists())
-          aLocalFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
+          aLocalFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
       }
     }
   },
 
   isUsableDirectory: function(aDirectory) {
     return aDirectory.exists() &&
            aDirectory.isDirectory() &&
            aDirectory.isWritable();
--- a/b2g/config/emulator-ics/releng-emulator-ics.tt
+++ b/b2g/config/emulator-ics/releng-emulator-ics.tt
@@ -2,15 +2,16 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
-"size": 12057960,
-"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
+"size": 12072532,
+"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
+"setup": "setup.sh",
 "unpack": true
 }
 ]
--- a/b2g/config/emulator-jb/releng-emulator-jb.tt
+++ b/b2g/config/emulator-jb/releng-emulator-jb.tt
@@ -2,15 +2,16 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
-"size": 12057960,
-"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
+"size": 12072532,
+"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
+"setup": "setup.sh",
 "unpack": true
 }
 ]
--- a/b2g/config/emulator-kk/releng-emulator-kk.tt
+++ b/b2g/config/emulator-kk/releng-emulator-kk.tt
@@ -2,15 +2,16 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
-"size": 12057960,
-"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
+"size": 12072532,
+"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
+"setup": "setup.sh",
 "unpack": true
 }
 ]
--- a/b2g/config/emulator-l/releng-emulator-l.tt
+++ b/b2g/config/emulator-l/releng-emulator-l.tt
@@ -2,15 +2,16 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
-"size": 12057960,
-"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
+"size": 12072532,
+"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
+"setup": "setup.sh",
 "unpack": true
 }
 ]
--- a/b2g/config/emulator-x86-kk/releng-emulator-kk.tt
+++ b/b2g/config/emulator-x86-kk/releng-emulator-kk.tt
@@ -2,15 +2,16 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
-"size": 12057960,
-"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
+"size": 12072532,
+"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
+"setup": "setup.sh",
 "unpack": true
 }
 ]
--- a/b2g/config/emulator-x86-l/releng-emulator-l.tt
+++ b/b2g/config/emulator-x86-l/releng-emulator-l.tt
@@ -2,15 +2,16 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
-"size": 12057960,
-"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
+"size": 12072532,
+"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
+"setup": "setup.sh",
 "unpack": true
 }
 ]
--- a/b2g/config/emulator/releng-emulator.tt
+++ b/b2g/config/emulator/releng-emulator.tt
@@ -2,15 +2,16 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
-"size": 12057960,
-"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
+"size": 12072532,
+"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
+"setup": "setup.sh",
 "unpack": true
 }
 ]
--- a/b2g/config/tooltool-manifests/linux32/releng.manifest
+++ b/b2g/config/tooltool-manifests/linux32/releng.manifest
@@ -2,18 +2,18 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
-"size": 11179576,
-"digest": "91567ce8e2bb8ab0ebc60c31e90731d88a1ea889fb71bcf55c735746a60fa7610b7e040ea3d8f727b6f692ae3ee703d6f3b30cdbd76fdf5617f77d9c38aa20ed",
+"size": 11189216,
+"digest": "18bc52b0599b1308b667e282abb45f47597bfc98a5140cfcab8da71dacf89dd76d0dee22a04ce26fe7ad1f04e2d6596991f9e5b01fd2aaaab5542965f596b0e6",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
 "setup": "setup.sh",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
--- a/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
@@ -2,18 +2,18 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
-"size": 12057960,
-"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
+"size": 12072532,
+"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
 "setup": "setup.sh",
 "unpack": true
 },
 {
 "size": 80164520,
 "digest": "26fd5301aaf6174a0e4f2ac3a8d19f39573f78a051aa78e876c065d60421b2b62207c11fbf1f20f92ba61acc4b9ce58d05409bf5af886943891b04c3d22f5e04",
--- a/b2g/dev/confvars.sh
+++ b/b2g/dev/confvars.sh
@@ -4,8 +4,9 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOZ_B2G=1
 MOZ_MULET=1
 
 . ${srcdir}/browser/confvars.sh
 
 MOZ_BUNDLED_FONTS=1
+MOZ_UA_OS_AGNOSTIC=1
--- a/b2g/graphene/graphene.js
+++ b/b2g/graphene/graphene.js
@@ -25,19 +25,16 @@ pref("layout.css.scroll-snap.enabled", t
 pref("dom.mozInputMethod.enabled", false);
 pref("browser.autofocus", true);
 pref("layers.async-pan-zoom.enabled", false);
 pref("network.predictor.enabled", true);
 
 // No AccessibleCaret
 pref("layout.accessiblecaret.enabled", false);
 
-pref("gfx.vsync.hw-vsync.enabled", true);
-pref("gfx.vsync.compositor", true);
-
 // To be removed once bug 942756 is fixed.
 pref("devtools.debugger.unix-domain-socket", "6000");
 
 pref("devtools.debugger.forbid-certified-apps", false);
 pref("devtools.debugger.prompt-connection", false);
 
 // Update url.
 pref("app.update.url", "https://aus4.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -319,19 +319,17 @@
 @RESPATH@/components/storage.xpt
 @RESPATH@/components/telemetry.xpt
 @RESPATH@/components/toolkit_asyncshutdown.xpt
 @RESPATH@/components/toolkit_filewatcher.xpt
 @RESPATH@/components/toolkit_finalizationwitness.xpt
 @RESPATH@/components/toolkit_formautofill.xpt
 @RESPATH@/components/toolkit_osfile.xpt
 @RESPATH@/components/toolkit_securityreporter.xpt
-#ifdef NIGHTLY_BUILD
 @RESPATH@/components/toolkit_perfmonitoring.xpt
-#endif
 @RESPATH@/components/toolkit_xulstore.xpt
 @RESPATH@/components/toolkitprofile.xpt
 #ifdef MOZ_ENABLE_XREMOTE
 @RESPATH@/components/toolkitremote.xpt
 #endif
 @RESPATH@/components/txtsvc.xpt
 @RESPATH@/components/txmgr.xpt
 #ifdef MOZ_USE_NATIVE_UCONV
--- a/b2g/moz.build
+++ b/b2g/moz.build
@@ -3,15 +3,12 @@
 # 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/.
 
 CONFIGURE_SUBST_FILES += ['installer/Makefile']
 
 DIRS += ['chrome', 'components', 'locales']
 
-if CONFIG['OS_ARCH'] == 'WINNT':
-    DIRS += ['../xulrunner/tools/redit']
-
 if CONFIG['GAIADIR']:
     DIRS += ['gaia']
 
 DIRS += ['app']
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -74,17 +74,17 @@ LPROJ_ROOT := $(subst -,_,$(AB_CD))
 else
 LPROJ_ROOT := $(firstword $(subst -, ,$(AB_CD)))
 endif
 LPROJ := Contents/Resources/$(LPROJ_ROOT).lproj
 
 clean clobber repackage::
 	$(RM) -r $(dist_dest)
 
-MAC_BUNDLE_VERSION = $(shell $(PYTHON) $(srcdir)/macversion.py --version=$(MOZ_APP_VERSION) --buildid=$(DEPTH)/config/buildid)
+MAC_BUNDLE_VERSION = $(shell $(PYTHON) $(srcdir)/macversion.py --version=$(MOZ_APP_VERSION) --buildid=$(DEPTH)/buildid.h)
 
 .PHONY: repackage
 tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME)
 	$(MKDIR) -p $(dist_dest)/Contents/MacOS
 	$(MKDIR) -p $(dist_dest)/$(LPROJ)
 	rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents $(dist_dest) --exclude English.lproj
 	rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents/Resources/English.lproj/ $(dist_dest)/$(LPROJ)
 	sed -e 's/%APP_VERSION%/$(MOZ_APP_VERSION)/' -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' -e 's/%MOZ_MACBUNDLE_ID%/$(MOZ_MACBUNDLE_ID)/' -e 's/%MAC_BUNDLE_VERSION%/$(MAC_BUNDLE_VERSION)/' $(srcdir)/macbuild/Contents/Info.plist.in > $(dist_dest)/Contents/Info.plist
--- a/browser/app/macversion.py
+++ b/browser/app/macversion.py
@@ -23,17 +23,17 @@ if not options.version:
     sys.exit(1)
 
 # We want to build a version number that matches the format allowed for
 # CFBundleVersion (nnnnn[.nn[.nn]]). We'll incorporate both the version
 # number as well as the date, so that it changes at least daily (for nightly
 # builds), but also so that newly-built older versions (e.g. beta build) aren't
 # considered "newer" than previously-built newer versions (e.g. a trunk nightly)
 
-buildid = open(options.buildid, 'r').read()
+define, MOZ_BUILDID, buildid = open(options.buildid, 'r').read().split()
 
 # extract only the major version (i.e. "14" from "14.0b1")
 majorVersion = re.match(r'^(\d+)[^\d].*', options.version).group(1)
 # last two digits of the year
 twodigityear = buildid[2:4]
 month = buildid[4:6]
 if month[0] == '0':
   month = month[1]
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -31,16 +31,17 @@
 
 #ifdef XP_WIN
 // we want a wmain entry point
 #ifdef MOZ_ASAN
 // ASAN requires firefox.exe to be built with -MD, and it's OK if we don't
 // support Windows XP SP2 in ASAN builds.
 #define XRE_DONT_SUPPORT_XPSP2
 #endif
+#define XRE_WANT_ENVIRON
 #include "nsWindowsWMain.cpp"
 #if defined(_MSC_VER) && (_MSC_VER < 1900)
 #define snprintf _snprintf
 #endif
 #define strcasecmp _stricmp
 #endif
 #include "BinaryPath.h"
 
@@ -118,29 +119,31 @@ static bool IsArg(const char* arg, const
 
 XRE_GetFileFromPathType XRE_GetFileFromPath;
 XRE_CreateAppDataType XRE_CreateAppData;
 XRE_FreeAppDataType XRE_FreeAppData;
 XRE_TelemetryAccumulateType XRE_TelemetryAccumulate;
 XRE_StartupTimelineRecordType XRE_StartupTimelineRecord;
 XRE_mainType XRE_main;
 XRE_StopLateWriteChecksType XRE_StopLateWriteChecks;
+XRE_XPCShellMainType XRE_XPCShellMain;
 
 static const nsDynamicFunctionLoad kXULFuncs[] = {
     { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
     { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData },
     { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData },
     { "XRE_TelemetryAccumulate", (NSFuncPtr*) &XRE_TelemetryAccumulate },
     { "XRE_StartupTimelineRecord", (NSFuncPtr*) &XRE_StartupTimelineRecord },
     { "XRE_main", (NSFuncPtr*) &XRE_main },
     { "XRE_StopLateWriteChecks", (NSFuncPtr*) &XRE_StopLateWriteChecks },
+    { "XRE_XPCShellMain", (NSFuncPtr*) &XRE_XPCShellMain },
     { nullptr, nullptr }
 };
 
-static int do_main(int argc, char* argv[], nsIFile *xreDirectory)
+static int do_main(int argc, char* argv[], char* envp[], nsIFile *xreDirectory)
 {
   nsCOMPtr<nsIFile> appini;
   nsresult rv;
   uint32_t mainFlags = 0;
 
   // Allow firefox.exe to launch XULRunner apps via -app <application.ini>
   // Note that -app must be the *first* argument.
   const char *appDataFile = getenv("XUL_APP_FILE");
@@ -167,16 +170,21 @@ static int do_main(int argc, char* argv[
     snprintf(appEnv, MAXPATHLEN, "XUL_APP_FILE=%s", argv[2]);
     if (putenv(appEnv)) {
       Output("Couldn't set %s.\n", appEnv);
       return 255;
     }
     argv[2] = argv[0];
     argv += 2;
     argc -= 2;
+  } else if (argc > 1 && IsArg(argv[1], "xpcshell")) {
+    for (int i = 1; i < argc; i++) {
+      argv[i] = argv[i + 1];
+    }
+    return XRE_XPCShellMain(--argc, argv, envp);
   }
 
   if (appini) {
     nsXREAppData *appData;
     rv = XRE_CreateAppData(appini, &appData);
     if (NS_FAILED(rv)) {
       Output("Couldn't read application.ini");
       return 255;
@@ -278,17 +286,17 @@ sizeof(XPCOM_DLL) - 1))
 #else
   rv = NS_NewNativeLocalFile(nsDependentCString(exePath), false,
                              xreDirectory);
 #endif
 
   return rv;
 }
 
-int main(int argc, char* argv[])
+int main(int argc, char* argv[], char* envp[])
 {
   mozilla::TimeStamp start = mozilla::TimeStamp::Now();
 
 #ifdef XP_MACOSX
   TriggerQuirks();
 #endif
 
   int gotCounters;
@@ -344,17 +352,17 @@ int main(int argc, char* argv[])
       XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_HARD_FAULTS,
                               int(newRUsage.ru_majflt - initialRUsage.ru_majflt));
     }
 #else
   #error "Unknown platform"  // having this here keeps cppcheck happy
 #endif
   }
 
-  int result = do_main(argc, argv, xreDirectory);
+  int result = do_main(argc, argv, envp, xreDirectory);
 
   NS_LogTerm();
 
 #ifdef XP_MACOSX
   // Allow writes again. While we would like to catch writes from static
   // destructors to allow early exits to use _exit, we know that there is
   // at least one such write that we don't control (see bug 826029). For
   // now we enable writes again and early exits will have to use exit instead
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -57,16 +57,22 @@ pref("extensions.blocklist.enabled", tru
 pref("extensions.blocklist.interval", 86400);
 // Controls what level the blocklist switches from warning about items to forcibly
 // blocking them.
 pref("extensions.blocklist.level", 2);
 pref("extensions.blocklist.url", "https://blocklist.addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
 pref("extensions.blocklist.detailsURL", "https://www.mozilla.org/%LOCALE%/blocklist/");
 pref("extensions.blocklist.itemURL", "https://blocklist.addons.mozilla.org/%LOCALE%/%APP%/blocked/%blockID%");
 
+// Kinto blocklist preferences
+pref("services.kinto.base", "https://firefox.settings.services.mozilla.com/v1");
+pref("services.kinto.bucket", "blocklists");
+pref("services.kinto.onecrl.collection", "certificates");
+pref("services.kinto.onecrl.checked", 0);
+
 pref("extensions.update.autoUpdateDefault", true);
 
 pref("extensions.hotfix.id", "firefox-hotfix@mozilla.org");
 pref("extensions.hotfix.cert.checkAttributes", true);
 pref("extensions.hotfix.certs.1.sha1Fingerprint", "91:53:98:0C:C1:86:DF:47:8F:35:22:9E:11:C9:A7:31:04:49:A1:AA");
 pref("extensions.hotfix.certs.2.sha1Fingerprint", "39:E7:2B:7A:5B:CF:37:78:F9:5D:4A:E0:53:2D:2F:3D:68:53:C5:60");
 
 // Check AUS for system add-on updates.
--- a/browser/base/content/abouthome/aboutHome.js
+++ b/browser/base/content/abouthome/aboutHome.js
@@ -364,15 +364,15 @@ function showDefaultSnippets()
       links[0].href = DEFAULT_SNIPPETS_URLS[randIndex];
     }
   }
   // Move the default snippet to the snippets element.
   snippetsElt.appendChild(entry);
 }
 
 function fitToWidth() {
-  if (window.scrollMaxX != window.scrollMinX) {
+  if (document.documentElement.scrollWidth > window.innerWidth) {
     document.body.setAttribute("narrow", "true");
   } else if (document.body.hasAttribute("narrow")) {
     document.body.removeAttribute("narrow");
     fitToWidth();
   }
 }
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -52,17 +52,39 @@
       <menuseparator id="spell-suggestions-separator"/>
       <menuitem id="context-openlinkincurrent"
                 label="&openLinkCmdInCurrent.label;"
                 accesskey="&openLinkCmdInCurrent.accesskey;"
                 oncommand="gContextMenu.openLinkInCurrent();"/>
       <menuitem id="context-openlinkintab"
                 label="&openLinkCmdInTab.label;"
                 accesskey="&openLinkCmdInTab.accesskey;"
-                oncommand="gContextMenu.openLinkInTab();"/>
+                usercontextid="0"
+                oncommand="gContextMenu.openLinkInTab(event);"/>
+
+      <menu id="context-openlinkinusercontext-menu"
+            label="&openLinkCmdInContainerTab.label;"
+            accesskey="&openLinkCmdInContainerTab.accesskey;"
+            hidden="true">
+        <menupopup oncommand="gContextMenu.openLinkInTab(event);">
+          <menuitem label="&userContextPersonal.label;"
+                    usercontextid="1"
+                    accesskey="&userContextPersonal.accesskey;"/>
+          <menuitem label="&userContextWork.label;"
+                    usercontextid="2"
+                    accesskey="&userContextWork.accesskey;"/>
+          <menuitem label="&userContextBanking.label;"
+                    usercontextid="3"
+                    accesskey="&userContextBanking.accesskey;"/>
+          <menuitem label="&userContextShopping.label;"
+                    usercontextid="4"
+                    accesskey="&userContextShopping.accesskey;"/>
+        </menupopup>
+      </menu>
+
       <menuitem id="context-openlink"
                 label="&openLinkCmd.label;"
                 accesskey="&openLinkCmd.accesskey;"
                 oncommand="gContextMenu.openLink();"/>
       <menuitem id="context-openlinkprivate"
                 label="&openLinkInPrivateWindowCmd.label;"
                 accesskey="&openLinkInPrivateWindowCmd.accesskey;"
                 oncommand="gContextMenu.openLinkInPrivateWindow();"/>
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -59,62 +59,62 @@ var FullScreen = {
       this._fullScrToggler = document.getElementById("fullscr-toggler");
       this._fullScrToggler.addEventListener("mouseover", this._expandCallback, false);
       this._fullScrToggler.addEventListener("dragenter", this._expandCallback, false);
     }
 
     if (enterFS) {
       gNavToolbox.setAttribute("inFullscreen", true);
       document.documentElement.setAttribute("inFullscreen", true);
-      if (!document.mozFullScreen && this.useLionFullScreen)
+      if (!document.fullscreenElement && this.useLionFullScreen)
         document.documentElement.setAttribute("OSXLionFullscreen", true);
     } else {
       gNavToolbox.removeAttribute("inFullscreen");
       document.documentElement.removeAttribute("inFullscreen");
       document.documentElement.removeAttribute("OSXLionFullscreen");
     }
 
-    if (!document.mozFullScreen)
+    if (!document.fullscreenElement)
       this._updateToolbars(enterFS);
 
     if (enterFS) {
       document.addEventListener("keypress", this._keyToggleCallback, false);
       document.addEventListener("popupshown", this._setPopupOpen, false);
       document.addEventListener("popuphidden", this._setPopupOpen, false);
       // In DOM fullscreen mode, we hide toolbars with CSS
-      if (!document.mozFullScreen)
+      if (!document.fullscreenElement)
         this.hideNavToolbox(true);
     }
     else {
       this.showNavToolbox(false);
       // This is needed if they use the context menu to quit fullscreen
       this._isPopupOpen = false;
       this.cleanup();
       // In TabsInTitlebar._update(), we cancel the appearance update on
       // resize event for exiting fullscreen, since that happens before we
       // change the UI here in the "fullscreen" event. Hence we need to
       // call it here to ensure the appearance is properly updated. See
       // TabsInTitlebar._update() and bug 1173768.
       TabsInTitlebar.updateAppearance(true);
     }
 
-    if (enterFS && !document.mozFullScreen) {
+    if (enterFS && !document.fullscreenElement) {
       Services.telemetry.getHistogramById("FX_BROWSER_FULLSCREEN_USED")
                         .add(1);
     }
   },
 
   exitDomFullScreen : function() {
-    document.mozCancelFullScreen();
+    document.exitFullscreen();
   },
 
   handleEvent: function (event) {
     switch (event.type) {
       case "activate":
-        if (document.mozFullScreen) {
+        if (document.fullscreenElement) {
           this._WarningBox.show();
         }
         break;
       case "fullscreen":
         this.toggle();
         break;
       case "MozDOMFullscreen:Entered": {
         // The event target is the element which requested the DOM
@@ -127,21 +127,21 @@ var FullScreen = {
         let browser;
         if (event.target == gBrowser) {
           browser = event.originalTarget;
         } else {
           let topWin = event.target.ownerDocument.defaultView.top;
           browser = gBrowser.getBrowserForContentWindow(topWin);
         }
         if (!browser || !this.enterDomFullscreen(browser)) {
-          if (document.mozFullScreen) {
+          if (document.fullscreenElement) {
             // MozDOMFullscreen:Entered is dispatched synchronously in
             // fullscreen change, hence we have to avoid calling this
             // method synchronously here.
-            setTimeout(() => document.mozCancelFullScreen(), 0);
+            setTimeout(() => document.exitFullscreen(), 0);
           }
           break;
         }
         // If it is a remote browser, send a message to ask the content
         // to enter fullscreen state. We don't need to do so if it is an
         // in-process browser, since all related document should have
         // entered fullscreen state at this point.
         if (this._isRemoteBrowser(browser)) {
@@ -173,17 +173,17 @@ var FullScreen = {
       case "DOMFullscreen:Painted": {
         Services.obs.notifyObservers(window, "fullscreen-painted", "");
         break;
       }
     }
   },
 
   enterDomFullscreen : function(aBrowser) {
-    if (!document.mozFullScreen)
+    if (!document.fullscreenElement)
       return false;
 
     // If we've received a fullscreen notification, we have to ensure that the
     // element that's requesting fullscreen belongs to the browser that's currently
     // active. If not, we exit fullscreen since the "full-screen document" isn't
     // actually visible now.
     if (gBrowser.selectedBrowser != aBrowser) {
       return false;
@@ -360,17 +360,17 @@ var FullScreen = {
         get delay() {
           return this._delay;
         }
       };
     },
 
     // Shows a warning that the site has entered fullscreen for a short duration.
     show: function(aOrigin) {
-      if (!document.mozFullScreen) {
+      if (!document.fullscreenElement) {
         return;
       }
 
       if (!this._element) {
         this._element = document.getElementById("fullscreen-warning");
         // Setup event listeners
         this._element.addEventListener("transitionend", this);
         window.addEventListener("mousemove", this, true);
--- a/browser/base/content/browser-gestureSupport.js
+++ b/browser/base/content/browser-gestureSupport.js
@@ -421,19 +421,22 @@ var gGestureSupport = {
    */
   _getPref: function GS__getPref(aPref, aDef) {
     // Preferences branch under which all gestures preferences are stored
     const branch = "browser.gesture.";
 
     try {
       // Determine what type of data to load based on default value's type
       let type = typeof aDef;
-      let getFunc = "get" + (type == "boolean" ? "Bool" :
-                             type == "number" ? "Int" : "Char") + "Pref";
-      return gPrefService[getFunc](branch + aPref);
+      let getFunc = "Char";
+      if (type == "boolean")
+        getFunc = "Bool";
+      else if (type == "number")
+        getFunc = "Int";
+      return gPrefService["get" + getFunc + "Pref"](branch + aPref);
     }
     catch (e) {
       return aDef;
     }
   },
 
   /**
    * Perform rotation for ImageDocuments
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -446,16 +446,20 @@
                    oncommand="return FeedHandler.subscribeToFeed(null, event);"
                    onclick="checkForMiddleClick(this, event);"/>
       </menu>
       <menuitem id="menu_bookmarkAllTabs"
                 label="&addCurPagesCmd.label;"
                 class="show-only-for-keyboard"
                 command="Browser:BookmarkAllTabs"
                 key="bookmarkAllTabsKb"/>
+      <menuseparator/>
+      <menuitem label="&recentBookmarks.label;"
+                id="menu_recentBookmarks"
+                disabled="true"/>
       <menuseparator id="bookmarksToolbarSeparator"/>
       <menu id="bookmarksToolbarFolderMenu"
             class="menu-iconic bookmark-item"
             label="&personalbarCmd.label;"
             container="true">
         <menupopup id="bookmarksToolbarFolderPopup"
 #ifndef XP_MACOSX
                    placespopup="true"
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -1300,16 +1300,19 @@ var BookmarkingUI = {
     if (widget.overflowed) {
       // Don't open a popup in the overflow popup, rather just open the Library.
       event.preventDefault();
       widget.node.removeAttribute("closemenu");
       PlacesCommandHook.showPlacesOrganizer("BookmarksMenu");
       return;
     }
 
+    this._updateRecentBookmarks(document.getElementById("BMB_recentBookmarks"),
+                                "subviewbutton");
+
     if (!this._popupNeedsUpdate)
       return;
     this._popupNeedsUpdate = false;
 
     let popup = event.target;
     let getPlacesAnonymousElement =
       aAnonId => document.getAnonymousElementByAttribute(popup.parentNode,
                                                          "placesanonid",
@@ -1333,16 +1336,71 @@ var BookmarkingUI = {
       extraClasses: {
         entry: "subviewbutton",
         footer: "panel-subview-footer"
       },
       insertionPoint: ".panel-subview-footer"
     });
   },
 
+  _updateRecentBookmarks: function(aHeaderItem, extraCSSClass = "") {
+    const kMaxResults = 5;
+
+    let options = PlacesUtils.history.getNewQueryOptions();
+    options.excludeQueries = true;
+    options.queryType = options.QUERY_TYPE_BOOKMARKS;
+    options.sortingMode = options.SORT_BY_DATEADDED_DESCENDING;
+    options.maxResults = kMaxResults;
+    let query = PlacesUtils.history.getNewQuery();
+
+    while (aHeaderItem.nextSibling &&
+           aHeaderItem.nextSibling.localName == "menuitem") {
+      aHeaderItem.nextSibling.remove();
+    }
+
+    PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
+                       .asyncExecuteLegacyQueries([query], 1, options, {
+      handleResult: function (aResultSet) {
+        let onItemClick = function (aEvent) {
+          let item = aEvent.target;
+          openUILink(item.getAttribute("targetURI"), aEvent);
+          CustomizableUI.hidePanelForNode(item);
+        };
+
+        let fragment = document.createDocumentFragment();
+        let row;
+        while ((row = aResultSet.getNextRow())) {
+          let uri = row.getResultByIndex(1);
+          let title = row.getResultByIndex(2);
+          let icon = row.getResultByIndex(6);
+
+          let item =
+            document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+                                     "menuitem");
+          item.setAttribute("label", title || uri);
+          item.setAttribute("targetURI", uri);
+          item.setAttribute("class", "menuitem-iconic menuitem-with-favicon bookmark-item " +
+                                     extraCSSClass);
+          item.addEventListener("click", onItemClick);
+          if (icon) {
+            let iconURL = "moz-anno:favicon:" + icon;
+            item.setAttribute("image", iconURL);
+          }
+          fragment.appendChild(item);
+        }
+        aHeaderItem.parentNode.insertBefore(fragment, aHeaderItem.nextSibling);
+      },
+      handleError: function (aError) {
+        Cu.reportError("Error while attempting to show recent bookmarks: " + aError);
+      },
+      handleCompletion: function (aReason) {
+      },
+    });
+  },
+
   /**
    * Handles star styling based on page proxy state changes.
    */
   onPageProxyStateChanged: function BUI_onPageProxyStateChanged(aState) {
     if (!this._shouldUpdateStarState() || !this.star) {
       return;
     }
 
@@ -1543,18 +1601,23 @@ var BookmarkingUI = {
     let isStarred = !forceReset && this._itemIds.length > 0;
     let label = isStarred ? "editlabel" : "bookmarklabel";
     if (this.broadcaster) {
       this.broadcaster.setAttribute("label", this.broadcaster.getAttribute(label));
     }
   },
 
   onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) {
+    // Don't handle events for submenus.
+    if (event.target != event.currentTarget)
+      return;
+
     this._updateBookmarkPageMenuItem();
     PlacesCommandHook.updateBookmarkAllTabsCommand();
+    this._updateRecentBookmarks(document.getElementById("menu_recentBookmarks"));
   },
 
   _showBookmarkedNotification: function BUI_showBookmarkedNotification() {
     function getCenteringTransformForRects(rectToPosition, referenceRect) {
       let topDiff = referenceRect.top - rectToPosition.top;
       let leftDiff = referenceRect.left - rectToPosition.left;
       let heightDiff = referenceRect.height - rectToPosition.height;
       let widthDiff = referenceRect.width - rectToPosition.width;
new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-refreshblocker.js
@@ -0,0 +1,84 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
+
+/**
+ * If the user has opted into blocking refresh and redirect attempts by
+ * default, this handles showing the notification to the user which
+ * gives them the option to let the refresh or redirect proceed.
+ */
+var RefreshBlocker = {
+  init() {
+    gBrowser.addEventListener("RefreshBlocked", this);
+  },
+
+  uninit() {
+    gBrowser.removeEventListener("RefreshBlocked", this);
+  },
+
+  handleEvent: function(event) {
+    if (event.type == "RefreshBlocked") {
+      this.block(event.originalTarget, event.detail);
+    }
+  },
+
+  /**
+   * Shows the blocked refresh / redirect notification for some browser.
+   *
+   * @param browser (<xul:browser>)
+   *        The browser that had the refresh blocked. This will be the browser
+   *        for which we'll show the notification on.
+   * @param data (object)
+   *        An object with the following properties:
+   *
+   *        URI (string)
+   *          The URI that a page is attempting to refresh or redirect to.
+   *
+   *        delay (int)
+   *          The delay (in milliseconds) before the page was going to reload
+   *          or redirect.
+   *
+   *        sameURI (bool)
+   *          true if we're refreshing the page. false if we're redirecting.
+   *
+   *        outerWindowID (int)
+   *          The outerWindowID of the frame that requested the refresh or
+   *          redirect.
+   */
+  block(browser, data) {
+    let brandBundle = document.getElementById("bundle_brand");
+    let brandShortName = brandBundle.getString("brandShortName");
+    let message =
+      gNavigatorBundle.getFormattedString(data.sameURI ? "refreshBlocked.refreshLabel"
+                                                       : "refreshBlocked.redirectLabel",
+                                          [brandShortName]);
+
+    let notificationBox = gBrowser.getNotificationBox(browser);
+    let notification = notificationBox.getNotificationWithValue("refresh-blocked");
+
+    if (notification) {
+      notification.label = message;
+    } else {
+      let refreshButtonText =
+        gNavigatorBundle.getString("refreshBlocked.goButton");
+      let refreshButtonAccesskey =
+        gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
+
+      let buttons = [{
+        label: refreshButtonText,
+        accessKey: refreshButtonAccesskey,
+        callback: function (notification, button) {
+          if (browser.messageManager) {
+            browser.messageManager.sendAsyncMessage("RefreshBlocker:Refresh", data);
+          }
+        }
+      }];
+
+      notificationBox.appendNotification(message, "refresh-blocked",
+                                         "chrome://browser/skin/Info.png",
+                                         notificationBox.PRIORITY_INFO_MEDIUM,
+                                         buttons);
+    }
+  }
+};
--- a/browser/base/content/browser-safebrowsing.js
+++ b/browser/base/content/browser-safebrowsing.js
@@ -3,34 +3,42 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Note: this file is not shipped (through jar.mn)
 // if MOZ_SAFE_BROWSING is not defined.
 
 var gSafeBrowsing = {
 
   setReportPhishingMenu: function() {
-    // A phishing page will have a specific about:blocked content documentURI
-    var uri = gBrowser.currentURI;
-    var isPhishingPage = uri && uri.spec.startsWith("about:blocked?e=phishingBlocked");
+    // In order to detect whether or not we're at the phishing warning
+    // page, we have to check the documentURI instead of the currentURI.
+    // This is because when the DocShell loads an error page, the
+    // currentURI stays at the original target, while the documentURI
+    // will point to the internal error page we loaded instead.
+    var docURI = gBrowser.selectedBrowser.documentURI;
+    var isPhishingPage =
+      docURI && docURI.spec.startsWith("about:blocked?e=phishingBlocked");
 
     // Show/hide the appropriate menu item.
     document.getElementById("menu_HelpPopup_reportPhishingtoolmenu")
             .hidden = isPhishingPage;
     document.getElementById("menu_HelpPopup_reportPhishingErrortoolmenu")
             .hidden = !isPhishingPage;
 
     var broadcasterId = isPhishingPage
                         ? "reportPhishingErrorBroadcaster"
                         : "reportPhishingBroadcaster";
 
     var broadcaster = document.getElementById(broadcasterId);
     if (!broadcaster)
       return;
 
+    // Now look at the currentURI to learn which page we were trying
+    // to browse to.
+    let uri = gBrowser.currentURI;
     if (uri && (uri.schemeIs("http") || uri.schemeIs("https")))
       broadcaster.removeAttribute("disabled");
     else
       broadcaster.setAttribute("disabled", true);
   },
 
   /**
    * Used to report a phishing page or a false positive
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -758,17 +758,17 @@ SocialShare = {
   }
 };
 
 SocialSidebar = {
   _openStartTime: 0,
 
   // Whether the sidebar can be shown for this window.
   get canShow() {
-    if (!SocialUI.enabled || document.mozFullScreen)
+    if (!SocialUI.enabled || document.fullscreenElement)
       return false;
     return Social.providers.some(p => p.sidebarURL);
   },
 
   // Whether the user has toggled the sidebar on (for windows where it can appear)
   get opened() {
     let broadcaster = document.getElementById("socialSidebarBroadcaster");
     return !broadcaster.hidden;
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -82,16 +82,18 @@ var gSyncUI = {
     broadcaster.setAttribute("label", this._stringBundle.GetStringFromName("syncnow.label"));
 
     // Initialize the Synced Tabs Sidebar
     if (Services.prefs.getBoolPref("services.sync.syncedTabsUIRefresh")) {
       let sidebarBroadcaster = document.getElementById("viewTabsSidebar");
       sidebarBroadcaster.removeAttribute("hidden");
     }
 
+    this.maybeMoveSyncedTabsButton();
+
     this.updateUI();
   },
 
 
   // Returns a promise that resolves with true if Sync needs to be configured,
   // false otherwise.
   _needsSetup() {
     // If Sync is configured for FxAccounts then we do that promise-dance.
@@ -327,16 +329,38 @@ var gSyncUI = {
         PanelUI.showSubView("PanelUI-remotetabs", anchor, area);
       }).catch(Cu.reportError);
     } else {
       // It is placed somewhere else - just try and show it.
       PanelUI.showSubView("PanelUI-remotetabs", anchor, area);
     }
   },
 
+  /* After Sync is initialized we perform a once-only check for the sync
+     button being in "customize purgatory" and if so, move it to the panel.
+     This is done primarily for profiles created before SyncedTabs landed,
+     where the button defaulted to being in that purgatory.
+     We use a preference to ensure we only do it once, so people can still
+     customize it away and have it stick.
+  */
+  maybeMoveSyncedTabsButton() {
+    const prefName = "browser.migrated-sync-button";
+    let migrated = false;
+    try {
+      migrated = Services.prefs.getBoolPref(prefName);
+    } catch (_) {}
+    if (migrated) {
+      return;
+    }
+    if (!CustomizableUI.getPlacementOfWidget("sync-button")) {
+      CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
+    }
+    Services.prefs.setBoolPref(prefName, true);
+  },
+
   /* Update the tooltip for the sync-status broadcaster (which will update the
      Sync Toolbar button and the Sync spinner in the FxA hamburger area.)
      If Sync is configured, the tooltip is when the last sync occurred,
      otherwise the tooltip reflects the fact that Sync needs to be
      (re-)configured.
   */
   _updateSyncButtonsTooltip: Task.async(function* () {
     if (!gBrowser)
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -359,19 +359,19 @@ toolbarpaletteitem > #personal-bookmarks
 toolbar:not(#TabsToolbar) > #wrapper-personal-bookmarks,
 toolbar:not(#TabsToolbar) > #personal-bookmarks {
   -moz-box-flex: 1;
 }
 
 /* Ensure that empty parts of the bookmarks container can be dragged on OSX, and on other OSes
  * only when a lwtheme is in use. */
 %ifdef XP_MACOSX
-#PlacesToolbarItems {
+#main-window[tabsintitlebar]:not([customizing]) #personal-bookmarks {
 %else
-#main-window[tabsintitlebar] #PlacesToolbarItems:-moz-lwtheme {
+#main-window[tabsintitlebar]:not([customizing]) #personal-bookmarks:-moz-lwtheme {
 %endif
   -moz-window-dragging: drag;
 }
 
 #zoom-controls[cui-areatype="toolbar"]:not([overflowedItem=true]) > #zoom-reset-button > .toolbarbutton-text {
   display: -moz-box;
 }
 
@@ -1015,47 +1015,25 @@ toolbar[mode="text"] > #downloads-button
 }
 
 toolbar[mode="text"] > #downloads-button > .toolbarbutton-icon {
   display: -moz-box;
   -moz-box-ordinal-group: 2;
   visibility: collapse;
 }
 
-/* full screen chat window support */
-chatbar:-moz-full-screen-ancestor,
-chatbox:-moz-full-screen-ancestor  {
-  border: none;
-  position: fixed !important;
-  top: 0 !important;
-  left: 0 !important;
-  right: 0 !important;
-  bottom: 0 !important;
-  width: 100% !important;
-  height: 100% !important;
-  margin: 0 !important;
-  min-width: 0 !important;
-  max-width: none !important;
-  min-height: 0 !important;
-  max-height: none !important;
-  box-sizing: border-box !important;
-}
-
 /* hide chat chrome when chat is fullscreen */
-chatbox:-moz-full-screen-ancestor > .chat-titlebar {
+#chat-window[sizemode="fullscreen"] chatbox > .chat-titlebar {
   display: none;
 }
 
-/* hide chatbar if browser tab is fullscreen */
-*:-moz-full-screen-ancestor chatbar:not(:-moz-full-screen-ancestor) {
-  display: none;
-}
-
-/* hide sidebar when fullscreen */
-*:-moz-full-screen-ancestor #social-sidebar-box:not(:-moz-full-screen-ancestor) {
+/* hide chatbar and sidebar if browser tab is fullscreen */
+#main-window[inFullscreen][inDOMFullscreen] chatbar,
+#main-window[inFullscreen][inDOMFullscreen] #social-sidebar-box,
+#main-window[inFullscreen][inDOMFullscreen] #social-sidebar-splitter {
   display: none;
 }
 
 /* Combobox dropdown renderer */
 #ContentSelectDropdown > menupopup {
   max-height: 350px;
   /* The menupopup itself should always be rendered LTR to ensure the scrollbar aligns with
    * the dropdown arrow on the dropdown widget. If a menuitem is RTL, its style will be set accordingly */
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -56,16 +56,18 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
                                   "resource://gre/modules/LightweightThemeManager.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "gAboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
   return Services.strings.createBundle('chrome://browser/locale/browser.properties');
 });
+XPCOMUtils.defineLazyModuleGetter(this, "AddonWatcher",
+                                  "resource://gre/modules/AddonWatcher.jsm");
 
 const nsIWebNavigation = Ci.nsIWebNavigation;
 
 var gLastBrowserCharset = null;
 var gLastValidURLStr = "";
 var gInPrintPreviewMode = false;
 var gContextMenu = null; // nsContextMenu instance
 var gMultiProcessBrowser =
@@ -76,16 +78,17 @@ var gMultiProcessBrowser =
 var gAppInfo = Cc["@mozilla.org/xre/app-info;1"]
                   .getService(Ci.nsIXULAppInfo)
                   .QueryInterface(Ci.nsIXULRuntime);
 
 if (AppConstants.platform != "macosx") {
   var gEditUIVisible = true;
 }
 
+/*globals gBrowser, gNavToolbox, gURLBar, gNavigatorBundle*/
 [
   ["gBrowser",            "content"],
   ["gNavToolbox",         "navigator-toolbox"],
   ["gURLBar",             "urlbar"],
   ["gNavigatorBundle",    "bundle_browser"]
 ].forEach(function (elementGlobal) {
   var [name, id] = elementGlobal;
   window.__defineGetter__(name, function () {
@@ -150,19 +153,19 @@ XPCOMUtils.defineLazyGetter(this, "Popup
                                       document.getElementById("notification-popup-box"));
   } catch (ex) {
     Cu.reportError(ex);
     return null;
   }
 });
 
 XPCOMUtils.defineLazyGetter(this, "DeveloperToolbar", function() {
-  let tmp = {};
-  Cu.import("resource://devtools/client/shared/DeveloperToolbar.jsm", tmp);
-  return new tmp.DeveloperToolbar(window, document.getElementById("developer-toolbar"));
+  let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+  let { DeveloperToolbar } = require("devtools/client/shared/developer-toolbar");
+  return new DeveloperToolbar(window, document.getElementById("developer-toolbar"));
 });
 
 XPCOMUtils.defineLazyGetter(this, "BrowserToolboxProcess", function() {
   let tmp = {};
   Cu.import("resource://devtools/client/framework/ToolboxProcess.jsm", tmp);
   return tmp.BrowserToolboxProcess;
 });
 
@@ -918,16 +921,17 @@ var gBrowserInit = {
     DOMLinkHandler.init();
     gPageStyleMenu.init();
     LanguageDetectionListener.init();
     BrowserOnClick.init();
     FeedHandler.init();
     DevEdition.init();
     AboutPrivateBrowsingListener.init();
     TrackingProtection.init();
+    RefreshBlocker.init();
 
     let mm = window.getGroupMessageManager("browsers");
     mm.loadFrameScript("chrome://browser/content/tab-content.js", true);
     mm.loadFrameScript("chrome://browser/content/content.js", true);
     mm.loadFrameScript("chrome://browser/content/content-UITour.js", true);
     mm.loadFrameScript("chrome://global/content/manifestMessages.js", true);
 
     window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad);
@@ -1096,16 +1100,22 @@ var gBrowserInit = {
           gBrowser.loadTabs(specs, false, true);
         } catch (e) {}
       }
       else if (uriToLoad instanceof XULElement) {
         // swap the given tab with the default about:blank tab and then close
         // the original tab in the other window.
         let tabToOpen = uriToLoad;
 
+        // If this tab was passed as a window argument, clear the
+        // reference to it from the arguments array.
+        if (window.arguments[0] == tabToOpen) {
+          window.arguments[0] = null;
+        }
+
         // Stop the about:blank load
         gBrowser.stop();
         // make sure it has a docshell
         gBrowser.docShell;
 
         // If the browser that we're swapping in was remote, then we'd better
         // be able to support remote browsers, and then make our selectedTab
         // remote.
@@ -1336,16 +1346,19 @@ var gBrowserInit = {
         return;
       }
 
       // Enable the Restore Last Session command if needed
       RestoreLastSessionObserver.init();
 
       SocialUI.init();
 
+      // Start monitoring slow add-ons
+      AddonWatcher.init();
+
       // Telemetry for master-password - we do this after 5 seconds as it
       // can cause IO if NSS/PSM has not already initialized.
       setTimeout(() => {
         if (window.closed) {
           return;
         }
         let secmodDB = Cc["@mozilla.org/security/pkcs11moduledb;1"]
                        .getService(Ci.nsIPKCS11ModuleDB);
@@ -1441,16 +1454,18 @@ var gBrowserInit = {
     BrowserOnClick.uninit();
 
     FeedHandler.uninit();
 
     DevEdition.uninit();
 
     TrackingProtection.uninit();
 
+    RefreshBlocker.uninit();
+
     gMenuButtonUpdateBadge.uninit();
 
     gMenuButtonBadgeManager.uninit();
 
     SidebarUI.uninit();
 
     // Now either cancel delayedStartup, or clean up the services initialized from
     // it.
@@ -3966,17 +3981,17 @@ function updateEditUIVisibility()
  * living on the tab's docShell.
  *
  * @param event
  *        A click event on a userContext File Menu option
  */
 function openNewUserContextTab(event)
 {
   openUILinkIn(BROWSER_NEW_TAB_URL, "tab", {
-    userContextId: event.target.getAttribute('usercontextid'),
+    userContextId: parseInt(event.target.getAttribute('usercontextid')),
   });
 }
 
 /**
  * Updates File Menu User Context UI visibility depending on
  * privacy.userContext.enabled pref state.
  */
 function updateUserContextUIVisibility()
@@ -4691,64 +4706,16 @@ var TabsProgressListener = {
     // longer exists)
     if (!Object.getOwnPropertyDescriptor(window, "PopupNotifications").get)
       PopupNotifications.locationChange(aBrowser);
 
     gBrowser.getNotificationBox(aBrowser).removeTransientNotifications();
 
     FullZoom.onLocationChange(aLocationURI, false, aBrowser);
   },
-
-  onRefreshAttempted: function (aBrowser, aWebProgress, aURI, aDelay, aSameURI) {
-    if (gPrefService.getBoolPref("accessibility.blockautorefresh")) {
-      let brandBundle = document.getElementById("bundle_brand");
-      let brandShortName = brandBundle.getString("brandShortName");
-      let refreshButtonText =
-        gNavigatorBundle.getString("refreshBlocked.goButton");
-      let refreshButtonAccesskey =
-        gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
-      let message =
-        gNavigatorBundle.getFormattedString(aSameURI ? "refreshBlocked.refreshLabel"
-                                                     : "refreshBlocked.redirectLabel",
-                                            [brandShortName]);
-      let docShell = aWebProgress.DOMWindow
-                                 .QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIWebNavigation)
-                                 .QueryInterface(Ci.nsIDocShell);
-      let notificationBox = gBrowser.getNotificationBox(aBrowser);
-      let notification = notificationBox.getNotificationWithValue("refresh-blocked");
-      if (notification) {
-        notification.label = message;
-        notification.refreshURI = aURI;
-        notification.delay = aDelay;
-        notification.docShell = docShell;
-      } else {
-        let buttons = [{
-          label: refreshButtonText,
-          accessKey: refreshButtonAccesskey,
-          callback: function (aNotification, aButton) {
-            var refreshURI = aNotification.docShell
-                                          .QueryInterface(Ci.nsIRefreshURI);
-            refreshURI.forceRefreshURI(aNotification.refreshURI,
-                                       aNotification.delay, true);
-          }
-        }];
-        notification =
-          notificationBox.appendNotification(message, "refresh-blocked",
-                                             "chrome://browser/skin/Info.png",
-                                             notificationBox.PRIORITY_INFO_MEDIUM,
-                                             buttons);
-        notification.refreshURI = aURI;
-        notification.delay = aDelay;
-        notification.docShell = docShell;
-      }
-      return false;
-    }
-    return true;
-  }
 }
 
 function nsBrowserAccess() { }
 
 nsBrowserAccess.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
 
   _openURIInNewTab: function(aURI, aReferrer, aReferrerPolicy, aIsPrivate,
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -807,16 +807,21 @@
             </menuitem>
             <!-- NB: temporary solution for bug 985024, this should go away soon. -->
             <menuitem id="BMB_bookmarksShowAllTop"
                       class="menuitem-iconic subviewbutton"
                       label="&showAllBookmarks2.label;"
                       command="Browser:ShowAllBookmarks"
                       key="manBookmarkKb"/>
             <menuseparator/>
+            <menuitem label="&recentBookmarks.label;"
+                      id="BMB_recentBookmarks"
+                      disabled="true"
+                      class="subviewbutton"/>
+            <menuseparator/>
             <menu id="BMB_bookmarksToolbar"
                   class="menu-iconic bookmark-item subviewbutton"
                   label="&personalbarCmd.label;"
                   container="true">
               <menupopup id="BMB_bookmarksToolbarPopup"
                          placespopup="true"
                          context="placesContext"
                          onpopupshowing="if (!this.parentNode._placesView)
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -714,18 +714,18 @@ addMessageListener("ContextMenu:MediaCom
     case "hidestats":
     case "showstats":
       let event = media.ownerDocument.createEvent("CustomEvent");
       event.initCustomEvent("media-showStatistics", false, true,
                             message.data.command == "showstats");
       media.dispatchEvent(event);
       break;
     case "fullscreen":
-      if (content.document.mozFullScreenEnabled)
-        media.mozRequestFullScreen();
+      if (content.document.fullscreenEnabled)
+        media.requestFullscreen();
       break;
   }
 });
 
 addMessageListener("ContextMenu:Canvas:ToDataURL", (message) => {
   let dataURL = message.objects.target.toDataURL();
   sendAsyncMessage("ContextMenu:Canvas:ToDataURL:Result", { dataURL });
 });
--- a/browser/base/content/contentSearchUI.js
+++ b/browser/base/content/contentSearchUI.js
@@ -272,19 +272,23 @@ ContentSearchUIController.prototype = {
         altKey: aEvent.altKey,
         button: aEvent.button,
       },
     };
 
     if (this.suggestionAtIndex(this.selectedIndex)) {
       eventData.selection = {
         index: this.selectedIndex,
-        kind: aEvent instanceof MouseEvent ? "mouse" :
-              aEvent instanceof KeyboardEvent ? "key" : undefined,
+        kind: undefined,
       };
+      if (aEvent instanceof MouseEvent) {
+        eventData.selection.kind = "mouse";
+      } else if (aEvent instanceof KeyboardEvent) {
+        eventData.selection.kind = "key";
+      }
     }
 
     this._sendMsg("Search", eventData);
     this.addInputValueToFormHistory();
   },
 
   _onInput: function () {
     if (!this.input.value) {
--- a/browser/base/content/global-scripts.inc
+++ b/browser/base/content/global-scripts.inc
@@ -19,16 +19,17 @@
 <script type="application/javascript" src="chrome://browser/content/browser-devedition.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-eme.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-feeds.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-fullScreen.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-fullZoom.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-gestureSupport.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-plugins.js"/>
+<script type="application/javascript" src="chrome://browser/content/browser-refreshblocker.js"/>
 #ifdef MOZ_SAFE_BROWSING
 <script type="application/javascript" src="chrome://browser/content/browser-safebrowsing.js"/>
 #endif
 <script type="application/javascript" src="chrome://browser/content/browser-sidebar.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-social.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-syncui.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-tabsintitlebar.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-thumbnails.js"/>
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -26,16 +26,17 @@ nsContextMenu.prototype = {
   initMenu: function CM_initMenu(aXulMenu, aIsShift) {
     // Get contextual info.
     this.setTarget(document.popupNode, document.popupRangeParent,
                    document.popupRangeOffset);
     if (!this.shouldDisplay)
       return;
 
     this.hasPageMenu = false;
+    this.isContentSelected = !this.selectionInfo.docSelectionIsCollapsed;
     if (!aIsShift) {
       if (this.isRemote) {
         this.hasPageMenu =
           PageMenuParent.addToPopup(gContextMenuContentData.customMenuItems,
                                     this.browser, aXulMenu);
       }
       else {
         this.hasPageMenu = PageMenuParent.buildAndAddToPopup(this.target, aXulMenu);
@@ -66,16 +67,18 @@ nsContextMenu.prototype = {
 
     this.isFrameImage = document.getElementById("isFrameImage");
     this.ellipsis = "\u2026";
     try {
       this.ellipsis = gPrefService.getComplexValue("intl.ellipsis",
                                                    Ci.nsIPrefLocalizedString).data;
     } catch (e) { }
 
+    // Reset after "on-build-contextmenu" notification in case selection was
+    // changed during the notification.
     this.isContentSelected = !this.selectionInfo.docSelectionIsCollapsed;
     this.onPlainTextLink = false;
 
     let bookmarkPage = document.getElementById("context-bookmarkpage");
     if (bookmarkPage)
       BookmarkingUI.onCurrentPageContextPopupShowing();
 
     // Initialize (disable/remove) menu items.
@@ -136,19 +139,21 @@ nsContextMenu.prototype = {
       } catch (ex) {}
 
       this.linkTextStr = this.selectionInfo.linkText;
       this.onPlainTextLink = true;
     }
 
     var shouldShow = this.onSaveableLink || isMailtoInternal || this.onPlainTextLink;
     var isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
+    var showContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
     this.showItem("context-openlink", shouldShow && !isWindowPrivate);
     this.showItem("context-openlinkprivate", shouldShow);
     this.showItem("context-openlinkintab", shouldShow);
+    this.showItem("context-openlinkinusercontext-menu", shouldShow && showContainers);
     this.showItem("context-openlinkincurrent", this.onPlainTextLink);
     this.showItem("context-sep-open", shouldShow);
   },
 
   initNavigationItems: function CM_initNavigationItems() {
     var shouldShow = !(this.isContentSelected || this.onLink || this.onImage ||
                        this.onCanvas || this.onVideo || this.onAudio ||
                        this.onTextInput || this.onSocial);
@@ -166,17 +171,17 @@ nsContextMenu.prototype = {
     this.showItem("context-stop", stopReloadItem == "stop");
 
     // XXX: Stop is determined in browser.js; the canStop broadcaster is broken
     //this.setItemAttrFromNode( "context-stop", "disabled", "canStop" );
   },
 
   initLeaveDOMFullScreenItems: function CM_initLeaveFullScreenItem() {
     // only show the option if the user is in DOM fullscreen
-    var shouldShow = (this.target.ownerDocument.mozFullScreenElement != null);
+    var shouldShow = (this.target.ownerDocument.fullscreenElement != null);
     this.showItem("context-leave-dom-fullscreen", shouldShow);
 
     // Explicitly show if in DOM fullscreen, but do not hide it has already been shown
     if (shouldShow)
         this.showItem("context-media-sep-commands", true);
   },
 
   initSaveItems: function CM_initSaveItems() {
@@ -462,17 +467,17 @@ nsContextMenu.prototype = {
     // Several mutually exclusive items... play/pause, mute/unmute, show/hide
     this.showItem("context-media-play",  onMedia && (this.target.paused || this.target.ended));
     this.showItem("context-media-pause", onMedia && !this.target.paused && !this.target.ended);
     this.showItem("context-media-mute",   onMedia && !this.target.muted);
     this.showItem("context-media-unmute", onMedia && this.target.muted);
     this.showItem("context-media-playbackrate", onMedia);
     this.showItem("context-media-showcontrols", onMedia && !this.target.controls);
     this.showItem("context-media-hidecontrols", onMedia && this.target.controls);
-    this.showItem("context-video-fullscreen", this.onVideo && this.target.ownerDocument.mozFullScreenElement == null);
+    this.showItem("context-video-fullscreen", this.onVideo && this.target.ownerDocument.fullscreenElement == null);
     var statsShowing = this.onVideo && this.target.mozMediaStatisticsShowing;
     this.showItem("context-video-showstats", this.onVideo && this.target.controls && !statsShowing);
     this.showItem("context-video-hidestats", this.onVideo && this.target.controls && statsShowing);
     this.showItem("context-media-eme-learnmore", this.onDRMMedia);
     this.showItem("context-media-eme-separator", this.onDRMMedia);
 
     // Disable them when there isn't a valid media source loaded.
     if (onMedia) {
@@ -952,17 +957,17 @@ nsContextMenu.prototype = {
   // Open linked-to URL in a new private window.
   openLinkInPrivateWindow : function () {
     urlSecurityCheck(this.linkURL, this.principal);
     openLinkIn(this.linkURL, "window",
                this._openLinkInParameters({ private: true }));
   },
 
   // Open linked-to URL in a new tab.
-  openLinkInTab: function() {
+  openLinkInTab: function(event) {
     urlSecurityCheck(this.linkURL, this.principal);
     let referrerURI = gContextMenuContentData.documentURIObject;
 
     // if the mixedContentChannel is present and the referring URI passes
     // a same origin check with the target URI, we can preserve the users
     // decision of disabling MCB on a page for it's child tabs.
     let persistAllowMixedContentInChildTab = false;
 
@@ -973,16 +978,17 @@ nsContextMenu.prototype = {
         sm.checkSameOriginURI(referrerURI, targetURI, false);
         persistAllowMixedContentInChildTab = true;
       }
       catch (e) { }
     }
 
     let params = this._openLinkInParameters({
       allowMixedContent: persistAllowMixedContentInChildTab,
+      userContextId: parseInt(event.target.getAttribute('usercontextid')),
     });
     openLinkIn(this.linkURL, "tab", params);
   },
 
   // open URL in current tab
   openLinkInCurrent: function() {
     urlSecurityCheck(this.linkURL, this.principal);
     openLinkIn(this.linkURL, "current", this._openLinkInParameters());
@@ -1154,17 +1160,17 @@ nsContextMenu.prototype = {
       saveImageURL(dataURL, name, "SaveImageTitle", true, false,
                    document.documentURIObject, null, null, null,
                    isPrivate);
     };
     mm.addMessageListener("ContextMenu:SaveVideoFrameAsImage:Result", onMessage);
   },
 
   leaveDOMFullScreen: function() {
-    document.mozCancelFullScreen();
+    document.exitFullscreen();
   },
 
   // Change current window to the URL of the background image.
   viewBGImage: function(e) {
     urlSecurityCheck(this.bgImageURL,
                      this.browser.contentPrincipal,
                      Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
     openUILink(this.bgImageURL, e, { disallowInheritPrincipal: true,
--- a/browser/base/content/pageinfo/permissions.js
+++ b/browser/base/content/pageinfo/permissions.js
@@ -295,17 +295,17 @@ function initPluginsRow() {
         permissionMap.set(permString, name);
       }
     }
   }
 
   let entries = Array.from(permissionMap, item => ({ name: item[1], permission: item[0] }));
 
   entries.sort(function(a, b) {
-    return a.name < b.name ? -1 : (a.name == b.name ? 0 : 1);
+    return a.name.localeCompare(b.name);
   });
 
   let permissionEntries = entries.map(p => fillInPluginPermissionTemplate(p.name, p.permission));
 
   let permPluginsRow = document.getElementById("perm-plugins-row");
   clearPluginPermissionTemplate();
   if (permissionEntries.length < 1) {
     permPluginsRow.hidden = true;
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -689,30 +689,31 @@ Sanitizer.showUI = function(aParentWindo
 Sanitizer.sanitize = function(aParentWindow)
 {
   Sanitizer.showUI(aParentWindow);
 };
 
 Sanitizer.onStartup = Task.async(function*() {
   // Make sure that we are triggered during shutdown, at the right time,
   // and only once.
-  let placesClient = Cc["@mozilla.org/browser/nav-history-service;1"]     .getService(Ci.nsPIPlacesDatabase)
-     .shutdownClient
-     .jsclient;
+  let placesClient = Cc["@mozilla.org/browser/nav-history-service;1"]
+                       .getService(Ci.nsPIPlacesDatabase)
+                       .shutdownClient
+                       .jsclient;
 
   let deferredSanitization = PromiseUtils.defer();
   let sanitizationInProgress = false;
   let doSanitize = function() {
-    if (sanitizationInProgress) {
-      return deferredSanitization.promise;
+    if (!sanitizationInProgress) {
+      sanitizationInProgress = true;
+      Sanitizer.onShutdown().catch(er => {Promise.reject(er) /* Do not return rejected promise */;}).then(() =>
+        deferredSanitization.resolve()
+      );
     }
-    sanitizationInProgress = true;
-    Sanitizer.onShutdown().catch(er => {Promise.reject(er) /* Do not return rejected promise */;}).then(() =>
-      deferredSanitization.resolve()
-    );
+    return deferredSanitization.promise;
   }
   placesClient.addBlocker("sanitize.js: Sanitize on shutdown", doSanitize);
 
   // Handle incomplete sanitizations
   if (Preferences.has(Sanitizer.PREF_SANITIZE_IN_PROGRESS)) {
     // Firefox crashed during sanitization.
     let s = new Sanitizer();
     let json = Preferences.get(Sanitizer.PREF_SANITIZE_IN_PROGRESS);
--- a/browser/base/content/social-content.js
+++ b/browser/base/content/social-content.js
@@ -6,32 +6,37 @@
 /* This content script should work in any browser or iframe and should not
  * depend on the frame being contained in tabbrowser. */
 
 var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
+// Tie `content` to this frame scripts' global scope explicitly. If we don't, then
+// `content` might be out of eval's scope and GC'ed before this script is done.
+// See bug 1229195 for empirical proof.
+var gContent = content;
+
 // social frames are always treated as app tabs
 docShell.isAppTab = true;
 
 var gDOMContentLoaded = false;
 addEventListener("DOMContentLoaded", function() {
   gDOMContentLoaded = true;
   sendAsyncMessage("DOMContentLoaded");
 });
 var gDOMTitleChangedByUs = false;
 addEventListener("DOMTitleChanged", function(e) {
   if (!gDOMTitleChangedByUs) {
     sendAsyncMessage("DOMTitleChanged", {
       title: e.target.title
     });
-    gDOMTitleChangedByUs = false;
   }
+  gDOMTitleChangedByUs = false;
 });
 
 // Error handling class used to listen for network errors in the social frames
 // and replace them with a social-specific error page
 SocialErrorListener = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
                                          Ci.nsIWebProgressListener,
                                          Ci.nsISupportsWeakReference,
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -202,16 +202,26 @@
           aTarget.setAttribute("label", this.content.contentTitle);
 
           aTarget.remote = this.remote;
           aTarget.src = this.src;
           let content = aTarget.content;
           content.setAttribute("origin", this.content.getAttribute("origin"));
           content.popupnotificationanchor.className = this.content.popupnotificationanchor.className;
           content.swapDocShells(this.content);
+
+          // When a chat window is attached or detached, the docShell hosting
+          // the chat document is swapped to the newly created chat window.
+          // (Be it inside a popup or back inside a chatbox element attached to
+          // the chatbar.)
+          // Since a swapDocShells call does not swap the messageManager instances
+          // attached to a browser, we'll need to add the message listeners to
+          // the new messageManager. This is not a bug in swapDocShells, merely
+          // a design decision.
+          content.messageManager.addMessageListener("DOMTitleChanged", content);
         ]]></body>
       </method>
 
       <method name="setDecorationAttributes">
         <parameter name="aTarget"/>
         <body><![CDATA[
           if (this.hasAttribute("customSize"))
             aTarget.setAttribute("customSize", this.getAttribute("customSize"));
--- a/browser/base/content/sync/utils.js
+++ b/browser/base/content/sync/utils.js
@@ -1,13 +1,13 @@
 /* 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/. */
 
-// Equivalent to 0600 permissions; used for saved Sync Recovery Key.
+// Equivalent to 0o600 permissions; used for saved Sync Recovery Key.
 // This constant can be replaced when the equivalent values are available to
 // chrome JS; see Bug 433295 and Bug 757351.
 const PERMISSIONS_RWUSR = 0x180;
 
 // Weave should always exist before before this file gets included.
 var gSyncUtils = {
   get bundle() {
     delete this.bundle;
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -627,17 +627,17 @@ var DOMFullscreenHandler = {
     return content.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils);
   },
 
   receiveMessage: function(aMessage) {
     switch(aMessage.name) {
       case "DOMFullscreen:Entered": {
         if (!this._windowUtils.handleFullscreenRequests() &&
-            !content.document.mozFullScreen) {
+            !content.document.fullscreenElement) {
           // If we don't actually have any pending fullscreen request
           // to handle, neither we have been in fullscreen, tell the
           // parent to just exit.
           sendAsyncMessage("DOMFullscreen:Exit");
         }
         break;
       }
       case "DOMFullscreen:CleanUp": {
@@ -665,17 +665,17 @@ var DOMFullscreenHandler = {
       }
       case "MozDOMFullscreen:Exit": {
         sendAsyncMessage("DOMFullscreen:Exit");
         break;
       }
       case "MozDOMFullscreen:Entered":
       case "MozDOMFullscreen:Exited": {
         addEventListener("MozAfterPaint", this);
-        if (!content || !content.document.mozFullScreen) {
+        if (!content || !content.document.fullscreenElement) {
           // If we receive any fullscreen change event, and find we are
           // actually not in fullscreen, also ask the parent to exit to
           // ensure that the parent always exits fullscreen when we do.
           sendAsyncMessage("DOMFullscreen:Exit");
         }
         break;
       }
       case "MozAfterPaint": {
@@ -683,12 +683,81 @@ var DOMFullscreenHandler = {
         sendAsyncMessage("DOMFullscreen:Painted");
         break;
       }
     }
   }
 };
 DOMFullscreenHandler.init();
 
+var RefreshBlocker = {
+  init() {
+    this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
+                     .createInstance(Ci.nsIWebProgress);
+    this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_REFRESH);
+
+    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                              .getInterface(Ci.nsIWebProgress);
+    webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_REFRESH);
+
+    addMessageListener("RefreshBlocker:Refresh", this);
+  },
+
+  uninit() {
+    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                              .getInterface(Ci.nsIWebProgress);
+    webProgress.removeProgressListener(this._filter);
+
+    this._filter.removeProgressListener(this);
+    this._filter = null;
+
+    removeMessageListener("RefreshBlocker:Refresh", this);
+  },
+
+  onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
+    if (Services.prefs.getBoolPref("accessibility.blockautorefresh")) {
+      let win = aWebProgress.DOMWindow;
+      let outerWindowID = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIDOMWindowUtils)
+                             .outerWindowID;
+
+      sendAsyncMessage("RefreshBlocker:Blocked", {
+        URI: aURI.spec,
+        originCharset: aURI.originCharset,
+        delay: aDelay,
+        sameURI: aSameURI,
+        outerWindowID,
+      });
+
+      return false;
+    }
+
+    return true;
+  },
+
+  receiveMessage(message) {
+    let data = message.data;
+
+    if (message.name == "RefreshBlocker:Refresh") {
+      let win = Services.wm.getOuterWindowWithId(data.outerWindowID);
+      let refreshURI = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIDocShell)
+                          .QueryInterface(Ci.nsIRefreshURI);
+
+      let URI = BrowserUtils.makeURI(data.URI, data.originCharset, null);
+
+      refreshURI.forceRefreshURI(URI, data.delay, true);
+    }
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener2,
+                                         Ci.nsIWebProgressListener,
+                                         Ci.nsISupportsWeakReference,
+                                         Ci.nsISupports]),
+};
+
+RefreshBlocker.init();
+
 ExtensionContent.init(this);
 addEventListener("unload", () => {
   ExtensionContent.uninit(this);
+  RefreshBlocker.uninit();
 });
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -742,35 +742,35 @@
 
             onLocationChange: function (aWebProgress, aRequest, aLocation,
                                         aFlags) {
               // OnLocationChange is called for both the top-level content
               // and the subframes.
               let topLevel = aWebProgress.isTopLevel;
 
               if (topLevel) {
+                let isSameDocument =
+                  !!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT);
                 // If userTypedClear > 0, the document loaded correctly and we should be
                 // clearing the user typed value. We also need to clear the typed value
                 // if the document failed to load, to make sure the urlbar reflects the
                 // failed URI (particularly for SSL errors). However, don't clear the value
                 // if the error page's URI is about:blank, because that causes complete
                 // loss of urlbar contents for invalid URI errors (see bug 867957).
                 // Another reason to clear the userTypedValue is if this was an anchor
                 // navigation.
                 if (this.mBrowser.userTypedClear > 0 ||
                     ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
                      aLocation.spec != "about:blank") ||
-                     aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
+                     isSameDocument) {
                   this.mBrowser.userTypedValue = null;
                 }
 
                 // If the browser was playing audio, we should remove the playing state.
-                if (this.mTab.hasAttribute("soundplaying") &&
-                    (!this.mBrowser.lastURI ||
-                     this.mBrowser.lastURI.spec != aLocation.spec)) {
+                if (this.mTab.hasAttribute("soundplaying") && !isSameDocument) {
                   this.mTab.removeAttribute("soundplaying");
                   this.mTabBrowser._tabAttrModified(this.mTab, ["soundplaying"]);
                 }
 
                 // If the browser was previously muted, we should restore the muted state.
                 if (this.mTab.hasAttribute("muted")) {
                   this.mTab.linkedBrowser.mute();
                 }
@@ -783,19 +783,19 @@
                     findBar.close();
                   }
 
                   // fix bug 253793 - turn off highlight when page changes
                   findBar.getElement("highlight").checked = false;
                 }
 
                 // Don't clear the favicon if this onLocationChange was
-                // triggered by a pushState or a replaceState.  See bug 550565.
-                if (aWebProgress.isLoadingDocument &&
-                    !(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE)) {
+                // triggered by a pushState or a replaceState (bug 550565) or
+                // a hash change (bug 408415).
+                if (aWebProgress.isLoadingDocument && !isSameDocument) {
                   this.mBrowser.mIconURL = null;
                 }
 
                 let autocomplete = this.mTabBrowser._placesAutocomplete;
                 let unifiedComplete = this.mTabBrowser._unifiedComplete;
                 if (this.mBrowser.registeredOpenURI) {
                   autocomplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
                   unifiedComplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
@@ -1101,16 +1101,18 @@
             if (listener && listener.mStateFlags) {
               this._callProgressListeners(null, "onUpdateCurrentBrowser",
                                           [listener.mStateFlags, listener.mStatus,
                                            listener.mMessage, listener.mTotalProgress],
                                           true, false);
             }
 
             if (!this._previewMode) {
+              this._recordTabAccess(this.mCurrentTab);
+
               this.mCurrentTab.lastAccessed = Infinity;
               this.mCurrentTab.removeAttribute("unread");
               oldTab.lastAccessed = Date.now();
 
               let oldFindBar = oldTab._findBar;
               if (oldFindBar &&
                   oldFindBar.findMode == oldFindBar.FIND_NORMAL &&
                   !oldFindBar.hidden)
@@ -1293,16 +1295,55 @@
                newFocusedElement.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple"))
             focusFlags |= fm.FLAG_SHOWRING;
         }
 
         fm.setFocus(newBrowser, focusFlags);
         ]]></body>
       </method>
 
+      <!--
+        This function assumes we have an LRU cache of tabs (either
+        images of tab content or their layers). The goal is to find
+        out how far into the cache we need to look in order to find
+        aTab. We record this number in telemetry and also move aTab to
+        the front of the cache.
+
+        A newly created tab has position Infinity in the cache.
+        If a tab is closed, it has no effect on the position of other
+        tabs in the cache since we assume that closing a tab doesn't
+        cause us to load in any other tabs.
+
+        We ignore the effect of dragging tabs between windows.
+      -->
+      <method name="_recordTabAccess">
+	<parameter name="aTab"/>
+        <body><![CDATA[
+          if (!Services.telemetry.canRecordExtended) {
+            return;
+          }
+
+          let tabs = Array.from(this.visibleTabs);
+
+          let pos = aTab.cachePosition;
+          for (let i = 0; i < tabs.length; i++) {
+            // If aTab is moving to the front, everything that was
+            // previously in front of it is bumped up one position.
+            if (tabs[i].cachePosition < pos) {
+              tabs[i].cachePosition++;
+            }
+          }
+          aTab.cachePosition = 0;
+
+          if (isFinite(pos)) {
+            Services.telemetry.getHistogramById("TAB_SWITCH_CACHE_POSITION").add(pos);
+          }
+        ]]></body>
+      </method>
+
       <method name="_tabAttrModified">
         <parameter name="aTab"/>
         <parameter name="aChanged"/>
         <body><![CDATA[
           if (aTab.closing)
             return;
 
           let event = new CustomEvent("TabAttrModified", {
@@ -1680,18 +1721,19 @@
             let b = document.createElementNS(NS_XUL, "browser");
             b.permanentKey = {};
             b.setAttribute("type", "content-targetable");
             b.setAttribute("message", "true");
             b.setAttribute("messagemanagergroup", "browsers");
             b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
             b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
 
-            if (userContextId)
+            if (userContextId) {
               b.setAttribute("usercontextid", userContextId);
+            }
 
             if (remote)
               b.setAttribute("remote", "true");
 
             if (window.gShowPageResizers && window.windowState == window.STATE_NORMAL) {
               b.setAttribute("showresizer", "true");
             }
 
@@ -1753,16 +1795,17 @@
             var aReferrerPolicy;
             var aFromExternal;
             var aRelatedToCurrent;
             var aSkipAnimation;
             var aAllowMixedContent;
             var aForceNotRemote;
             var aNoReferrer;
             var aUserContextId;
+            var aEventDetail;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aReferrerPolicy       = params.referrerPolicy;
               aCharset              = params.charset;
               aPostData             = params.postData;
@@ -1770,26 +1813,34 @@
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aFromExternal         = params.fromExternal;
               aRelatedToCurrent     = params.relatedToCurrent;
               aSkipAnimation        = params.skipAnimation;
               aAllowMixedContent    = params.allowMixedContent;
               aForceNotRemote       = params.forceNotRemote;
               aNoReferrer           = params.noReferrer;
               aUserContextId        = params.userContextId;
+              aEventDetail          = params.eventDetail;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
             var uriIsAboutBlank = !aURI || aURI == "about:blank";
 
+            if (!aURI || isBlankPageURL(aURI)) {
+              t.setAttribute("label", this.mStringBundle.getString("tabs.emptyTabTitle"));
+            } else if (aURI.toLowerCase().startsWith("javascript:")) {
+              // This can go away when bug 672618 or bug 55696 are fixed.
+              t.setAttribute("label", aURI);
+            }
+
             if (aUserContextId)
               t.setAttribute("usercontextid", aUserContextId);
             t.setAttribute("crop", "end");
             t.setAttribute("onerror", "this.removeAttribute('image');");
             t.className = "tabbrowser-tab";
 
             // The new browser should be remote if this is an e10s window and
             // the uri to load can be loaded remotely.
@@ -1807,18 +1858,17 @@
                           Services.prefs.getBoolPref("browser.tabs.animate");
             if (!animate) {
               t.setAttribute("fadein", "true");
               setTimeout(function (tabContainer) {
                 tabContainer._handleNewTab(t);
               }, 0, this.tabContainer);
             }
 
-            // invalidate caches
-            this._browsers = null;
+            // invalidate cache
             this._visibleTabs = null;
 
             this.tabContainer.appendChild(t);
 
             // If this new tab is owned by another, assert that relationship
             if (aOwner)
               t.owner = aOwner;
 
@@ -1858,25 +1908,16 @@
               // NB: this appendChild call causes us to run constructors for the
               // browser element, which fires off a bunch of notifications. Some
               // of those notifications can cause code to run that inspects our
               // state, so it is important that the tab element is fully
               // initialized by this point.
               this.mPanelContainer.appendChild(notificationbox);
             }
 
-            // We've waited until the tab is in the DOM to set the label. This
-            // allows the TabLabelModified event to be properly dispatched.
-            if (!aURI || isBlankPageURL(aURI)) {
-              t.label = this.mStringBundle.getString("tabs.emptyTabTitle");
-            } else if (aURI.toLowerCase().startsWith("javascript:")) {
-              // This can go away when bug 672618 or bug 55696 are fixed.
-              t.label = aURI;
-            }
-
             this.tabContainer.updateVisibility();
 
             // wire up a progress listener for the new browser object.
             var tabListener = this.mTabProgressListener(t, b, uriIsAboutBlank, usingPreloadedContent);
             const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
                                      .createInstance(Components.interfaces.nsIWebProgress);
             filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
             b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
@@ -1888,18 +1929,18 @@
             // Swap in a preloaded customize tab, if available.
             if (aURI == "about:customizing") {
               usingPreloadedContent = gCustomizationTabPreloader.newTab(t);
             }
 
             // Dispatch a new tab notification.  We do this once we're
             // entirely done, so that things are in a consistent state
             // even if the event listener opens or closes tabs.
-            var evt = document.createEvent("Events");
-            evt.initEvent("TabOpen", true, false);
+            var detail = aEventDetail || {};
+            var evt = new CustomEvent("TabOpen", { bubbles: true, detail });
             t.dispatchEvent(evt);
 
             // If we didn't swap docShells with a preloaded browser
             // then let's just continue loading the page normally.
             if (!usingPreloadedContent && !uriIsAboutBlank) {
               // pretend the user typed this so it'll be available till
               // the document successfully loads
               if (aURI && gInitialPages.indexOf(aURI) == -1)
@@ -2123,17 +2164,17 @@
             if (!animate &&
                 aTab.closing) {
               this._endRemoveTab(aTab);
               return;
             }
 
             var isLastTab = (this.tabs.length - this._removingTabs.length == 1);
 
-            if (!this._beginRemoveTab(aTab, false, null, true, skipPermitUnload))
+            if (!this._beginRemoveTab(aTab, null, null, true, skipPermitUnload))
               return;
 
             if (!aTab.pinned && !aTab.hidden && aTab._fullyOpen && byMouse)
               this.tabContainer._lockTabSizing(aTab);
             else
               this.tabContainer._unlockTabSizing();
 
             if (!animate /* the caller didn't opt in */ ||
@@ -2168,17 +2209,17 @@
       <!-- Tab close requests are ignored if the window is closing anyway,
            e.g. when holding Ctrl+W. -->
       <field name="_windowIsClosing">
         false
       </field>
 
       <method name="_beginRemoveTab">
         <parameter name="aTab"/>
-        <parameter name="aTabWillBeMoved"/>
+        <parameter name="aAdoptedByTab"/>
         <parameter name="aCloseWindowWithLastTab"/>
         <parameter name="aCloseWindowFastpath"/>
         <parameter name="aSkipPermitUnload"/>
         <body>
           <![CDATA[
             if (aTab.closing ||
                 this._windowIsClosing)
               return false;
@@ -2202,17 +2243,17 @@
                 // cancels the operation.  We are finished here in both cases.
                 this._windowIsClosing = window.closeWindow(true, window.warnAboutClosingWindow);
                 return null;
               }
 
               newTab = true;
             }
 
-            if (!aTab._pendingPermitUnload && !aTabWillBeMoved && !aSkipPermitUnload) {
+            if (!aTab._pendingPermitUnload && !aAdoptedByTab && !aSkipPermitUnload) {
               // We need to block while calling permitUnload() because it
               // processes the event queue and may lead to another removeTab()
               // call before permitUnload() returns.
               aTab._pendingPermitUnload = true;
               let {permitUnload} = browser.permitUnload();
               delete aTab._pendingPermitUnload;
               // If we were closed during onbeforeunload, we return false now
               // so we don't (try to) close the same tab again. Of course, we
@@ -2236,37 +2277,36 @@
               this.addTab(BROWSER_NEW_TAB_URL, {skipAnimation: true});
             else
               this.tabContainer.updateVisibility();
 
             // We're committed to closing the tab now.
             // Dispatch a notification.
             // We dispatch it before any teardown so that event listeners can
             // inspect the tab that's about to close.
-            var evt = document.createEvent("UIEvent");
-            evt.initUIEvent("TabClose", true, false, window, aTabWillBeMoved ? 1 : 0);
+            var evt = new CustomEvent("TabClose", { bubbles: true, detail: { adoptedBy: aAdoptedByTab } });
             aTab.dispatchEvent(evt);
 
-            if (!aTabWillBeMoved && !gMultiProcessBrowser) {
+            if (!aAdoptedByTab && !gMultiProcessBrowser) {
               // Prevent this tab from showing further dialogs, since we're closing it
               var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
                                 getInterface(Ci.nsIDOMWindowUtils);
               windowUtils.disableDialogs();
             }
 
             // Remove the tab's filter and progress listener.
             const filter = this._tabFilters.get(aTab);
 
             browser.webProgress.removeProgressListener(filter);
 
             const listener = this._tabListeners.get(aTab);
             filter.removeProgressListener(listener);
             listener.destroy();
 
-            if (browser.registeredOpenURI && !aTabWillBeMoved) {
+            if (browser.registeredOpenURI && !aAdoptedByTab) {
               this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI);
               this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI);
               delete browser.registeredOpenURI;
             }
 
             // We are no longer the primary content area.
             browser.setAttribute("type", "content-targetable");
 
@@ -2333,20 +2373,16 @@
             // destroyed until the document goes away.  So we force a
             // cleanup ourselves.
             // This has to happen before we remove the child so that the
             // XBL implementation of nsIObserver still works.
             browser.destroy();
 
             var wasPinned = aTab.pinned;
 
-            // Invalidate browsers cache, as the tab is removed from the
-            // tab container.
-            this._browsers = null;
-
             // Remove the tab ...
             this.tabContainer.removeChild(aTab);
 
             // ... and fix up the _tPos properties immediately.
             for (let i = aTab._tPos; i < this.tabs.length; i++)
               this.tabs[i]._tPos = i;
 
             if (!this._windowIsClosing) {
@@ -2490,17 +2526,17 @@
 
             // That's gBrowser for the other window, not the tab's browser!
             var remoteBrowser = aOtherTab.ownerDocument.defaultView.gBrowser;
             var isPending = aOtherTab.hasAttribute("pending");
 
             // First, start teardown of the other browser.  Make sure to not
             // fire the beforeunload event in the process.  Close the other
             // window if this was its last tab.
-            if (!remoteBrowser._beginRemoveTab(aOtherTab, true, true))
+            if (!remoteBrowser._beginRemoveTab(aOtherTab, aOurTab, true))
               return;
 
             let modifiedAttrs = [];
             if (aOtherTab.hasAttribute("muted")) {
               aOurTab.setAttribute("muted", "true");
               aOurTab.muteReason = aOtherTab.muteReason;
               ourBrowser.mute();
               modifiedAttrs.push("muted");
@@ -2814,25 +2850,42 @@
           ]]>
         </setter>
       </property>
 
       <property name="selectedBrowser"
                 onget="return this.mCurrentBrowser;"
                 readonly="true"/>
 
-      <property name="browsers" readonly="true">
-       <getter>
-          <![CDATA[
-            return this._browsers ||
-                   (this._browsers = Array.map(this.tabs, tab => tab.linkedBrowser));
-          ]]>
-        </getter>
-      </property>
-      <field name="_browsers">null</field>
+      <field name="browsers" readonly="true">
+        <![CDATA[
+          // This defines a proxy which allows us to access browsers by
+          // index without actually creating a full array of browsers.
+          new Proxy([], {
+            has: (target, name) => {
+              if (typeof name == "string" && Number.isInteger(parseInt(name))) {
+                return (name in this.tabs);
+              }
+              return false;
+            },
+            get: (target, name) => {
+              if (name == "length") {
+                return this.tabs.length;
+              }
+              if (typeof name == "string" && Number.isInteger(parseInt(name))) {
+                if (!(name in this.tabs)) {
+                  return undefined;
+                }
+                return this.tabs[name].linkedBrowser;
+              }
+              return target[name];
+            }
+          });
+        ]]>
+      </field>
 
       <!-- Moves a tab to a new browser window, unless it's already the only tab
            in the current window, in which case this will do nothing. -->
       <method name="replaceTabWithWindow">
         <parameter name="aTab"/>
         <parameter name="aOptions"/>
         <body>
           <![CDATA[
@@ -2883,18 +2936,17 @@
           this._lastRelatedTab = null;
 
           let wasFocused = (document.activeElement == this.mCurrentTab);
 
           aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
           this.mCurrentTab._logicallySelected = false;
           this.mCurrentTab._visuallySelected = false;
 
-          // invalidate caches
-          this._browsers = null;
+          // invalidate cache
           this._visibleTabs = null;
 
           // use .item() instead of [] because dragging to the end of the strip goes out of
           // bounds: .item() returns null (so it acts like appendChild), but [] throws
           this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex));
 
           for (let i = 0; i < this.tabs.length; i++) {
             this.tabs[i]._tPos = i;
@@ -2931,16 +2983,72 @@
             if (nextTab)
               this.moveTabTo(this.mCurrentTab, nextTab._tPos);
             else if (this.arrowKeysShouldWrap)
               this.moveTabToStart();
           ]]>
         </body>
       </method>
 
+      <!-- Adopts a tab from another browser window, and inserts it at aIndex -->
+      <method name="adoptTab">
+        <parameter name="aTab"/>
+        <parameter name="aIndex"/>
+        <parameter name="aSelectTab"/>
+        <body>
+        <![CDATA[
+          // Swap the dropped tab with a new one we create and then close
+          // it in the other window (making it seem to have moved between
+          // windows).
+          let newTab = this.addTab("about:blank", { eventDetail: { adoptedTab: aTab } });
+          let newBrowser = this.getBrowserForTab(newTab);
+          let newURL = aTab.linkedBrowser.currentURI.spec;
+
+          // If we're an e10s browser window, an exception will be thrown
+          // if we attempt to drag a non-remote browser in, so we need to
+          // ensure that the remoteness of the newly created browser is
+          // appropriate for the URL of the tab being dragged in.
+          this.updateBrowserRemotenessByURL(newBrowser, newURL);
+
+          // Stop the about:blank load.
+          newBrowser.stop();
+          // Make sure it has a docshell.
+          newBrowser.docShell;
+
+          let numPinned = this._numPinnedTabs;
+          if (aIndex < numPinned || (aTab.pinned && aIndex == numPinned)) {
+            this.pinTab(newTab);
+          }
+
+          this.moveTabTo(newTab, aIndex);
+
+          // We need to select the tab before calling swapBrowsersAndCloseOther
+          // so that window.content in chrome windows points to the right tab
+          // when pagehide/show events are fired. This is no longer necessary
+          // for any exiting browser code, but it may be necessary for add-on
+          // compatibility.
+          if (aSelectTab) {
+            this.selectedTab = newTab;
+          }
+
+          aTab.parentNode._finishAnimateTabMove();
+          this.swapBrowsersAndCloseOther(newTab, aTab);
+
+          if (aSelectTab) {
+            // Call updateCurrentBrowser to make sure the URL bar is up to date
+            // for our new tab after we've done swapBrowsersAndCloseOther.
+            this.updateCurrentBrowser(true);
+          }
+
+          return newTab;
+        ]]>
+        </body>
+      </method>
+
+
       <method name="moveTabBackward">
         <body>
           <![CDATA[
             let previousTab = this.mCurrentTab.previousSibling;
             while (previousTab && previousTab.hidden)
               previousTab = previousTab.previousSibling;
 
             if (previousTab)
@@ -4025,17 +4133,17 @@
               break;
           }
         ]]></body>
       </method>
 
       <method name="receiveMessage">
         <parameter name="aMessage"/>
         <body><![CDATA[
-          let json = aMessage.json;
+          let data = aMessage.data;
           let browser = aMessage.target;
 
           switch (aMessage.name) {
             case "DOMTitleChanged": {
               let tab = this.getTabForBrowser(browser);
               if (!tab || tab.hasAttribute("pending"))
                 return undefined;
               let titleChanged = this.setTabTitle(tab);
@@ -4057,42 +4165,42 @@
               if (tab) {
                 // Skip running PermitUnload since it already happened in
                 // the content process.
                 this.removeTab(tab, {skipPermitUnload: true});
               }
               break;
             }
             case "contextmenu": {
-              let spellInfo = aMessage.data.spellInfo;
+              let spellInfo = data.spellInfo;
               if (spellInfo)
                 spellInfo.target = aMessage.target.messageManager;
-              let documentURIObject = makeURI(aMessage.data.docLocation,
-                                              aMessage.data.charSet,
-                                              makeURI(aMessage.data.baseURI));
+              let documentURIObject = makeURI(data.docLocation,
+                                              data.charSet,
+                                              makeURI(data.baseURI));
               gContextMenuContentData = { isRemote: true,
                                           event: aMessage.objects.event,
                                           popupNode: aMessage.objects.popupNode,
                                           browser: browser,
-                                          editFlags: aMessage.data.editFlags,
+                                          editFlags: data.editFlags,
                                           spellInfo: spellInfo,
-                                          principal: aMessage.data.principal,
-                                          customMenuItems: aMessage.data.customMenuItems,
-                                          addonInfo: aMessage.data.addonInfo,
+                                          principal: data.principal,
+                                          customMenuItems: data.customMenuItems,
+                                          addonInfo: data.addonInfo,
                                           documentURIObject: documentURIObject,
-                                          docLocation: aMessage.data.docLocation,
-                                          charSet: aMessage.data.charSet,
-                                          referrer: aMessage.data.referrer,
-                                          referrerPolicy: aMessage.data.referrerPolicy,
-                                          contentType: aMessage.data.contentType,
-                                          contentDisposition: aMessage.data.contentDisposition,
-                                          frameOuterWindowID: aMessage.data.frameOuterWindowID,
-                                          selectionInfo: aMessage.data.selectionInfo,
-                                          disableSetDesktopBackground: aMessage.data.disableSetDesktopBg,
-                                          loginFillInfo: aMessage.data.loginFillInfo,
+                                          docLocation: data.docLocation,
+                                          charSet: data.charSet,
+                                          referrer: data.referrer,
+                                          referrerPolicy: data.referrerPolicy,
+                                          contentType: data.contentType,
+                                          contentDisposition: data.contentDisposition,
+                                          frameOuterWindowID: data.frameOuterWindowID,
+                                          selectionInfo: data.selectionInfo,
+                                          disableSetDesktopBackground: data.disableSetDesktopBg,
+                                          loginFillInfo: data.loginFillInfo,
                                         };
               let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
               let event = gContextMenuContentData.event;
               popup.openPopupAtScreen(event.screenX, event.screenY, true);
               break;
             }
             case "DOMServiceWorkerFocusClient":
             case "DOMWebNotificationClicked": {
@@ -4113,33 +4221,44 @@
               break;
             }
             case "Findbar:Keypress": {
               let tab = this.getTabForBrowser(browser);
               // If the find bar for this tab is not yet alive, only initialize
               // it if there's a possibility FindAsYouType will be used.
               // There's no point in doing it for most random keypresses.
               if (!this.isFindBarInitialized(tab) &&
-                aMessage.data.shouldFastFind) {
+                data.shouldFastFind) {
                 let shouldFastFind = this._findAsYouType;
                 if (!shouldFastFind) {
                   // Please keep in sync with toolkit/content/widgets/findbar.xml
                   const FAYT_LINKS_KEY = "'";
                   const FAYT_TEXT_KEY = "/";
-                  let charCode = aMessage.data.fakeEvent.charCode;
+                  let charCode = data.fakeEvent.charCode;
                   let key = charCode ? String.fromCharCode(charCode) : null;
                   shouldFastFind = key == FAYT_LINKS_KEY || key == FAYT_TEXT_KEY;
                 }
                 if (shouldFastFind) {
                   // Make sure we return the result.
                   return this.getFindBar(tab).receiveMessage(aMessage);
                 }
               }
               break;
             }
+            case "RefreshBlocker:Blocked": {
+              let event = new CustomEvent("RefreshBlocked", {
+                bubbles: true,
+                cancelable: false,
+                detail: data,
+              });
+
+              browser.dispatchEvent(event);
+
+              break;
+            }
 
           }
         ]]></body>
       </method>
 
       <method name="observe">
         <parameter name="aSubject"/>
         <parameter name="aTopic"/>
@@ -4194,16 +4313,17 @@
           window.addEventListener("sizemodechange", this, false);
 
           var uniqueId = this._generateUniquePanelID();
           this.mPanelContainer.childNodes[0].id = uniqueId;
           this.mCurrentTab.linkedPanel = uniqueId;
           this.mCurrentTab._tPos = 0;
           this.mCurrentTab._fullyOpen = true;
           this.mCurrentTab.lastAccessed = Infinity;
+          this.mCurrentTab.cachePosition = 0;
           this.mCurrentTab.linkedBrowser = this.mCurrentBrowser;
           this._tabForBrowser.set(this.mCurrentBrowser, this.mCurrentTab);
 
           // set up the shared autoscroll popup
           this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
           this._autoScrollPopup.id = "autoscroller";
           this.appendChild(this._autoScrollPopup);
           this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
@@ -4239,16 +4359,17 @@
             // which does asynchronous tab switching.
             this.mPanelContainer.classList.add("tabbrowser-tabpanels");
           } else {
             this._outerWindowIDBrowserMap.set(this.mCurrentBrowser.outerWindowID,
                                               this.mCurrentBrowser);
           }
           messageManager.addMessageListener("DOMWebNotificationClicked", this);
           messageManager.addMessageListener("DOMServiceWorkerFocusClient", this);
+          messageManager.addMessageListener("RefreshBlocker:Blocked", this);
 
           // To correctly handle keypresses for potential FindAsYouType, while
           // the tab's find bar is not yet initialized.
           this._findAsYouType = Services.prefs.getBoolPref("accessibility.typeaheadfind");
           Services.prefs.addObserver("accessibility.typeaheadfind", this, false);
           messageManager.addMessageListener("Findbar:Keypress", this);
         ]]>
       </constructor>
@@ -5774,52 +5895,18 @@
           // actually move the dragged tab
           if ("animDropIndex" in draggedTab._dragData) {
             let newIndex = draggedTab._dragData.animDropIndex;
             if (newIndex > draggedTab._tPos)
               newIndex--;
             this.tabbrowser.moveTabTo(draggedTab, newIndex);
           }
         } else if (draggedTab) {
-          // swap the dropped tab with a new one we create and then close
-          // it in the other window (making it seem to have moved between
-          // windows)
           let newIndex = this._getDropIndex(event, false);
-          let newTab = this.tabbrowser.addTab("about:blank");
-          let newBrowser = this.tabbrowser.getBrowserForTab(newTab);
-          let draggedBrowserURL = draggedTab.linkedBrowser.currentURI.spec;
-
-          // If we're an e10s browser window, an exception will be thrown
-          // if we attempt to drag a non-remote browser in, so we need to
-          // ensure that the remoteness of the newly created browser is
-          // appropriate for the URL of the tab being dragged in.
-          this.tabbrowser.updateBrowserRemotenessByURL(newBrowser,
-                                                       draggedBrowserURL);
-
-          // Stop the about:blank load
-          newBrowser.stop();
-          // make sure it has a docshell
-          newBrowser.docShell;
-
-          let numPinned = this.tabbrowser._numPinnedTabs;
-          if (newIndex < numPinned || draggedTab.pinned && newIndex == numPinned)
-            this.tabbrowser.pinTab(newTab);
-          this.tabbrowser.moveTabTo(newTab, newIndex);
-
-          // We need to select the tab before calling swapBrowsersAndCloseOther
-          // so that window.content in chrome windows points to the right tab
-          // when pagehide/show events are fired.
-          this.tabbrowser.selectedTab = newTab;
-
-          draggedTab.parentNode._finishAnimateTabMove();
-          this.tabbrowser.swapBrowsersAndCloseOther(newTab, draggedTab);
-
-          // Call updateCurrentBrowser to make sure the URL bar is up to date
-          // for our new tab after we've done swapBrowsersAndCloseOther.
-          this.tabbrowser.updateCurrentBrowser(true);
+          this.tabbrowser.adoptTab(draggedTab, newIndex, true);
         } else {
           // Pass true to disallow dropping javascript: or data: urls
           let url;
           try {
             url = browserDragAndDrop.drop(event, { }, true);
           } catch (ex) {}
 
           if (!url)
@@ -5991,18 +6078,17 @@
                      class="tab-icon-image"
                      validate="never"
                      role="presentation"/>
           <xul:image xbl:inherits="crashed,busy,soundplaying,pinned,muted"
                      anonid="overlay-icon"
                      class="tab-icon-overlay"
                      role="presentation"/>
           <xul:label flex="1"
-                     anonid="tab-label"
-                     xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected,visuallyselected,attention"
+                     xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected,visuallyselected,attention"
                      class="tab-text tab-label"
                      role="presentation"/>
           <xul:image xbl:inherits="soundplaying,pinned,muted,visuallyselected"
                      anonid="soundplaying-icon"
                      class="tab-icon-sound"
                      role="presentation"/>
           <xul:toolbarbutton anonid="close-button"
                              xbl:inherits="fadein,pinned,selected,visuallyselected"
@@ -6058,42 +6144,16 @@
             this._visuallySelected = val;
           }
 
           return val;
         ]]>
         </setter>
       </property>
 
-      <property name="label">
-        <getter>
-          return this.getAttribute("label");
-        </getter>
-        <setter>
-          this.setAttribute("label", val);
-          let event = new CustomEvent("TabLabelModified", {
-            bubbles: true,
-            cancelable: true
-          });
-          this.dispatchEvent(event);
-
-          // Let listeners prevent synchronizing the actual label to the
-          // visible label (allowing them to override the visible label).
-          if (!event.defaultPrevented)
-            this.visibleLabel = val;
-        </setter>
-      </property>
-      <property name="visibleLabel">
-        <getter>
-          return this.getAttribute("visibleLabel");
-        </getter>
-        <setter>
-          this.setAttribute("visibleLabel", val);
-        </setter>
-      </property>
       <property name="pinned" readonly="true">
         <getter>
           return this.getAttribute("pinned") == "true";
         </getter>
       </property>
       <property name="hidden" readonly="true">
         <getter>
           return this.getAttribute("hidden") == "true";
@@ -6123,16 +6183,18 @@
         <getter>
           return this._lastAccessed == Infinity ? Date.now() : this._lastAccessed;
         </getter>
         <setter>
           this._lastAccessed = val;
         </setter>
       </property>
 
+      <field name="cachePosition">Infinity</field>
+
       <field name="mOverCloseButton">false</field>
       <property name="_overPlayingIcon" readonly="true">
         <getter><![CDATA[
           let iconVisible = this.hasAttribute("soundplaying") ||
                             this.hasAttribute("muted");
           let soundPlayingIcon =
             document.getAnonymousElementByAttribute(this, "anonid", "soundplaying-icon");
           let overlayIcon =
@@ -6217,18 +6279,23 @@
         ]]>
         </body>
       </method>
 
       <method name="setUserContextId">
         <parameter name="aUserContextId"/>
         <body>
         <![CDATA[
-          this.linkedBrowser.setAttribute("usercontextid", aUserContextId);
-          this.setAttribute("usercontextid", aUserContextId);
+          if (aUserContextId) {
+            this.linkedBrowser.setAttribute("usercontextid", aUserContextId);
+            this.setAttribute("usercontextid", aUserContextId);
+          } else {
+            this.linkedBrowser.removeAttribute("usercontextid");
+            this.removeAttribute("usercontextid");
+          }
         ]]>
         </body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="mouseover"><![CDATA[
         let anonid = event.originalTarget.getAttribute("anonid");
--- a/browser/base/content/test/chat/browser.ini
+++ b/browser/base/content/test/chat/browser.ini
@@ -3,9 +3,8 @@ skip-if = buildapp == 'mulet' || e10s
 support-files =
   head.js
   chat.html
 
 [browser_chatwindow.js]
 skip-if = true # Bug 1245937 - Intermittent failures/timeouts.
 [browser_focus.js]
 [browser_tearoff.js]
-skip-if = true # Bug 1245805 - tearing off chat windows causes a browser crash.
--- a/browser/base/content/test/chat/browser_tearoff.js
+++ b/browser/base/content/test/chat/browser_tearoff.js
@@ -60,17 +60,17 @@ add_chat_task(function* testTearoffChat(
   Assert.equal(domwindow.document.documentElement.getAttribute("windowtype"), "Social:Chat", "Social:Chat window opened");
   Assert.equal(numChatsInWindow(window), 0, "should be no chats in the chat bar");
 
   // get the chatbox from the new window.
   chatbox = domwindow.document.getElementById("chatter")
   Assert.equal(chatbox.getAttribute("label"), chatTitle, "window should have same title as chat");
 
   yield ContentTask.spawn(chatbox.content, null, function* () {
-    div = content.document.getElementById("testdiv");
+    let div = content.document.getElementById("testdiv");
     is(div.getAttribute("test"), "1", "docshell should have been swapped");
     div.setAttribute("test", "2");
   });
 
   // swap the window back to the chatbar
   promise = promiseOneEvent(domwindow, "unload");
   swap = domwindow.document.getAnonymousElementByAttribute(chatbox, "anonid", "swap");
   swap.click();
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -24,18 +24,18 @@ support-files =
   bug792517.html
   bug792517.sjs
   bug839103.css
   discovery.html
   domplate_test.js
   download_page.html
   dummy_page.html
   feed_tab.html
-  file_bug550565_favicon.ico
-  file_bug550565_popup.html
+  file_generic_favicon.ico
+  file_with_favicon.html
   file_bug822367_1.html
   file_bug822367_1.js
   file_bug822367_2.html
   file_bug822367_3.html
   file_bug822367_4.html
   file_bug822367_4.js
   file_bug822367_4B.html
   file_bug822367_5.html
@@ -124,23 +124,23 @@ skip-if = os == "linux" # Bug 958026
 support-files =
   content_aboutAccounts.js
 [browser_aboutCertError.js]
 [browser_aboutSupport_newtab_security_state.js]
 [browser_aboutHealthReport.js]
 skip-if = os == "linux" # Bug 924307
 [browser_aboutHome.js]
 skip-if = e10s # Bug 1093153 - no about:home support yet
+[browser_aboutHome_wrapsCorrectly.js]
 [browser_action_keyword.js]
 [browser_action_keyword_override.js]
 [browser_action_searchengine.js]
 [browser_action_searchengine_alias.js]
 [browser_addKeywordSearch.js]
 [browser_search_favicon.js]
-skip-if = e10s # Bug 1212647
 [browser_alltabslistener.js]
 [browser_audioTabIcon.js]
 [browser_autocomplete_a11y_label.js]
 skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir (works on its own)
 [browser_autocomplete_cursor.js]
 [browser_autocomplete_edit_completed.js]
 [browser_autocomplete_enter_race.js]
 [browser_autocomplete_no_title.js]
@@ -159,16 +159,17 @@ skip-if = true # browser_bug321000.js is
 [browser_bug329212.js]
 skip-if = e10s # Bug 1236991 - Update or remove tests that use fillInPageTooltip
 [browser_bug331772_xul_tooltiptext_in_html.js]
 skip-if = e10s # Bug 1236991 - Update or remove tests that use fillInPageTooltip
 [browser_bug356571.js]
 [browser_bug380960.js]
 [browser_bug386835.js]
 [browser_bug406216.js]
+[browser_bug408415.js]
 [browser_bug409481.js]
 [browser_bug409624.js]
 [browser_bug413915.js]
 [browser_bug416661.js]
 [browser_bug417483.js]
 [browser_bug419612.js]
 [browser_bug422590.js]
 [browser_bug423833.js]
@@ -283,18 +284,16 @@ tags = mcb
 skip-if = os == 'win' || e10s # Bug 1159268 - Need a content-process safe version of synthesizeWheel
 [browser_bug1064280_changeUrlInPinnedTab.js]
 [browser_bug1070778.js]
 [browser_accesskeys.js]
 [browser_canonizeURL.js]
 skip-if = e10s # Bug 1094510 - test hits the network in e10s mode only
 [browser_clipboard.js]
 [browser_contentAreaClick.js]
-[browser_contextSearchTabPosition.js]
-skip-if = os == "mac" || e10s # bug 967013; e10s: bug 1094761 - test hits the network in e10s, causing next test to crash
 [browser_ctrlTab.js]
 [browser_datachoices_notification.js]
 skip-if = !datareporting
 [browser_devedition.js]
 [browser_devices_get_user_media.js]
 skip-if = buildapp == 'mulet' || (os == "linux" && debug) # linux: bug 976544
 [browser_devices_get_user_media_about_urls.js]
 skip-if = e10s # Bug 1071623
@@ -363,16 +362,17 @@ skip-if = (os == 'linux') || (e10s && de
 [browser_printpreview.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 1101973 - breaks the next test in e10s, and may be responsible for later timeout after logging "Error: Channel closing: too late to send/recv, messages will be lost"
 [browser_private_browsing_window.js]
 skip-if = buildapp == 'mulet'
 [browser_private_no_prompt.js]
 skip-if = buildapp == 'mulet'
 [browser_purgehistory_clears_sh.js]
 [browser_PageMetaData_pushstate.js]
+[browser_refreshBlocker.js]
 [browser_relatedTabs.js]
 [browser_remoteTroubleshoot.js]
 support-files =
   test_remoteTroubleshoot.html
 [browser_remoteWebNavigation_postdata.js]
 [browser_removeTabsToTheEnd.js]
 [browser_removeUnsafeProtocolsFromURLBarPaste.js]
 [browser_restore_isAppTab.js]
@@ -483,17 +483,16 @@ skip-if = os == "linux" # Bug 1073339 - 
 [browser_urlbarSearchTelemetry.js]
 [browser_urlbarStop.js]
 [browser_urlbarTrimURLs.js]
 [browser_urlbar_autoFill_backspaced.js]
 [browser_urlbar_searchsettings.js]
 [browser_utilityOverlay.js]
 [browser_viewSourceInTabOnViewSource.js]
 [browser_visibleFindSelection.js]
-[browser_visibleLabel.js]
 [browser_visibleTabs.js]
 [browser_visibleTabs_bookmarkAllPages.js]
 skip-if = true # Bug 1005420 - fails intermittently. also with e10s enabled: bizarre problem with hidden tab having _mouseenter called, via _setPositionalAttributes, and tab not being found resulting in 'candidate is undefined'
 [browser_visibleTabs_bookmarkAllTabs.js]
 [browser_visibleTabs_contextMenu.js]
 [browser_visibleTabs_tabPreview.js]
 skip-if = (os == "win" && !debug)
 [browser_web_channel.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_aboutHome_wrapsCorrectly.js
@@ -0,0 +1,28 @@
+add_task(function* () {
+  let newWindow = yield BrowserTestUtils.openNewBrowserWindow();
+
+  let resizedPromise = BrowserTestUtils.waitForEvent(newWindow, "resize");
+  newWindow.resizeTo(300, 300);
+  yield resizedPromise;
+
+  yield BrowserTestUtils.openNewForegroundTab(newWindow.gBrowser, "about:home");
+
+  yield ContentTask.spawn(newWindow.gBrowser.selectedBrowser, {}, function* () {
+    is(content.document.body.getAttribute("narrow"), "true", "narrow mode");
+  });
+
+  resizedPromise = BrowserTestUtils.waitForContentEvent(newWindow.gBrowser.selectedBrowser, "resize");
+
+
+  yield ContentTask.spawn(newWindow.gBrowser.selectedBrowser, {}, function* () {
+    content.window.resizeTo(800, 800);
+  });
+
+  yield resizedPromise;
+
+  yield ContentTask.spawn(newWindow.gBrowser.selectedBrowser, {}, function* () {
+    is(content.document.body.hasAttribute("narrow"), false, "non-narrow mode");
+  });
+
+  yield BrowserTestUtils.closeWindow(newWindow);
+});
--- a/browser/base/content/test/general/browser_aboutTabCrashed.js
+++ b/browser/base/content/test/general/browser_aboutTabCrashed.js
@@ -120,73 +120,73 @@ function crashTabTestHelper(fieldValues,
 
 /**
  * Tests what we send with the crash report by default. By default, we do not
  * send any comments, the URL of the crashing page, or the email address of
  * the user.
  */
 add_task(function* test_default() {
   yield crashTabTestHelper({}, {
-    "Comments": "",
+    "Comments": null,
     "URL": "",
-    "Email": "",
+    "Email": null,
   });
 });
 
 /**
  * Test just sending a comment.
  */
 add_task(function* test_just_a_comment() {
   yield crashTabTestHelper({
     comments: COMMENTS,
   }, {
     "Comments": COMMENTS,
     "URL": "",
-    "Email": "",
+    "Email": null,
   });
 });
 
 /**
  * Test that we don't send email if emailMe is unchecked
  */
 add_task(function* test_no_email() {
   yield crashTabTestHelper({
     email: EMAIL,
     emailMe: false,
   }, {
-    "Comments": "",
+    "Comments": null,
     "URL": "",
-    "Email": "",
+    "Email": null,
   });
 });
 
 /**
  * Test that we can send an email address if emailMe is checked
  */
 add_task(function* test_yes_email() {
   yield crashTabTestHelper({
     email: EMAIL,
     emailMe: true,
   }, {
-    "Comments": "",
+    "Comments": null,
     "URL": "",
     "Email": EMAIL,
   });
 });
 
 /**
  * Test that we will send the URL of the page if includeURL is checked.
  */
 add_task(function* test_send_URL() {
   yield crashTabTestHelper({
     includeURL: true,
   }, {
-    "Comments": "",
+    "Comments": null,
     "URL": PAGE,
-    "Email": "",
+    "Email": null,
   });
 });
 
 /**
  * Test that we can send comments, the email address, and the URL
  */
 add_task(function* test_send_all() {
   yield crashTabTestHelper({
@@ -195,8 +195,9 @@ add_task(function* test_send_all() {
     email: EMAIL,
     comments: COMMENTS,
   }, {
     "Comments": COMMENTS,
     "URL": PAGE,
     "Email": EMAIL,
   });
 });
+
copy from browser/base/content/test/general/browser_bug550565.js
copy to browser/base/content/test/general/browser_bug408415.js
--- a/browser/base/content/test/general/browser_bug550565.js
+++ b/browser/base/content/test/general/browser_bug408415.js
@@ -1,21 +1,21 @@
 function test() {
   waitForExplicitFinish();
 
   let testPath = getRootDirectory(gTestPath);
 
-  let tab = gBrowser.addTab(testPath + "file_bug550565_popup.html");
+  let tab = gBrowser.addTab(testPath + "file_with_favicon.html");
 
   tab.linkedBrowser.addEventListener("DOMContentLoaded", function() {
     tab.linkedBrowser.removeEventListener("DOMContentLoaded", arguments.callee, true);
 
-    let expectedIcon = testPath + "file_bug550565_favicon.ico";
+    let expectedIcon = testPath + "file_generic_favicon.ico";
 
-    is(gBrowser.getIcon(tab), expectedIcon, "Correct icon before pushState.");
-    tab.linkedBrowser.contentWindow.history.pushState("page2", "page2", "page2");
-    is(gBrowser.getIcon(tab), expectedIcon, "Correct icon after pushState.");
+    is(gBrowser.getIcon(tab), expectedIcon, "Correct icon before hash change.");
+    tab.linkedBrowser.contentWindow.location.href += "#foo";
+    is(gBrowser.getIcon(tab), expectedIcon, "Correct icon after hash change.");
 
     gBrowser.removeTab(tab);
 
     finish();
   }, true);
 }
--- a/browser/base/content/test/general/browser_bug491431.js
+++ b/browser/base/content/test/general/browser_bug491431.js
@@ -8,24 +8,24 @@ function test() {
   waitForExplicitFinish();
 
   let newWin, tabA, tabB;
 
   // test normal close
   tabA = gBrowser.addTab(testPage);
   gBrowser.tabContainer.addEventListener("TabClose", function(aEvent) {
     gBrowser.tabContainer.removeEventListener("TabClose", arguments.callee, true);
-    ok(!aEvent.detail, "This was a normal tab close");
+    ok(!aEvent.detail.adoptedBy, "This was a normal tab close");
 
     // test tab close by moving
     tabB = gBrowser.addTab(testPage);
     gBrowser.tabContainer.addEventListener("TabClose", function(aEvent) {
       gBrowser.tabContainer.removeEventListener("TabClose", arguments.callee, true);
       executeSoon(function() {
-        ok(aEvent.detail, "This was a tab closed by moving");
+        ok(aEvent.detail.adoptedBy, "This was a tab closed by moving");
 
         // cleanup
         newWin.close();
         executeSoon(finish);
       });
     }, true);
     newWin = gBrowser.replaceTabWithWindow(tabB);
   }, true);
--- a/browser/base/content/test/general/browser_bug550565.js
+++ b/browser/base/content/test/general/browser_bug550565.js
@@ -1,19 +1,19 @@
 function test() {
   waitForExplicitFinish();
 
   let testPath = getRootDirectory(gTestPath);
 
-  let tab = gBrowser.addTab(testPath + "file_bug550565_popup.html");
+  let tab = gBrowser.addTab(testPath + "file_with_favicon.html");
 
   tab.linkedBrowser.addEventListener("DOMContentLoaded", function() {
     tab.linkedBrowser.removeEventListener("DOMContentLoaded", arguments.callee, true);
 
-    let expectedIcon = testPath + "file_bug550565_favicon.ico";
+    let expectedIcon = testPath + "file_generic_favicon.ico";
 
     is(gBrowser.getIcon(tab), expectedIcon, "Correct icon before pushState.");
     tab.linkedBrowser.contentWindow.history.pushState("page2", "page2", "page2");
     is(gBrowser.getIcon(tab), expectedIcon, "Correct icon after pushState.");
 
     gBrowser.removeTab(tab);
 
     finish();
--- a/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js
+++ b/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js
@@ -1,28 +1,28 @@
 "use strict";
 
 var gMessageManager;
 
 function frameScript() {
   addMessageListener("Test:RequestFullscreen", () => {
-    content.document.body.mozRequestFullScreen();
+    content.document.body.requestFullscreen();
   });
   addMessageListener("Test:ExitFullscreen", () => {
-    content.document.mozCancelFullScreen();
+    content.document.exitFullscreen();
   });
   addMessageListener("Test:QueryFullscreenState", () => {
     sendAsyncMessage("Test:FullscreenState", {
-      inDOMFullscreen: content.document.mozFullScreen,
+      inDOMFullscreen: !!content.document.fullscreenElement,
       inFullscreen: content.fullScreen
     });
   });
-  content.document.addEventListener("mozfullscreenchange", () => {
+  content.document.addEventListener("fullscreenchange", () => {
     sendAsyncMessage("Test:FullscreenChanged", {
-      inDOMFullscreen: content.document.mozFullScreen,
+      inDOMFullscreen: !!content.document.fullscreenElement,
       inFullscreen: content.fullScreen
     });
   });
   function waitUntilActive() {
     let doc = content.document;
     if (doc.docShell.isActive && doc.hasFocus()) {
       sendAsyncMessage("Test:Activated");
     } else {
@@ -125,17 +125,17 @@ function checkState(expectedStates, cont
      "The DOM fullscreen state of the content should match");
   // TODO window.fullScreen is not updated as soon as the fullscreen
   //      state flips in child process, hence checking it could cause
   //      anonying intermittent failure. As we just want to confirm the
   //      fullscreen state of the browser window, we can just check the
   //      that on the chrome window below.
   // is(contentStates.inFullscreen, expectedStates.inFullscreen,
   //    "The fullscreen state of the content should match");
-  is(document.mozFullScreen, expectedStates.inDOMFullscreen,
+  is(!!document.fullscreenElement, expectedStates.inDOMFullscreen,
      "The DOM fullscreen state of the chrome should match");
   is(window.fullScreen, expectedStates.inFullscreen,
      "The fullscreen state of the chrome should match");
 }
 
 const kPage = "http://example.org/browser/browser/" +
               "base/content/test/general/dummy_page.html";
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_refreshBlocker.js
@@ -0,0 +1,92 @@
+"use strict";
+
+const PAGE = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
+const PREF = "accessibility.blockautorefresh";
+
+/**
+ * Goes into the content, and simulates a meta-refresh header at a very
+ * low level, and checks to see if it was blocked. This will always cancel
+ * the refresh, regardless of whether or not the refresh was blocked.
+ *
+ * @param browser (<xul:browser>)
+ *        The browser to test for refreshing.
+ * @param expectRefresh (bool)
+ *        Whether or not we expect the refresh attempt to succeed.
+ * @returns Promise
+ */
+function* attemptRefresh(browser, expectRefresh) {
+  yield ContentTask.spawn(browser, expectRefresh, function*(expectRefresh) {
+    let URI = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI;
+    let refresher = docShell.QueryInterface(Ci.nsIRefreshURI);
+    refresher.refreshURI(URI, 0, false, true);
+
+    is(refresher.refreshPending, expectRefresh,
+       "Got the right refreshPending state");
+
+    if (refresher.refreshPending) {
+      // Cancel the pending refresh
+      refresher.cancelRefreshURITimers();
+    }
+  });
+}
+
+/**
+ * Tests that we can enable the blocking pref and block a refresh
+ * from occurring while showing a notification bar. Also tests that
+ * when we disable the pref, that refreshes can go through again.
+ */
+add_task(function* test_can_enable_and_block() {
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: PAGE,
+  }, function*(browser) {
+    // By default, we should be able to reload the page.
+    yield attemptRefresh(browser, true);
+
+    yield pushPrefs(["accessibility.blockautorefresh", true]);
+
+    let notificationPromise =
+      BrowserTestUtils.waitForNotificationBar(gBrowser, browser,
+                                              "refresh-blocked");
+
+    yield attemptRefresh(browser, false);
+
+    yield notificationPromise;
+
+    yield pushPrefs(["accessibility.blockautorefresh", false]);
+
+    // Page reloads should go through again.
+    yield attemptRefresh(browser, true);
+  });
+});
+
+/**
+ * Tests that when a refresh is blocked that we show a notification
+ * bar, and that when we click on the "Allow" button, the refresh
+ * can then go through.
+ */
+add_task(function* test_can_allow_refresh() {
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: PAGE,
+  }, function*(browser) {
+    yield pushPrefs(["accessibility.blockautorefresh", true]);
+
+    let notificationPromise =
+      BrowserTestUtils.waitForNotificationBar(gBrowser, browser,
+                                              "refresh-blocked");
+
+    yield attemptRefresh(browser, false);
+    let notification = yield notificationPromise;
+
+    // Then click the button to submit the crash report.
+    let buttons = notification.querySelectorAll(".notification-button");
+    is(buttons.length, 1, "Should have one button.");
+
+    // Prepare a Promise that should resolve when the refresh goes through
+    let refreshPromise = BrowserTestUtils.browserLoaded(browser);
+    buttons[0].click();
+
+    yield refreshPromise;
+  });
+});
--- a/browser/base/content/test/general/browser_save_link-perwindowpb.js
+++ b/browser/base/content/test/general/browser_save_link-perwindowpb.js
@@ -195,13 +195,13 @@ Cc["@mozilla.org/moz/jssubscript-loader;
 
 function createTemporarySaveDirectory() {
   var saveDir = Cc["@mozilla.org/file/directory_service;1"]
                   .getService(Ci.nsIProperties)
                   .get("TmpD", Ci.nsIFile);
   saveDir.append("testsavedir");
   if (!saveDir.exists()) {
     info("create testsavedir!");
-    saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+    saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
   }
   info("return from createTempSaveDir: " + saveDir.path);
   return saveDir;
 }
--- a/browser/base/content/test/general/browser_save_link_when_window_navigates.js
+++ b/browser/base/content/test/general/browser_save_link_when_window_navigates.js
@@ -15,17 +15,17 @@ Cc["@mozilla.org/moz/jssubscript-loader;
 
 function createTemporarySaveDirectory() {
   var saveDir = Cc["@mozilla.org/file/directory_service;1"]
                   .getService(Ci.nsIProperties)
                   .get("TmpD", Ci.nsIFile);
   saveDir.append("testsavedir");
   if (!saveDir.exists()) {
     info("create testsavedir!");
-    saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+    saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
   }
   info("return from createTempSaveDir: " + saveDir.path);
   return saveDir;
 }
 
 function triggerSave(aWindow, aCallback) {
   info("started triggerSave, persite downloads: " + (Services.prefs.getBoolPref(SAVE_PER_SITE_PREF) ? "on" : "off"));
   var fileName;
--- a/browser/base/content/test/general/browser_save_private_link_perwindowpb.js
+++ b/browser/base/content/test/general/browser_save_private_link_perwindowpb.js
@@ -48,17 +48,17 @@ function test() {
   }
 
   function createTemporarySaveDirectory() {
     var saveDir = Cc["@mozilla.org/file/directory_service;1"]
                     .getService(Ci.nsIProperties)
                     .get("TmpD", Ci.nsIFile);
     saveDir.append("testsavedir");
     if (!saveDir.exists())
-      saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+      saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
     return saveDir;
   }
 
   function doTest(aIsPrivateMode, aWindow, aCallback) {
     function contextMenuOpened(event) {
       cache.clear();
 
       aWindow.document.removeEventListener("popupshown", contextMenuOpened);
--- a/browser/base/content/test/general/browser_save_video.js
+++ b/browser/base/content/test/general/browser_save_video.js
@@ -77,11 +77,11 @@ Cc["@mozilla.org/moz/jssubscript-loader;
                  this);
 
 function createTemporarySaveDirectory() {
   var saveDir = Cc["@mozilla.org/file/directory_service;1"]
                   .getService(Ci.nsIProperties)
                   .get("TmpD", Ci.nsIFile);
   saveDir.append("testsavedir");
   if (!saveDir.exists())
-    saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+    saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
   return saveDir;
 }
--- a/browser/base/content/test/general/browser_save_video_frame.js
+++ b/browser/base/content/test/general/browser_save_video_frame.js
@@ -19,17 +19,17 @@ Cc["@mozilla.org/moz/jssubscript-loader;
  * @return nsIFile
  */
 function createTemporarySaveDirectory() {
   let saveDir = Cc["@mozilla.org/file/directory_service;1"]
                   .getService(Ci.nsIProperties)
                   .get("TmpD", Ci.nsIFile);
   saveDir.append("testsavedir");
   if (!saveDir.exists())
-    saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+    saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
   return saveDir;
 }
 /**
  * MockTransfer exposes a "mockTransferCallback" global which
  * allows us to define a callback to be called once the mock file
  * selector has selected where to save the file.
  */
 function waitForTransferComplete() {
--- a/browser/base/content/test/general/browser_tab_drag_drop_perwindow.js
+++ b/browser/base/content/test/general/browser_tab_drag_drop_perwindow.js
@@ -142,8 +142,38 @@ add_task(function* test_dragging_blackli
      "The browser we just dragged in should not be remote.");
 
   is(draggedBrowser.currentURI.spec, BLACKLISTED_URL,
      `Expected the URL of the dragged in tab to be ${BLACKLISTED_URL}`);
 
   yield BrowserTestUtils.closeWindow(remoteWin1);
   yield BrowserTestUtils.closeWindow(remoteWin2);
 });
+
+
+/**
+ * Tests that tabs dragged between windows dispatch TabOpen and TabClose
+ * events with the appropriate adoption details.
+ */
+add_task(function* test_dragging_adoption_events() {
+  let win1 = yield BrowserTestUtils.openNewBrowserWindow();
+  let win2 = yield BrowserTestUtils.openNewBrowserWindow();
+
+  let tab1 = yield BrowserTestUtils.openNewForegroundTab(win1.gBrowser);
+  let tab2 = yield BrowserTestUtils.openNewForegroundTab(win2.gBrowser);
+
+  let awaitCloseEvent = BrowserTestUtils.waitForEvent(tab1, "TabClose");
+  let awaitOpenEvent = BrowserTestUtils.waitForEvent(win2, "TabOpen");
+
+  let effect = ChromeUtils.synthesizeDrop(tab1, tab2,
+    [[{type: TAB_DROP_TYPE, data: tab1}]],
+    null, win1, win2);
+  is(effect, "move", "Tab should be moved from win1 to win2.");
+
+  let closeEvent = yield awaitCloseEvent;
+  let openEvent = yield awaitOpenEvent;
+
+  is(openEvent.detail.adoptedTab, tab1, "New tab adopted old tab");
+  is(closeEvent.detail.adoptedBy, openEvent.target, "Old tab adopted by new tab");
+
+  yield BrowserTestUtils.closeWindow(win1);
+  yield BrowserTestUtils.closeWindow(win2);
+});
--- a/browser/base/content/test/general/browser_urlHighlight.js
+++ b/browser/base/content/test/general/browser_urlHighlight.js
@@ -61,16 +61,29 @@ function test() {
   testVal("<user:pass@>mozilla.org");
 
   testVal("<https://>mozilla.org</file.ext>");
   testVal("<https://>mozilla.org</sub/file.ext>");
   testVal("<https://>mozilla.org</sub/file.ext?foo>");
   testVal("<https://>mozilla.org</sub/file.ext?foo&bar>");
   testVal("<https://>mozilla.org</sub/file.ext?foo&bar#top>");
   testVal("<https://>mozilla.org</sub/file.ext?foo&bar#top>");
+  testVal("foo.bar<?q=test>");
+  testVal("foo.bar<#mozilla.org>");
+  testVal("foo.bar<?somewhere.mozilla.org>");
+  testVal("foo.bar<?@mozilla.org>");
+  testVal("foo.bar<#x@mozilla.org>");
+  testVal("foo.bar<#@x@mozilla.org>");
+  testVal("foo.bar<?x@mozilla.org>");
+  testVal("foo.bar<?@x@mozilla.org>");
+  testVal("<foo.bar@x@>mozilla.org");
+  testVal("<foo.bar@:baz@>mozilla.org");
+  testVal("<foo.bar:@baz@>mozilla.org");
+  testVal("<foo.bar@:ba:z@>mozilla.org");
+  testVal("<foo.:bar:@baz@>mozilla.org");
 
   testVal("<https://sub.>mozilla.org<:666/file.ext>");
   testVal("<sub.>mozilla.org<:666/file.ext>");
   testVal("localhost<:666/file.ext>");
 
   let IPs = ["192.168.1.1",
              "[::]",
              "[::1]",
deleted file mode 100644
--- a/browser/base/content/test/general/browser_visibleLabel.js
+++ /dev/null
@@ -1,94 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/* Tests:
- *   verify that the visibleLabel attribute works
- *   verify the TabLabelModified event works for both existing and new tabs
- */
-
-function test() {
-  waitForExplicitFinish();
-  registerCleanupFunction(function() {
-    gBrowser.removeCurrentTab({animate: false});
-  });
-  let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank",
-                                                   {skipAnimation: true});
-  tab.linkedBrowser.addEventListener("load", function onLoad(event) {
-    event.currentTarget.removeEventListener("load", onLoad, true);
-    executeSoon(afterLoad);
-  }, true);
-}
-
-function afterLoad() {
-  let tab = gBrowser.selectedTab;
-  let xulLabel = document.getAnonymousElementByAttribute(tab, "anonid",
-                                                         "tab-label");
-  // Verify we're starting out on the right foot
-  is(tab.label, "New Tab", "Initial tab label is default");
-  is(xulLabel.value, "New Tab", "Label element is default");
-  is(tab.visibleLabel, "New Tab", "visibleLabel is default");
-
-  // Check that a normal label setting works correctly
-  tab.label = "Hello, world!";
-  is(tab.label, "Hello, world!", "tab label attribute set via tab.label");
-  is(xulLabel.value, "Hello, world!", "xul:label set via tab.label");
-  is(tab.visibleLabel, "Hello, world!", "visibleLabel set via tab.label");
-
-  // Check that setting visibleLabel only affects the label element
-  tab.visibleLabel = "Goodnight, Irene";
-  is(tab.label, "Hello, world!", "Tab.label unaffected by visibleLabel setter");
-  is(xulLabel.value, "Goodnight, Irene",
-     "xul:label set by visibleLabel setter");
-  is(tab.visibleLabel, "Goodnight, Irene",
-     "visibleLabel attribute set by visibleLabel setter");
-
-  // Check that setting the label property hits everything
-  tab.label = "One more label";
-  is(tab.label, "One more label",
-     "Tab label set via label property after diverging from visibleLabel");
-  is(xulLabel.value, "One more label",
-     "xul:label set via label property after diverging from visibleLabel");
-  is(tab.visibleLabel, "One more label",
-     "visibleLabel set from label property after diverging from visibleLabel");
-
-  tab.addEventListener("TabLabelModified", overrideTabLabel, true);
-  tab.label = "This won't be the visibleLabel";
-}
-
-function overrideTabLabel(aEvent) {
-  aEvent.target.removeEventListener("TabLabelModified", overrideTabLabel, true);
-  aEvent.preventDefault();
-  aEvent.stopPropagation();
-  aEvent.target.visibleLabel = "Handler set this as the visible label";
-  executeSoon(checkTabLabelModified);
-}
-
-function checkTabLabelModified() {
-  let tab = gBrowser.selectedTab;
-  let xulLabel = document.getAnonymousElementByAttribute(tab, "anonid",
-                                                         "tab-label");
-
-  is(tab.label, "This won't be the visibleLabel",
-     "Tab label set via label property that triggered event");
-  is(xulLabel.value, "Handler set this as the visible label",
-     "xul:label set by TabLabelModified handler");
-  is(tab.visibleLabel, "Handler set this as the visible label",
-     "visibleLabel set by TabLabelModified handler");
-
-  gBrowser.removeCurrentTab({animate: false});
-  executeSoon(checkTabLabelModifiedOnNewTab);
-}
-
-function checkTabLabelModifiedOnNewTab() {
-  gBrowser.tabContainer.addEventListener("TabLabelModified",
-    handleTabLabelModifiedOnNewTab, true);
-  let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank",
-                                                   {skipAnimation: true});
-}
-
-function handleTabLabelModifiedOnNewTab(aEvent) {
-  gBrowser.tabContainer.removeEventListener("TabLabelModified",
-    handleTabLabelModifiedOnNewTab, true);
-  ok(true, "Event received from new tab default being set");
-  executeSoon(finish);
-}
rename from browser/base/content/test/general/file_bug550565_favicon.ico
rename to browser/base/content/test/general/file_generic_favicon.ico
rename from browser/base/content/test/general/file_bug550565_popup.html
rename to browser/base/content/test/general/file_with_favicon.html
--- a/browser/base/content/test/general/file_bug550565_popup.html
+++ b/browser/base/content/test/general/file_with_favicon.html
@@ -1,12 +1,12 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <title>Test file for bug 550565.</title>
+  <title>Test file for bugs with favicons</title>
 
   <!--Set a favicon; that's the whole point of this file.-->
-  <link rel="icon" href="file_bug550565_favicon.ico">
+  <link rel="icon" href="file_generic_favicon.ico">
 </head>
 <body>
-  Test file for bug 550565.
+  Test file for bugs with favicons
 </body>
 </html>
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -1144,29 +1144,34 @@ function getPropertyBagValue(bag, key) {
   return null;
 }
 
 /**
  * Returns a Promise that resolves once a crash report has
  * been submitted. This function will also test the crash
  * reports extra data to see if it matches expectedExtra.
  *
- * @param expectedExtra
+ * @param expectedExtra (object)
  *        An Object whose key-value pairs will be compared
  *        against the key-value pairs in the extra data of the
  *        crash report. A test failure will occur if there is
  *        a mismatch.
  *
- *        Note that this will only check the values that exist
+ *        If the value of the key-value pair is "null", this will
+ *        be interpreted as "this key should not be included in the
+ *        extra data", and will cause a test failure if it is detected
+ *        in the crash report.
+ *
+ *        Note that this will ignore any keys that are not included
  *        in expectedExtra. It's possible that the crash report
  *        will contain other extra information that is not
  *        compared against.
  * @returns Promise
  */
-function promiseCrashReport(expectedExtra) {
+function promiseCrashReport(expectedExtra={}) {
   return Task.spawn(function*() {
     info("Starting wait on crash-report-status");
     let [subject, data] =
       yield TestUtils.topicObserved("crash-report-status", (subject, data) => {
         return data == "success";
       });
     info("Topic observed!");
 
@@ -1195,14 +1200,18 @@ function promiseCrashReport(expectedExtr
     }
 
     info("Iterating crash report extra keys");
     let enumerator = extra.enumerator;
     while (enumerator.hasMoreElements()) {
       let key = enumerator.getNext().QueryInterface(Ci.nsIProperty).name;
       let value = extra.getPropertyAsAString(key);
       if (key in expectedExtra) {
-        is(value, expectedExtra[key],
-           `Crash report had the right extra value for ${key}`);
+        if (expectedExtra[key] == null) {
+          ok(false, `Got unexpected key ${key} with value ${value}`);
+        } else {
+          is(value, expectedExtra[key],
+             `Crash report had the right extra value for ${key}`);
+        }
       }
     }
   });
 }
--- a/browser/base/content/test/general/test_contextmenu.html
+++ b/browser/base/content/test/general/test_contextmenu.html
@@ -503,27 +503,27 @@ function runTest(testNum) {
                           "---",                  null,
                           "context-viewsource",   true,
                           "context-viewinfo",     true
                          ].concat(inspectItems));
 
         invokeItemAction("1");
         closeContextMenu();
 
-        // run mozRequestFullScreen on the element we're testing
+        // run requestFullscreen on the element we're testing
         var full_screen_element = subwindow.document.getElementById("test-dom-full-screen");
         var openDomFullScreen = function() {
-            subwindow.removeEventListener("mozfullscreenchange", openDomFullScreen, false);
+            subwindow.removeEventListener("fullscreenchange", openDomFullScreen, false);
             openContextMenuFor(dom_full_screen, true); // Invoke context menu for next test.
         }
-        subwindow.addEventListener("mozfullscreenchange", openDomFullScreen, false);
+        subwindow.addEventListener("fullscreenchange", openDomFullScreen, false);
         SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", false);
         SpecialPowers.setCharPref("full-screen-api.transition-duration.enter", "0 0");
         SpecialPowers.setCharPref("full-screen-api.transition-duration.leave", "0 0");
-        full_screen_element.mozRequestFullScreen();
+        full_screen_element.requestFullscreen();
     },
 
     function () {
         // Context menu for DOM Fullscreen mode (NOTE: this is *NOT* on an img)
         checkContextMenu(["context-navigation", null,
                               ["context-back",            false,
                                "context-forward",         false,
                                "context-reload",          true,
@@ -537,24 +537,24 @@ function runTest(testNum) {
                           "context-selectall",            true,
                           "---",                          null,
                           "context-viewsource",           true,
                           "context-viewinfo",             true
                          ].concat(inspectItems));
         closeContextMenu();
         var full_screen_element = subwindow.document.getElementById("test-dom-full-screen");
         var openPagemenu = function() {
-            subwindow.removeEventListener("mozfullscreenchange", openPagemenu, false);
+            subwindow.removeEventListener("fullscreenchange", openPagemenu, false);
             SpecialPowers.clearUserPref("full-screen-api.allow-trusted-requests-only");
             SpecialPowers.clearUserPref("full-screen-api.transition-duration.enter", "0 0");
             SpecialPowers.clearUserPref("full-screen-api.transition-duration.leave", "0 0");
             openContextMenuFor(pagemenu, true); // Invoke context menu for next test.
         }
-        subwindow.addEventListener("mozfullscreenchange", openPagemenu, false);
-        subwindow.document.mozCancelFullScreen();
+        subwindow.addEventListener("fullscreenchange", openPagemenu, false);
+        subwindow.document.exitFullscreen();
     },
 
     function () {
         // Context menu for element with assigned content context menu
         // The shift key should bypass content context menu processing
         checkContextMenu(["context-navigation", null,
                               ["context-back",         false,
                                "context-forward",      false,
--- a/browser/base/content/test/social/browser.ini
+++ b/browser/base/content/test/social/browser.ini
@@ -24,23 +24,20 @@ support-files =
   social_sidebar.html
   social_sidebar_empty.html
   social_window.html
   social_worker.js
   unchecked.jpg
 
 [browser_aboutHome_activation.js]
 [browser_addons.js]
-skip-if = e10s && debug # Leaking docshells (bug 1150147)
 [browser_blocklist.js]
-skip-if = e10s && debug # Leaking docshells (bug 1150147)
 [browser_share.js]
 skip-if = true # bug 1115131
 [browser_social_activation.js]
-skip-if = e10s && debug # e10s/Linux/Debug Leaking docshells (bug 1150147)
 [browser_social_chatwindow.js]
 skip-if = true # Bug 1245798 'document-element-inserted' is not fired for chat windows anymore, so no mozSocial
 [browser_social_chatwindow_resize.js]
 skip-if = true # Bug 1245798 'document-element-inserted' is not fired for chat windows anymore, so no mozSocial
 [browser_social_chatwindowfocus.js]
 skip-if = true # Bug 1245798 'document-element-inserted' is not fired for chat windows anymore, so no mozSocial
 [browser_social_contextmenu.js]
 skip-if = (os == 'linux' && e10s) # Bug 1072669 context menu relies on target element
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -256,17 +256,17 @@ file, You can obtain one at http://mozil
           // trimmedLength to ensure we don't count the length of a trimmed protocol
           // when determining which parts of the URL to highlight as "preDomain".
           let trimmedLength = 0;
           if (uriInfo.fixedURI.scheme == "http" && !value.startsWith("http://")) {
             value = "http://" + value;
             trimmedLength = "http://".length;
           }
 
-          let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
+          let matchedURL = value.match(/^((?:[a-z]+:\/\/)(?:[^\/#?]+@)?)(.+?)(?::\d+)?(?:[\/#?]|$)/);
           if (!matchedURL)
             return;
 
           // Strike out the "https" part if mixed active content is loaded.
           if (this.getAttribute("pageproxystate") == "valid" &&
               value.startsWith("https:") &&
               gBrowser.securityUI.state &
                 Ci.nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT) {
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -84,16 +84,17 @@ browser.jar:
         content/browser/browser-eme.js                (content/browser-eme.js)
         content/browser/browser-feeds.js              (content/browser-feeds.js)
         content/browser/browser-fullScreen.js         (content/browser-fullScreen.js)
         content/browser/browser-fullZoom.js           (content/browser-fullZoom.js)
         content/browser/browser-fxaccounts.js         (content/browser-fxaccounts.js)
         content/browser/browser-gestureSupport.js     (content/browser-gestureSupport.js)
         content/browser/browser-places.js             (content/browser-places.js)
         content/browser/browser-plugins.js            (content/browser-plugins.js)
+        content/browser/browser-refreshblocker.js     (content/browser-refreshblocker.js)
 #ifdef MOZ_SAFE_BROWSING
         content/browser/browser-safebrowsing.js       (content/browser-safebrowsing.js)
 #endif
         content/browser/browser-sidebar.js            (content/browser-sidebar.js)
         content/browser/browser-social.js             (content/browser-social.js)
         content/browser/browser-syncui.js             (content/browser-syncui.js)
 *       content/browser/browser-tabPreviews.xml       (content/browser-tabPreviews.xml)
 #ifdef CAN_DRAW_IN_TITLEBAR
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -179,63 +179,57 @@ const CustomizableWidgets = [
     onViewShowing: function(aEvent) {
       // Populate our list of history
       const kMaxResults = 15;
       let doc = aEvent.detail.ownerDocument;
       let win = doc.defaultView;
 
       let options = PlacesUtils.history.getNewQueryOptions();
       options.excludeQueries = true;
-      options.includeHidden = false;
-      options.resultType = options.RESULTS_AS_URI;
       options.queryType = options.QUERY_TYPE_HISTORY;
       options.sortingMode = options.SORT_BY_DATE_DESCENDING;
       options.maxResults = kMaxResults;
       let query = PlacesUtils.history.getNewQuery();
 
       let items = doc.getElementById("PanelUI-historyItems");
       // Clear previous history items.
       while (items.firstChild) {
-        items.removeChild(items.firstChild);
+        items.firstChild.remove();
       }
 
       // Get all statically placed buttons to supply them with keyboard shortcuts.
       let staticButtons = items.parentNode.getElementsByTagNameNS(kNSXUL, "toolbarbutton");
       for (let i = 0, l = staticButtons.length; i < l; ++i)
         CustomizableUI.addShortcut(staticButtons[i]);
 
       PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
                          .asyncExecuteLegacyQueries([query], 1, options, {
         handleResult: function (aResultSet) {
-          let onHistoryVisit = function (aUri, aEvent, aItem) {
-            doc.defaultView.openUILink(aUri, aEvent);
-            CustomizableUI.hidePanelForNode(aItem);
+          let onItemClick = function (aEvent) {
+            let item = aEvent.target;
+            win.openUILink(item.getAttribute("targetURI"), aEvent);
+            CustomizableUI.hidePanelForNode(item);
           };
           let fragment = doc.createDocumentFragment();
-          for (let row, i = 0; (row = aResultSet.getNextRow()); i++) {
-            try {
-              let uri = row.getResultByIndex(1);
-              let title = row.getResultByIndex(2);
-              let icon = row.getResultByIndex(6);
+          let row;
+          while ((row = aResultSet.getNextRow())) {
+            let uri = row.getResultByIndex(1);
+            let title = row.getResultByIndex(2);
+            let icon = row.getResultByIndex(6);
 
-              let item = doc.createElementNS(kNSXUL, "toolbarbutton");
-              item.setAttribute("label", title || uri);
-              item.setAttribute("targetURI", uri);
-              item.setAttribute("class", "subviewbutton");
-              item.addEventListener("click", function (aEvent) {
-                onHistoryVisit(uri, aEvent, item);
-              });
-              if (icon) {
-                let iconURL = "moz-anno:favicon:" + icon;
-                item.setAttribute("image", iconURL);
-              }
-              fragment.appendChild(item);
-            } catch (e) {
-              log.error("Error while showing history subview: " + e);
+            let item = doc.createElementNS(kNSXUL, "toolbarbutton");
+            item.setAttribute("label", title || uri);
+            item.setAttribute("targetURI", uri);
+            item.setAttribute("class", "subviewbutton");
+            item.addEventListener("click", onItemClick);
+            if (icon) {
+              let iconURL = "moz-anno:favicon:" + icon;
+              item.setAttribute("image", iconURL);
             }
+            fragment.appendChild(item);
           }
           items.appendChild(fragment);
         },
         handleError: function (aError) {
           log.debug("History view tried to show but had an error: " + aError);
         },
         handleCompletion: function (aReason) {
           log.debug("History view is being shown!");
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -911,18 +911,22 @@ CustomizeMode.prototype = {
       } else if (wrapper.hasAttribute("haswideitem")) {
         wrapper.removeAttribute("haswideitem");
       }
     }
 
     let removable = aPlace == "palette" || CustomizableUI.isWidgetRemovable(aNode);
     wrapper.setAttribute("removable", removable);
 
-    let contextMenuAttrName = aNode.getAttribute("context") ? "context" :
-                                aNode.getAttribute("contextmenu") ? "contextmenu" : "";
+    let contextMenuAttrName = "";
+    if (aNode.getAttribute("context")) {
+      contextMenuAttrName = "context";
+    } else if (aNode.getAttribute("contextmenu")) {
+      contextMenuAttrName = "contextmenu";
+    }
     let currentContextMenu = aNode.getAttribute(contextMenuAttrName);
     let contextMenuForPlace = aPlace == "panel" ?
                                 kPanelItemContextMenu :
                                 kPaletteItemContextMenu;
     if (aPlace != "toolbar") {
       wrapper.setAttribute("context", contextMenuForPlace);
     }
     // Only keep track of the menu if it is non-default.
--- a/browser/components/extensions/ext-bookmarks.js
+++ b/browser/components/extensions/ext-bookmarks.js
@@ -1,13 +1,13 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/PlacesUtils.jsm");
 var Bookmarks = PlacesUtils.bookmarks;
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
@@ -106,17 +106,19 @@ extensions.registerSchemaAPI("bookmarks"
       getTree: function() {
         return getTree(Bookmarks.rootGuid, false);
       },
 
       getSubTree: function(id) {
         return getTree(id, false);
       },
 
-      // search
+      search: function(query) {
+        return Bookmarks.search(query).then(result => result.map(convert));
+      },
 
       create: function(bookmark) {
         let info = {
           title: bookmark.title || "",
         };
 
         // If url is NULL or missing, it will be a folder.
         if (bookmark.url !== null) {
@@ -134,17 +136,17 @@ extensions.registerSchemaAPI("bookmarks"
           info.parentGuid = bookmark.parentId;
         } else {
           info.parentGuid = Bookmarks.unfiledGuid;
         }
 
         try {
           return Bookmarks.insert(info).then(convert);
         } catch (e) {
-          return Promise.reject({ message: `Invalid bookmark: ${JSON.stringify(info)}` });
+          return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
         }
       },
 
       move: function(id, destination) {
         let info = {
           guid: id,
         };
 
@@ -153,17 +155,17 @@ extensions.registerSchemaAPI("bookmarks"
         }
         if (destination.index !== null) {
           info.index = destination.index;
         }
 
         try {
           return Bookmarks.update(info).then(convert);
         } catch (e) {
-          return Promise.reject({ message: `Invalid bookmark: ${JSON.stringify(info)}` });
+          return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
         }
       },
 
       update: function(id, changes) {
         let info = {
           guid: id,
         };
 
@@ -172,28 +174,28 @@ extensions.registerSchemaAPI("bookmarks"
         }
         if (changes.url !== null) {
           info.url = changes.url;
         }
 
         try {
           return Bookmarks.update(info).then(convert);
         } catch (e) {
-          return Promise.reject({ message: `Invalid bookmark: ${JSON.stringify(info)}` });
+          return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
         }
       },
 
       remove: function(id) {
         let info = {
           guid: id,
         };
 
         // The API doesn't give you the old bookmark at the moment
         try {
           return Bookmarks.remove(info).then(result => {});
         } catch (e) {
-          return Promise.reject({ message: `Invalid bookmark: ${JSON.stringify(info)}` });
+          return Promise.reject({message: `Invalid bookmark: ${JSON.stringify(info)}`});
         }
       },
     },
   };
 });
 
--- a/browser/components/extensions/ext-browserAction.js
+++ b/browser/components/extensions/ext-browserAction.js
@@ -39,17 +39,17 @@ function BrowserAction(options, extensio
     popup = extension.baseURI.resolve(popup);
   }
 
   this.defaults = {
     enabled: true,
     title: title || extension.name,
     badgeText: "",
     badgeBackgroundColor: null,
-    icon: IconDetails.normalize({ path: options.default_icon }, extension,
+    icon: IconDetails.normalize({path: options.default_icon}, extension,
                                 null, true),
     popup: popup,
   };
 
   this.tabContext = new TabContext(tab => Object.create(this.defaults),
                                    extension);
 
   EventEmitter.decorate(this);
--- a/browser/components/extensions/ext-contextMenus.js
+++ b/browser/components/extensions/ext-contextMenus.js
@@ -305,17 +305,17 @@ MenuItem.prototype = {
     this.children.splice(idx, 1);
     child.parent = null;
   },
 
   get root() {
     let extension = this.extension;
     if (!rootItems.has(extension)) {
       let root = new MenuItem(extension, this.context,
-                              { title: extension.name },
+                              {title: extension.name},
                               /* isRoot = */ true);
       rootItems.set(extension, root);
     }
 
     return rootItems.get(extension);
   },
 
   remove() {
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ext-desktop-runtime.js
@@ -0,0 +1,10 @@
+"use strict";
+
+/* eslint-disable mozilla/balanced-listeners */
+extensions.on("uninstall", (msg, extension) => {
+  if (extension.uninstallURL) {
+    let browser = Services.wm.getMostRecentWindow("navigator:browser").gBrowser;
+    browser.addTab(extension.uninstallURL, {relatedToCurrent: true});
+  }
+});
+
--- a/browser/components/extensions/ext-pageAction.js
+++ b/browser/components/extensions/ext-pageAction.js
@@ -23,17 +23,17 @@ function PageAction(options, extension) 
   let popup = extension.localize(options.default_popup || "");
   if (popup) {
     popup = extension.baseURI.resolve(popup);
   }
 
   this.defaults = {
     show: false,
     title: title || extension.name,
-    icon: IconDetails.normalize({ path: options.default_icon }, extension,
+    icon: IconDetails.normalize({path: options.default_icon}, extension,
                                 null, true),
     popup: popup && extension.baseURI.resolve(popup),
   };
 
   this.tabContext = new TabContext(tab => Object.create(this.defaults),
                                    extension);
 
   this.tabContext.on("location-change", this.handleLocationChange.bind(this)); // eslint-disable-line mozilla/balanced-listeners
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -460,17 +460,17 @@ extensions.registerSchemaAPI("tabs", nul
             }
           }
         }
         return Promise.resolve(result);
       },
 
       captureVisibleTab: function(windowId, options) {
         if (!extension.hasPermission("<all_urls>")) {
-          return Promise.reject({ message: "The <all_urls> permission is required to use the captureVisibleTab API" });
+          return Promise.reject({message: "The <all_urls> permission is required to use the captureVisibleTab API"});
         }
 
         let window = windowId == null ?
           WindowManager.topWindow :
           WindowManager.getWindow(windowId);
 
         let browser = window.gBrowser.selectedBrowser;
         let recipient = {
@@ -519,31 +519,31 @@ extensions.registerSchemaAPI("tabs", nul
         }
 
         if (details.code !== null) {
           options[kind + "Code"] = details.code;
         }
         if (details.file !== null) {
           let url = context.uri.resolve(details.file);
           if (!extension.isExtensionURL(url)) {
-            return Promise.reject({ message: "Files to be injected must be within the extension" });
+            return Promise.reject({message: "Files to be injected must be within the extension"});
           }
           options[kind].push(url);
         }
         if (details.allFrames) {
           options.all_frames = details.allFrames;
         }
         if (details.matchAboutBlank) {
           options.match_about_blank = details.matchAboutBlank;
         }
         if (details.runAt !== null) {
           options.run_at = details.runAt;
         }
 
-        return context.sendMessage(mm, "Extension:Execute", { options }, recipient);
+        return context.sendMessage(mm, "Extension:Execute", {options}, recipient);
       },
 
       executeScript: function(tabId, details) {
         return self.tabs._execute(tabId, details, "js");
       },
 
       insertCSS: function(tabId, details) {
         return self.tabs._execute(tabId, details, "css");
@@ -608,51 +608,43 @@ extensions.registerSchemaAPI("tabs", nul
           let tab = TabManager.getTab(tabId);
           // Ignore invalid tab ids.
           if (!tab) {
             continue;
           }
 
           // If the window is not specified, use the window from the tab.
           let window = destinationWindow || tab.ownerDocument.defaultView;
-          let windowId = WindowManager.getId(window);
           let gBrowser = window.gBrowser;
 
-          let getInsertionPoint = () => {
-            let point = indexMap.get(window) || index;
-            // If the index is -1 it should go to the end of the tabs.
-            if (point == -1) {
-              point = gBrowser.tabs.length;
-            }
-            indexMap.set(window, point + 1);
-            return point;
-          };
+          let insertionPoint = indexMap.get(window) || index;
+          // If the index is -1 it should go to the end of the tabs.
+          if (insertionPoint == -1) {
+            insertionPoint = gBrowser.tabs.length;
+          }
 
-          if (WindowManager.getId(tab.ownerDocument.defaultView) !== windowId) {
+          // We can only move pinned tabs to a point within, or just after,
+          // the current set of pinned tabs. Unpinned tabs, likewise, can only
+          // be moved to a position after the current set of pinned tabs.
+          // Attempts to move a tab to an illegal position are ignored.
+          let numPinned = gBrowser._numPinnedTabs;
+          let ok = tab.pinned ? insertionPoint <= numPinned : insertionPoint >= numPinned;
+          if (!ok) {
+            continue;
+          }
+
+          indexMap.set(window, insertionPoint + 1);
+
+          if (tab.ownerDocument.defaultView !== window) {
             // If the window we are moving the tab in is different, then move the tab
             // to the new window.
-            let newTab = gBrowser.addTab("about:blank");
-            let newBrowser = gBrowser.getBrowserForTab(newTab);
-            gBrowser.updateBrowserRemotenessByURL(newBrowser, tab.linkedBrowser.currentURI.spec);
-            newBrowser.stop();
-            // This is necessary for getter side-effects.
-            void newBrowser.docShell;
-
-            if (tab.pinned) {
-              gBrowser.pinTab(newTab);
-            }
-
-            gBrowser.moveTabTo(newTab, getInsertionPoint());
-
-            tab.parentNode._finishAnimateTabMove();
-            gBrowser.swapBrowsersAndCloseOther(newTab, tab);
-            tab = newTab;
+            tab = gBrowser.adoptTab(tab, insertionPoint, false);
           } else {
             // If the window we are moving is the same, just move the tab.
-            gBrowser.moveTabTo(tab, getInsertionPoint());
+            gBrowser.moveTabTo(tab, insertionPoint);
           }
           tabsMoved.push(tab);
         }
 
         return Promise.resolve(tabsMoved.map(tab => TabManager.convert(extension, tab)));
       },
     },
   };
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -230,17 +230,17 @@ class BasePopup {
       // We can't finish setting up the browser until the binding has fully
       // initialized. Waiting for the first load event guarantees that it has.
       let loadListener = event => {
         this.browser.removeEventListener("load", loadListener, true);
         resolve();
       };
       this.browser.addEventListener("load", loadListener, true);
     }).then(() => {
-      let { contentWindow } = this.browser;
+      let {contentWindow} = this.browser;
 
       contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindowUtils)
                    .allowScriptsToClose();
 
       this.context = new ExtensionPage(this.extension, {
         type: "popup",
         contentWindow,
@@ -418,17 +418,17 @@ ExtensionTabManager.prototype = {
 
   hasTabPermission(tab) {
     return this.extension.hasPermission("tabs") || this.hasActiveTabPermission(tab);
   },
 
   convert(tab) {
     let window = tab.ownerDocument.defaultView;
 
-    let mutedInfo = { muted: tab.muted };
+    let mutedInfo = {muted: tab.muted};
     if (tab.muteReason === null) {
       mutedInfo.reason = "user";
     } else if (tab.muteReason) {
       mutedInfo.reason = "extension";
       mutedInfo.extensionId = tab.muteReason;
     }
 
     let result = {
--- a/browser/components/extensions/jar.mn
+++ b/browser/components/extensions/jar.mn
@@ -3,11 +3,12 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 browser.jar:
     content/browser/extension.svg
     content/browser/ext-utils.js
     content/browser/ext-contextMenus.js
     content/browser/ext-browserAction.js
     content/browser/ext-pageAction.js
+    content/browser/ext-desktop-runtime.js
     content/browser/ext-tabs.js
     content/browser/ext-windows.js
     content/browser/ext-bookmarks.js
--- a/browser/components/extensions/schemas/bookmarks.json
+++ b/browser/components/extensions/schemas/bookmarks.json
@@ -229,17 +229,16 @@
                 "items": { "$ref": "BookmarkTreeNode" }
               }
             ]
           }
         ]
       },
       {
         "name": "search",
-        "unsupported": true,
         "type": "function",
         "description": "Searches for BookmarkTreeNodes matching the given query. Queries specified with an object produce BookmarkTreeNodes matching all specified properties.",
         "async": "callback",
         "parameters": [
           {
             "name": "query",
             "description": "Either a string of words and quoted phrases that are matched against bookmark URLs and titles, or an object. If an object, the properties <code>query</code>, <code>url</code>, and <code>title</code> may be specified and bookmarks matching all specified properties will be produced.",
             "choices": [
@@ -253,16 +252,17 @@
                 "properties": {
                   "query": {
                     "type": "string",
                     "optional": true,
                     "description": "A string of words and quoted phrases that are matched against bookmark URLs and titles."
                   },
                   "url": {
                     "type": "string",
+                    "format": "url",
                     "optional": true,
                     "description": "The URL of the bookmark; matches verbatim. Note that folders have no URL."
                   },
                   "title": {
                     "type": "string",
                     "optional": true,
                     "description": "The title of the bookmark; matches verbatim."
                   }
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -16,16 +16,17 @@ support-files =
 [browser_ext_browserAction_disabled.js]
 [browser_ext_pageAction_context.js]
 [browser_ext_pageAction_popup.js]
 [browser_ext_browserAction_popup.js]
 [browser_ext_popup_api_injection.js]
 [browser_ext_contextMenus.js]
 [browser_ext_getViews.js]
 [browser_ext_lastError.js]
+[browser_ext_runtime_setUninstallURL.js]
 [browser_ext_tabs_audio.js]
 [browser_ext_tabs_captureVisibleTab.js]
 [browser_ext_tabs_executeScript.js]
 [browser_ext_tabs_executeScript_good.js]
 [browser_ext_tabs_executeScript_bad.js]
 [browser_ext_tabs_insertCSS.js]
 [browser_ext_tabs_query.js]
 [browser_ext_tabs_getCurrent.js]
@@ -33,8 +34,9 @@ support-files =
 [browser_ext_tabs_update.js]
 [browser_ext_tabs_onUpdated.js]
 [browser_ext_tabs_sendMessage.js]
 [browser_ext_tabs_move.js]
 [browser_ext_tabs_move_window.js]
 [browser_ext_windows_update.js]
 [browser_ext_contentscript_connect.js]
 [browser_ext_tab_runtimeConnect.js]
+[browser_ext_webNavigation_getFrames.js]
\ No newline at end of file
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_context.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_context.js
@@ -8,20 +8,20 @@ function* runTests(options) {
     // promise that resolves to an object containing them.
     function getDetails(tabId) {
       return Promise.all([
         browser.browserAction.getTitle({tabId}),
         browser.browserAction.getPopup({tabId}),
         browser.browserAction.getBadgeText({tabId}),
         browser.browserAction.getBadgeBackgroundColor({tabId})]
       ).then(details => {
-        return Promise.resolve({ title: details[0],
-                                 popup: details[1],
-                                 badge: details[2],
-                                 badgeBackgroundColor: details[3] });
+        return Promise.resolve({title: details[0],
+                                popup: details[1],
+                                badge: details[2],
+                                badgeBackgroundColor: details[3]});
       });
     }
 
     function checkDetails(expecting, tabId) {
       return getDetails(tabId).then(details => {
         browser.test.assertEq(expecting.title, details.title,
                               "expected value from getTitle");
 
@@ -48,17 +48,17 @@ function* runTests(options) {
     // and passes control back to the outer test scope.
     function nextTest() {
       let test = tests.shift();
 
       test(expecting => {
         // Check that the API returns the expected values, and then
         // run the next test.
         new Promise(resolve => {
-          return browser.tabs.query({ active: true, currentWindow: true }, resolve);
+          return browser.tabs.query({active: true, currentWindow: true}, resolve);
         }).then(tabs => {
           return checkDetails(expecting, tabs[0].id);
         }).then(() => {
           // Check that the actual icon has the expected values, then
           // run the next test.
           browser.test.sendMessage("nextTest", expecting, tests.length);
         });
       });
@@ -67,17 +67,17 @@ function* runTests(options) {
     browser.test.onMessage.addListener((msg) => {
       if (msg != "runNextTest") {
         browser.test.fail("Expecting 'runNextTest' message");
       }
 
       nextTest();
     });
 
-    browser.tabs.query({ active: true, currentWindow: true }, resultTabs => {
+    browser.tabs.query({active: true, currentWindow: true}, resultTabs => {
       tabs[0] = resultTabs[0].id;
 
       nextTest();
     });
   }
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: options.manifest,
@@ -143,82 +143,82 @@ add_task(function* testTabSwitchContext(
         "default_popup": "default.html",
         "default_title": "Default Title",
       },
       "permissions": ["tabs"],
     },
 
     getTests(tabs, expectDefaults) {
       let details = [
-        { "icon": browser.runtime.getURL("default.png"),
-          "popup": browser.runtime.getURL("default.html"),
-          "title": "Default Title",
-          "badge": "",
-          "badgeBackgroundColor": null },
-        { "icon": browser.runtime.getURL("1.png"),
-          "popup": browser.runtime.getURL("default.html"),
-          "title": "Default Title",
-          "badge": "",
-          "badgeBackgroundColor": null },
-        { "icon": browser.runtime.getURL("2.png"),
-          "popup": browser.runtime.getURL("2.html"),
-          "title": "Title 2",
-          "badge": "2",
-          "badgeBackgroundColor": [0xff, 0, 0, 0xff],
-          "disabled": true },
-        { "icon": browser.runtime.getURL("1.png"),
-          "popup": browser.runtime.getURL("default-2.html"),
-          "title": "Default Title 2",
-          "badge": "d2",
-          "badgeBackgroundColor": [0, 0xff, 0, 0xff],
-          "disabled": true },
-        { "icon": browser.runtime.getURL("1.png"),
-          "popup": browser.runtime.getURL("default-2.html"),
-          "title": "Default Title 2",
-          "badge": "d2",
-          "badgeBackgroundColor": [0, 0xff, 0, 0xff],
-          "disabled": false },
-        { "icon": browser.runtime.getURL("default-2.png"),
-          "popup": browser.runtime.getURL("default-2.html"),
-          "title": "Default Title 2",
-          "badge": "d2",
-          "badgeBackgroundColor": [0, 0xff, 0, 0xff] },
+        {"icon": browser.runtime.getURL("default.png"),
+         "popup": browser.runtime.getURL("default.html"),
+         "title": "Default Title",
+         "badge": "",
+         "badgeBackgroundColor": null},
+        {"icon": browser.runtime.getURL("1.png"),
+         "popup": browser.runtime.getURL("default.html"),
+         "title": "Default Title",
+         "badge": "",
+         "badgeBackgroundColor": null},
+        {"icon": browser.runtime.getURL("2.png"),
+         "popup": browser.runtime.getURL("2.html"),
+         "title": "Title 2",
+         "badge": "2",
+         "badgeBackgroundColor": [0xff, 0, 0, 0xff],
+          "disabled": true},
+        {"icon": browser.runtime.getURL("1.png"),
+         "popup": browser.runtime.getURL("default-2.html"),
+         "title": "Default Title 2",
+         "badge": "d2",
+         "badgeBackgroundColor": [0, 0xff, 0, 0xff],
+          "disabled": true},
+        {"icon": browser.runtime.getURL("1.png"),
+         "popup": browser.runtime.getURL("default-2.html"),
+         "title": "Default Title 2",
+         "badge": "d2",
+         "badgeBackgroundColor": [0, 0xff, 0, 0xff],
+         "disabled": false},
+        {"icon": browser.runtime.getURL("default-2.png"),
+         "popup": browser.runtime.getURL("default-2.html"),
+         "title": "Default Title 2",
+         "badge": "d2",
+         "badgeBackgroundColor": [0, 0xff, 0, 0xff]},
       ];
 
       return [
         expect => {
           browser.test.log("Initial state, expect default properties.");
           expectDefaults(details[0]).then(() => {
             expect(details[0]);
           });
         },
         expect => {
           browser.test.log("Change the icon in the current tab. Expect default properties excluding the icon.");
-          browser.browserAction.setIcon({ tabId: tabs[0], path: "1.png" });
+          browser.browserAction.setIcon({tabId: tabs[0], path: "1.png"});
           expectDefaults(details[0]).then(() => {
             expect(details[1]);
           });
         },
         expect => {
           browser.test.log("Create a new tab. Expect default properties.");
-          browser.tabs.create({ active: true, url: "about:blank?0" }, tab => {
+          browser.tabs.create({active: true, url: "about:blank?0"}, tab => {
             tabs.push(tab.id);
             expectDefaults(details[0]).then(() => {
               expect(details[0]);
             });
           });
         },
         expect => {
           browser.test.log("Change properties. Expect new properties.");
           let tabId = tabs[1];
-          browser.browserAction.setIcon({ tabId, path: "2.png" });
-          browser.browserAction.setPopup({ tabId, popup: "2.html" });
-          browser.browserAction.setTitle({ tabId, title: "Title 2" });
-          browser.browserAction.setBadgeText({ tabId, text: "2" });
-          browser.browserAction.setBadgeBackgroundColor({ tabId, color: [0xff, 0, 0, 0xff] });
+          browser.browserAction.setIcon({tabId, path: "2.png"});
+          browser.browserAction.setPopup({tabId, popup: "2.html"});
+          browser.browserAction.setTitle({tabId, title: "Title 2"});
+          browser.browserAction.setBadgeText({tabId, text: "2"});
+          browser.browserAction.setBadgeBackgroundColor({tabId, color: [0xff, 0, 0, 0xff]});
           browser.browserAction.disable(tabId);
 
           expectDefaults(details[0]).then(() => {
             expect(details[2]);
           });
         },
         expect => {
           browser.test.log("Navigate to a new page. Expect no changes.");
@@ -227,60 +227,60 @@ add_task(function* testTabSwitchContext(
           // callback currently fires too early in e10s windows.
           browser.tabs.onUpdated.addListener(function listener(tabId, changed) {
             if (tabId == tabs[1] && changed.url) {
               browser.tabs.onUpdated.removeListener(listener);
               expect(details[2]);
             }
           });
 
-          browser.tabs.update(tabs[1], { url: "about:blank?1" });
+          browser.tabs.update(tabs[1], {url: "about:blank?1"});
         },
         expect => {
           browser.test.log("Switch back to the first tab. Expect previously set properties.");
-          browser.tabs.update(tabs[0], { active: true }, () => {
+          browser.tabs.update(tabs[0], {active: true}, () => {
             expect(details[1]);
           });
         },
         expect => {
           browser.test.log("Change default values, expect those changes reflected.");
-          browser.browserAction.setIcon({ path: "default-2.png" });
-          browser.browserAction.setPopup({ popup: "default-2.html" });
-          browser.browserAction.setTitle({ title: "Default Title 2" });
-          browser.browserAction.setBadgeText({ text: "d2" });
-          browser.browserAction.setBadgeBackgroundColor({ color: [0, 0xff, 0, 0xff] });
+          browser.browserAction.setIcon({path: "default-2.png"});
+          browser.browserAction.setPopup({popup: "default-2.html"});
+          browser.browserAction.setTitle({title: "Default Title 2"});
+          browser.browserAction.setBadgeText({text: "d2"});
+          browser.browserAction.setBadgeBackgroundColor({color: [0, 0xff, 0, 0xff]});
           browser.browserAction.disable();
           expectDefaults(details[3]).then(() => {
             expect(details[3]);
           });
         },
         expect => {
           browser.test.log("Re-enable by default. Expect enabled.");
           browser.browserAction.enable();
           expectDefaults(details[4]).then(() => {
             expect(details[4]);
           });
         },
         expect => {
           browser.test.log("Switch back to tab 2. Expect former value, unaffected by changes to defaults in previous step.");
-          browser.tabs.update(tabs[1], { active: true }, () => {
+          browser.tabs.update(tabs[1], {active: true}, () => {
             expectDefaults(details[3]).then(() => {
               expect(details[2]);
             });
           });
         },
         expect => {
           browser.test.log("Delete tab, switch back to tab 1. Expect previous results again.");
           browser.tabs.remove(tabs[1], () => {
             expect(details[4]);
           });
         },
         expect => {
           browser.test.log("Create a new tab. Expect new default properties.");
-          browser.tabs.create({ active: true, url: "about:blank?2" }, tab => {
+          browser.tabs.create({active: true, url: "about:blank?2"}, tab => {
             tabs.push(tab.id);
             expect(details[5]);
           });
         },
         expect => {
           browser.test.log("Delete tab.");
           browser.tabs.remove(tabs[2], () => {
             expect(details[4]);
@@ -300,69 +300,69 @@ add_task(function* testDefaultTitle() {
         "default_icon": "icon.png",
       },
 
       "permissions": ["tabs"],
     },
 
     getTests(tabs, expectDefaults) {
       let details = [
-        { "title": "Foo Extension",
-          "popup": "",
-          "badge": "",
-          "badgeBackgroundColor": null,
-          "icon": browser.runtime.getURL("icon.png") },
-        { "title": "Foo Title",
-          "popup": "",
-          "badge": "",
-          "badgeBackgroundColor": null,
-          "icon": browser.runtime.getURL("icon.png") },
-        { "title": "Bar Title",
-          "popup": "",
-          "badge": "",
-          "badgeBackgroundColor": null,
-          "icon": browser.runtime.getURL("icon.png") },
-        { "title": "",
-          "popup": "",
-          "badge": "",
-          "badgeBackgroundColor": null,
-          "icon": browser.runtime.getURL("icon.png") },
+        {"title": "Foo Extension",
+         "popup": "",
+         "badge": "",
+         "badgeBackgroundColor": null,
+         "icon": browser.runtime.getURL("icon.png")},
+        {"title": "Foo Title",
+         "popup": "",
+         "badge": "",
+         "badgeBackgroundColor": null,
+         "icon": browser.runtime.getURL("icon.png")},
+        {"title": "Bar Title",
+         "popup": "",
+         "badge": "",
+         "badgeBackgroundColor": null,
+         "icon": browser.runtime.getURL("icon.png")},
+        {"title": "",
+         "popup": "",
+         "badge": "",
+         "badgeBackgroundColor": null,
+         "icon": browser.runtime.getURL("icon.png")},
       ];
 
       return [
         expect => {
           browser.test.log("Initial state. Expect extension title as default title.");
           expectDefaults(details[0]).then(() => {
             expect(details[0]);
           });
         },
         expect => {
           browser.test.log("Change the title. Expect new title.");
-          browser.browserAction.setTitle({ tabId: tabs[0], title: "Foo Title" });
+          browser.browserAction.setTitle({tabId: tabs[0], title: "Foo Title"});
           expectDefaults(details[0]).then(() => {
             expect(details[1]);
           });
         },
         expect => {
           browser.test.log("Change the default. Expect same properties.");
-          browser.browserAction.setTitle({ title: "Bar Title" });
+          browser.browserAction.setTitle({title: "Bar Title"});
           expectDefaults(details[2]).then(() => {
             expect(details[1]);
           });
         },
         expect => {
           browser.test.log("Clear the title. Expect new default title.");
-          browser.browserAction.setTitle({ tabId: tabs[0], title: "" });
+          browser.browserAction.setTitle({tabId: tabs[0], title: ""});
           expectDefaults(details[2]).then(() => {
             expect(details[2]);
           });
         },
         expect => {
           browser.test.log("Set default title to null string. Expect null string from API, extension title in UI.");
-          browser.browserAction.setTitle({ title: "" });
+          browser.browserAction.setTitle({title: ""});
           expectDefaults(details[3]).then(() => {
             expect(details[3]);
           });
         },
       ];
     },
   });
 });
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js
@@ -26,110 +26,110 @@ add_task(function* testDetailsObjects() 
     let imageData = {
       red: getImageData("red"),
       green: getImageData("green"),
     };
 
     /* eslint-disable comma-dangle, indent */
     let iconDetails = [
       // Only paths.
-      { details: { "path": "a.png" },
+      {details: {"path": "a.png"},
         resolutions: {
           "1": browser.runtime.getURL("data/a.png"),
-          "2": browser.runtime.getURL("data/a.png"), } },
-      { details: { "path": "/a.png" },
+          "2": browser.runtime.getURL("data/a.png")}},
+      {details: {"path": "/a.png"},
         resolutions: {
           "1": browser.runtime.getURL("a.png"),
-          "2": browser.runtime.getURL("a.png"), } },
-      { details: { "path": { "19": "a.png" } },
+          "2": browser.runtime.getURL("a.png")}},
+      {details: {"path": {"19": "a.png"}},
         resolutions: {
           "1": browser.runtime.getURL("data/a.png"),
-          "2": browser.runtime.getURL("data/a.png"), } },
-      { details: { "path": { "38": "a.png" } },
+          "2": browser.runtime.getURL("data/a.png")}},
+      {details: {"path": {"38": "a.png"}},
         resolutions: {
           "1": browser.runtime.getURL("data/a.png"),
-          "2": browser.runtime.getURL("data/a.png"), } },
-      { details: { "path": { "19": "a.png", "38": "a-x2.png" } },
+          "2": browser.runtime.getURL("data/a.png")}},
+      {details: {"path": {"19": "a.png", "38": "a-x2.png"}},
         resolutions: {
           "1": browser.runtime.getURL("data/a.png"),
-          "2": browser.runtime.getURL("data/a-x2.png"), } },
+          "2": browser.runtime.getURL("data/a-x2.png")}},
 
       // Only ImageData objects.
-      { details: { "imageData": imageData.red.imageData },
+      {details: {"imageData": imageData.red.imageData},
         resolutions: {
           "1": imageData.red.url,
-          "2": imageData.red.url, } },
-      { details: { "imageData": { "19": imageData.red.imageData } },
+          "2": imageData.red.url}},
+      {details: {"imageData": {"19": imageData.red.imageData}},
         resolutions: {
           "1": imageData.red.url,
-          "2": imageData.red.url, } },
-      { details: { "imageData": { "38": imageData.red.imageData } },
+          "2": imageData.red.url}},
+      {details: {"imageData": {"38": imageData.red.imageData}},
         resolutions: {
           "1": imageData.red.url,
-          "2": imageData.red.url, } },
-      { details: { "imageData": {
+          "2": imageData.red.url}},
+      {details: {"imageData": {
           "19": imageData.red.imageData,
-          "38": imageData.green.imageData } },
+          "38": imageData.green.imageData}},
         resolutions: {
           "1": imageData.red.url,
-          "2": imageData.green.url, } },
+          "2": imageData.green.url}},
 
       // Mixed path and imageData objects.
       //
       // The behavior is currently undefined if both |path| and
       // |imageData| specify icons of the same size.
-      { details: {
-          "path": { "19": "a.png" },
-          "imageData": { "38": imageData.red.imageData } },
+      {details: {
+          "path": {"19": "a.png"},
+          "imageData": {"38": imageData.red.imageData}},
         resolutions: {
           "1": browser.runtime.getURL("data/a.png"),
-          "2": imageData.red.url, } },
-      { details: {
-          "path": { "38": "a.png" },
-          "imageData": { "19": imageData.red.imageData } },
+          "2": imageData.red.url}},
+      {details: {
+          "path": {"38": "a.png"},
+          "imageData": {"19": imageData.red.imageData}},
         resolutions: {
           "1": imageData.red.url,
-          "2": browser.runtime.getURL("data/a.png"), } },
+          "2": browser.runtime.getURL("data/a.png")}},
 
       // A path or ImageData object by itself is treated as a 19px icon.
-      { details: {
+      {details: {
           "path": "a.png",
-          "imageData": { "38": imageData.red.imageData } },
+          "imageData": {"38": imageData.red.imageData}},
         resolutions: {
           "1": browser.runtime.getURL("data/a.png"),
-          "2": imageData.red.url, } },
-      { details: {
-          "path": { "38": "a.png" },
-          "imageData": imageData.red.imageData, },
+          "2": imageData.red.url}},
+      {details: {
+          "path": {"38": "a.png"},
+          "imageData": imageData.red.imageData},
         resolutions: {
           "1": imageData.red.url,
-          "2": browser.runtime.getURL("data/a.png"), } },
+          "2": browser.runtime.getURL("data/a.png")}},
 
       // Various resolutions
-      { details: { "path": { "18": "a.png", "32": "a-x2.png" } },
+      {details: {"path": {"18": "a.png", "32": "a-x2.png"}},
         resolutions: {
           "1": browser.runtime.getURL("data/a.png"),
-          "2": browser.runtime.getURL("data/a-x2.png"), } },
-      { details: { "path": { "16": "16.png", "100": "100.png" } },
+          "2": browser.runtime.getURL("data/a-x2.png")}},
+      {details: {"path": {"16": "16.png", "100": "100.png"}},
         resolutions: {
           "1": browser.runtime.getURL("data/100.png"),
-          "2": browser.runtime.getURL("data/100.png"), } },
-      { details: { "path": { "2": "2.png"} },
+          "2": browser.runtime.getURL("data/100.png")}},
+      {details: {"path": {"2": "2.png"}},
         resolutions: {
           "1": browser.runtime.getURL("data/2.png"),
-          "2": browser.runtime.getURL("data/2.png"), } },
-      { details: { "path": {
+          "2": browser.runtime.getURL("data/2.png")}},
+      {details: {"path": {
         "6": "6.png",
         "18": "18.png",
         "32": "32.png",
         "48": "48.png",
-        "128": "128.png" } },
+        "128": "128.png"}},
         resolutions: {
           "1": browser.runtime.getURL("data/18.png"),
-          "2": browser.runtime.getURL("data/48.png"), } },
+          "2": browser.runtime.getURL("data/48.png")}},
     ];
 
     // Allow serializing ImageData objects for logging.
     ImageData.prototype.toJSON = () => "<ImageData>";
 
     let tabId;
 
     browser.test.onMessage.addListener((msg, test) => {
@@ -157,25 +157,25 @@ add_task(function* testDetailsObjects() 
     // but it can't pass us icon definitions with ImageData objects. This
     // shouldn't be a problem, since structured clones should handle ImageData
     // objects without issue. Unfortunately, |cloneInto| implements a slightly
     // different algorithm than we use in web APIs, and does not handle them
     // correctly.
     let tests = [];
     for (let [idx, icon] of iconDetails.entries()) {
       for (let res of Object.keys(icon.resolutions)) {
-        tests.push({ index: idx, resolution: Number(res) });
+        tests.push({index: idx, resolution: Number(res)});
       }
     }
 
     // Sort by resolution, so we don't needlessly switch back and forth
     // between each test.
     tests.sort(test => test.resolution);
 
-    browser.tabs.query({ active: true, currentWindow: true }, tabs => {
+    browser.tabs.query({active: true, currentWindow: true}, tabs => {
       tabId = tabs[0].id;
       browser.pageAction.show(tabId);
 
       browser.test.sendMessage("ready", tests);
     });
   }
 
   let extension = ExtensionTestUtils.loadExtension({
@@ -225,52 +225,56 @@ add_task(function* testDetailsObjects() 
 add_task(function* testInvalidIconSizes() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "browser_action": {},
       "page_action": {},
     },
 
     background: function() {
-      browser.tabs.query({ active: true, currentWindow: true }, tabs => {
+      browser.tabs.query({active: true, currentWindow: true}, tabs => {
         let tabId = tabs[0].id;
 
+        let promises = [];
         for (let api of ["pageAction", "browserAction"]) {
           // helper function to run setIcon and check if it fails
           let assertSetIconThrows = function(detail, error, message) {
-            try {
-              detail.tabId = tabId;
-              browser[api].setIcon(detail);
+            detail.tabId = tabId;
+            promises.push(
+              browser[api].setIcon(detail).then(
+                () => {
+                  browser.test.fail("Expected an error on invalid icon size.");
+                  browser.test.notifyFail("setIcon with invalid icon size");
+                },
+                error => {
+                  browser.test.succeed("setIcon with invalid icon size");
+                }));
+          };
 
-              browser.test.fail("Expected an error on invalid icon size.");
-              browser.test.notifyFail("setIcon with invalid icon size");
-              return;
-            } catch (e) {
-              browser.test.succeed("setIcon with invalid icon size");
-            }
-          };
+          let imageData = new ImageData(1, 1);
 
           // test invalid icon size inputs
           for (let type of ["path", "imageData"]) {
-            assertSetIconThrows({ [type]: { "abcdef": "test.png" } });
-            assertSetIconThrows({ [type]: { "48px": "test.png" } });
-            assertSetIconThrows({ [type]: { "20.5": "test.png" } });
-            assertSetIconThrows({ [type]: { "5.0": "test.png" } });
-            assertSetIconThrows({ [type]: { "-300": "test.png" } });
-            assertSetIconThrows({ [type]: {
-              "abc": "test.png",
-              "5": "test.png"
-            }});
+            let img = type == "imageData" ? imageData : "test.png";
+
+            assertSetIconThrows({[type]: {"abcdef": img}});
+            assertSetIconThrows({[type]: {"48px": img}});
+            assertSetIconThrows({[type]: {"20.5": img}});
+            assertSetIconThrows({[type]: {"5.0": img}});
+            assertSetIconThrows({[type]: {"-300": img}});
+            assertSetIconThrows({[type]: {"abc": img, "5": img}});
           }
 
-          assertSetIconThrows({ imageData: { "abcdef": "test.png" }, path: {"5": "test.png"} });
-          assertSetIconThrows({ path: { "abcdef": "test.png" }, imageData: {"5": "test.png"} });
+          assertSetIconThrows({imageData: {"abcdef": imageData}, path: {"5": "test.png"}});
+          assertSetIconThrows({path: {"abcdef": "test.png"}, imageData: {"5": imageData}});
         }
 
-        browser.test.notifyPass("setIcon with invalid icon size");
+        Promise.all(promises).then(() => {
+          browser.test.notifyPass("setIcon with invalid icon size");
+        });
       });
     }
   });
 
   yield Promise.all([extension.startup(), extension.awaitFinish("setIcon with invalid icon size")]);
 
   yield extension.unload();
 });
@@ -278,32 +282,32 @@ add_task(function* testInvalidIconSizes(
 
 // Test that default icon details in the manifest.json file are handled
 // correctly.
 add_task(function* testDefaultDetails() {
   // TODO: Test localized variants.
   let icons = [
     "foo/bar.png",
     "/foo/bar.png",
-    { "19": "foo/bar.png" },
-    { "38": "foo/bar.png" },
-    { "19": "foo/bar.png", "38": "baz/quux.png" },
+    {"19": "foo/bar.png"},
+    {"38": "foo/bar.png"},
+    {"19": "foo/bar.png", "38": "baz/quux.png"},
   ];
 
   let expectedURL = new RegExp(String.raw`^moz-extension://[^/]+/foo/bar\.png$`);
 
   for (let icon of icons) {
     let extension = ExtensionTestUtils.loadExtension({
       manifest: {
-        "browser_action": { "default_icon": icon },
-        "page_action": { "default_icon": icon },
+        "browser_action": {"default_icon": icon},
+        "page_action": {"default_icon": icon},
       },
 
       background: function() {
-        browser.tabs.query({ active: true, currentWindow: true }, tabs => {
+        browser.tabs.query({active: true, currentWindow: true}, tabs => {
           let tabId = tabs[0].id;
 
           browser.pageAction.show(tabId);
           browser.test.sendMessage("ready");
         });
       }
     });
 
@@ -336,41 +340,40 @@ add_task(function* testSecureURLsDenied(
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "browser_action": {},
       "page_action": {},
     },
 
     background: function() {
-      browser.tabs.query({ active: true, currentWindow: true }, tabs => {
+      browser.tabs.query({active: true, currentWindow: true}, tabs => {
         let tabId = tabs[0].id;
 
         let urls = ["chrome://browser/content/browser.xul",
                     "javascript:true"];
 
+        let promises = [];
         for (let url of urls) {
           for (let api of ["pageAction", "browserAction"]) {
-            try {
-              browser[api].setIcon({tabId, path: url});
-
-              browser.test.fail(`Load of '${url}' succeeded. Expected failure.`);
-              browser.test.notifyFail("setIcon security tests");
-              return;
-            } catch (e) {
-              // We can't actually inspect the error here, since the
-              // error object belongs to the privileged scope of the API,
-              // rather than to the extension scope that calls into it.
-              // Just assume it's the expected security error, for now.
-              browser.test.succeed(`Load of '${url}' failed. Expected failure.`);
-            }
+            promises.push(
+              browser[api].setIcon({tabId, path: url}).then(
+                () => {
+                  browser.test.fail(`Load of '${url}' succeeded. Expected failure.`);
+                  browser.test.notifyFail("setIcon security tests");
+                },
+                error => {
+                  browser.test.succeed(`Load of '${url}' failed. Expected failure. ${error}`);
+                }));
           }
         }
 
-        browser.test.notifyPass("setIcon security tests");
+        Promise.all(promises).then(() => {
+          browser.test.notifyPass("setIcon security tests");
+        });
       });
     },
   });
 
   yield extension.startup();
 
   yield extension.awaitFinish("setIcon security tests");
   yield extension.unload();
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
@@ -25,44 +25,44 @@ function* testInArea(area) {
       },
 
       "data/background.html": `<script src="background.js"></script>`,
 
       "data/background.js": function() {
         let sendClick;
         let tests = [
           () => {
-            sendClick({ expectEvent: false, expectPopup: "a" });
+            sendClick({expectEvent: false, expectPopup: "a"});
           },
           () => {
-            sendClick({ expectEvent: false, expectPopup: "a" });
+            sendClick({expectEvent: false, expectPopup: "a"});
           },
           () => {
-            browser.browserAction.setPopup({ popup: "popup-b.html" });
-            sendClick({ expectEvent: false, expectPopup: "b" });
+            browser.browserAction.setPopup({popup: "popup-b.html"});
+            sendClick({expectEvent: false, expectPopup: "b"});
           },
           () => {
-            sendClick({ expectEvent: false, expectPopup: "b" });
+            sendClick({expectEvent: false, expectPopup: "b"});
           },
           () => {
-            browser.browserAction.setPopup({ popup: "" });
-            sendClick({ expectEvent: true, expectPopup: null });
+            browser.browserAction.setPopup({popup: ""});
+            sendClick({expectEvent: true, expectPopup: null});
           },
           () => {
-            sendClick({ expectEvent: true, expectPopup: null });
+            sendClick({expectEvent: true, expectPopup: null});
           },
           () => {
-            browser.browserAction.setPopup({ popup: "/popup-a.html" });
-            sendClick({ expectEvent: false, expectPopup: "a" });
+            browser.browserAction.setPopup({popup: "/popup-a.html"});
+            sendClick({expectEvent: false, expectPopup: "a"});
           },
         ];
 
         let expect = {};
-        sendClick = ({ expectEvent, expectPopup }) => {
-          expect = { event: expectEvent, popup: expectPopup };
+        sendClick = ({expectEvent, expectPopup}) => {
+          expect = {event: expectEvent, popup: expectPopup};
           browser.test.sendMessage("send-click");
         };
 
         browser.runtime.onMessage.addListener(msg => {
           if (expect.popup) {
             browser.test.assertEq(msg, `from-popup-${expect.popup}`,
                                   "expected popup opened");
           } else {
--- a/browser/components/extensions/test/browser/browser_ext_contentscript_connect.js
+++ b/browser/components/extensions/test/browser/browser_ext_contentscript_connect.js
@@ -38,17 +38,17 @@ add_task(function* () {
 
           port_messages_received++;
           browser.test.assertEq(2, port_messages_received, "2 port messages received");
 
           browser.test.notifyPass("contentscript_connect.pass");
         });
       });
 
-      browser.tabs.executeScript({ file: "script.js" });
+      browser.tabs.executeScript({file: "script.js"});
     },
 
     files: {
       "script.js": function() {
         let port = browser.runtime.connect();
         port.postMessage("port message");
       },
     },
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus.js
@@ -17,54 +17,55 @@ add_task(function* () {
     },
 
     background: function() {
       // A generic onclick callback function.
       function genericOnClick(info) {
         browser.test.sendMessage("menuItemClick", JSON.stringify(info));
       }
 
-      browser.contextMenus.create({ contexts: ["all"], type: "separator" });
+      browser.contextMenus.create({contexts: ["all"], type: "separator"});
 
       let contexts = ["page", "selection", "image"];
       for (let i = 0; i < contexts.length; i++) {
         let context = contexts[i];
         let title = context;
-        browser.contextMenus.create({ title: title, contexts: [context], id: "ext-" + context,
-                                      onclick: genericOnClick });
+        browser.contextMenus.create({title: title, contexts: [context], id: "ext-" + context,
+                                     onclick: genericOnClick});
         if (context == "selection") {
           browser.contextMenus.update("ext-selection", {
             title: "selection is: '%s'",
             onclick: (info) => {
               browser.contextMenus.removeAll();
               genericOnClick(info);
             },
           });
         }
       }
 
-      let parent = browser.contextMenus.create({ title: "parent" });
+      let parent = browser.contextMenus.create({title: "parent"});
       browser.contextMenus.create(
-        { title: "child1", parentId: parent, onclick: genericOnClick });
+        {title: "child1", parentId: parent, onclick: genericOnClick});
       let child2 = browser.contextMenus.create(
-        { title: "child2", parentId: parent, onclick: genericOnClick });
+        {title: "child2", parentId: parent, onclick: genericOnClick});
 
-      let parentToDel = browser.contextMenus.create({ title: "parentToDel" });
+      let parentToDel = browser.contextMenus.create({title: "parentToDel"});
       browser.contextMenus.create(
-        { title: "child1", parentId: parentToDel, onclick: genericOnClick });
+        {title: "child1", parentId: parentToDel, onclick: genericOnClick});
       browser.contextMenus.create(
-        { title: "child2", parentId: parentToDel, onclick: genericOnClick });
+        {title: "child2", parentId: parentToDel, onclick: genericOnClick});
       browser.contextMenus.remove(parentToDel);
 
-      try {
-        browser.contextMenus.update(parent, { parentId: child2 });
-        browser.test.notifyFail();
-      } catch (e) {
-        browser.test.notifyPass();
-      }
+      browser.contextMenus.update(parent, {parentId: child2}).then(
+        () => {
+          browser.test.notifyFail();
+        },
+        () => {
+          browser.test.notifyPass();
+        });
     },
   });
 
   let expectedClickInfo;
   function checkClickInfo(info) {
     info = JSON.parse(info);
     for (let i in expectedClickInfo) {
       is(info[i], expectedClickInfo[i],
@@ -75,17 +76,17 @@ add_task(function* () {
 
   yield extension.startup();
   yield extension.awaitFinish();
 
   // Bring up context menu
   let contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
   let popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
   yield BrowserTestUtils.synthesizeMouseAtCenter("#img1",
-    { type: "contextmenu", button: 2 }, gBrowser.selectedBrowser);
+    {type: "contextmenu", button: 2}, gBrowser.selectedBrowser);
   yield popupShownPromise;
 
   // Check some menu items
   let items = contentAreaContextMenu.getElementsByAttribute("ext-type", "top-level-menu");
   is(items.length, 1, "top level item was found (context=image)");
   let topItem = items.item(0);
   let top = topItem.childNodes[0];
 
@@ -128,17 +129,17 @@ add_task(function* () {
     range.setStart(textNode, 0);
     range.setEnd(textNode, 100);
     selection.addRange(range);
   });
 
   // Bring up context menu again
   popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
   yield BrowserTestUtils.synthesizeMouse(null, 1, 1,
-    { type: "contextmenu", button: 2 }, gBrowser.selectedBrowser);
+    {type: "contextmenu", button: 2}, gBrowser.selectedBrowser);
   yield popupShownPromise;
 
   items = contentAreaContextMenu.getElementsByAttribute("ext-type", "top-level-menu");
   is(items.length, 1, "top level item was found (context=selection)");
   top = items.item(0).childNodes[0];
 
   // Check some menu items
   items = top.getElementsByAttribute("label", "selection is: 'just some text 123456789012345678901234567890...'");
@@ -157,17 +158,17 @@ add_task(function* () {
   top.openPopup(topItem, "end_before", 0, 0, true, false);
   EventUtils.synthesizeMouseAtCenter(selectionItem, {});
   clickInfo = yield extension.awaitMessage("menuItemClick");
   checkClickInfo(clickInfo);
   yield popupHiddenPromise;
 
   popupShownPromise = BrowserTestUtils.waitForEvent(contentAreaContextMenu, "popupshown");
   yield BrowserTestUtils.synthesizeMouseAtCenter("#img1",
-    { type: "contextmenu", button: 2 }, gBrowser.selectedBrowser);
+    {type: "contextmenu", button: 2}, gBrowser.selectedBrowser);
   yield popupShownPromise;
 
   items = contentAreaContextMenu.getElementsByAttribute("ext-type", "top-level-menu");
   is(items.length, 0, "top level item was not found (after removeAll()");
 
   yield extension.unload();
 
   yield BrowserTestUtils.removeTab(tab1);
--- a/browser/components/extensions/test/browser/browser_ext_lastError.js
+++ b/browser/components/extensions/test/browser/browser_ext_lastError.js
@@ -32,17 +32,17 @@ add_task(function* testLastError() {
 
   // Check that we have no unexpected console messages when lastError is
   // checked.
   for (let api of ["extension", "runtime"]) {
     let waitForConsole = new Promise(resolve => {
       SimpleTest.monitorConsole(resolve, [{message: /Invalid extension ID/, forbid: true}]);
     });
 
-    yield sendMessage({ checkLastError: api });
+    yield sendMessage({checkLastError: api});
 
     SimpleTest.endMonitorConsole();
     yield waitForConsole;
   }
 
   // Check that we do have a console message when lastError is not checked.
   let waitForConsole = new Promise(resolve => {
     SimpleTest.monitorConsole(resolve, [{message: /Unchecked lastError value: Error: Invalid extension ID/}]);
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_context.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_context.js
@@ -6,26 +6,26 @@ function* runTests(options) {
   function background(getTests) {
     let tabs;
     let tests;
 
     // Gets the current details of the page action, and returns a
     // promise that resolves to an object containing them.
     function getDetails() {
       return new Promise(resolve => {
-        return browser.tabs.query({ active: true, currentWindow: true }, resolve);
+        return browser.tabs.query({active: true, currentWindow: true}, resolve);
       }).then(([tab]) => {
         let tabId = tab.id;
         browser.test.log(`Get details: tab={id: ${tabId}, url: ${JSON.stringify(tab.url)}}`);
         return Promise.all([
           browser.pageAction.getTitle({tabId}),
           browser.pageAction.getPopup({tabId})]);
       }).then(details => {
-        return Promise.resolve({ title: details[0],
-                                 popup: details[1] });
+        return Promise.resolve({title: details[0],
+                                popup: details[1]});
       });
     }
 
 
     // Runs the next test in the `tests` array, checks the results,
     // and passes control back to the outer test scope.
     function nextTest() {
       let test = tests.shift();
@@ -54,17 +54,17 @@ function* runTests(options) {
         }
       });
     }
 
     function runTests() {
       tabs = [];
       tests = getTests(tabs);
 
-      browser.tabs.query({ active: true, currentWindow: true }, resultTabs => {
+      browser.tabs.query({active: true, currentWindow: true}, resultTabs => {
         tabs[0] = resultTabs[0].id;
 
         nextTest();
       });
     }
 
     browser.test.onMessage.addListener((msg) => {
       if (msg == "runTests") {
@@ -158,28 +158,28 @@ add_task(function* testTabSwitchContext(
         "default_title": "Default Title \u263a",
       },
 
       "permissions": ["tabs"],
     },
 
     getTests(tabs) {
       let details = [
-        { "icon": browser.runtime.getURL("default.png"),
-          "popup": browser.runtime.getURL("default.html"),
-          "title": "Default Title \u263a" },
-        { "icon": browser.runtime.getURL("1.png"),
-          "popup": browser.runtime.getURL("default.html"),
-          "title": "Default Title \u263a" },
-        { "icon": browser.runtime.getURL("2.png"),
-          "popup": browser.runtime.getURL("2.html"),
-          "title": "Title 2" },
-        { "icon": browser.runtime.getURL("2.png"),
-          "popup": browser.runtime.getURL("2.html"),
-          "title": "Default Title \u263a" },
+        {"icon": browser.runtime.getURL("default.png"),
+         "popup": browser.runtime.getURL("default.html"),
+         "title": "Default Title \u263a"},
+        {"icon": browser.runtime.getURL("1.png"),
+         "popup": browser.runtime.getURL("default.html"),
+         "title": "Default Title \u263a"},
+        {"icon": browser.runtime.getURL("2.png"),
+         "popup": browser.runtime.getURL("2.html"),
+         "title": "Title 2"},
+        {"icon": browser.runtime.getURL("2.png"),
+         "popup": browser.runtime.getURL("2.html"),
+         "title": "Default Title \u263a"},
       ];
 
       let promiseTabLoad = details => {
         return new Promise(resolve => {
           browser.tabs.onUpdated.addListener(function listener(tabId, changed) {
             if (tabId == details.id && changed.url == details.url) {
               browser.tabs.onUpdated.removeListener(listener);
               resolve();
@@ -196,75 +196,75 @@ add_task(function* testTabSwitchContext(
         },
         expect => {
           browser.test.log("Show the icon on the first tab, expect default properties.");
           browser.pageAction.show(tabs[0]);
           expect(details[0]);
         },
         expect => {
           browser.test.log("Change the icon. Expect default properties excluding the icon.");
-          browser.pageAction.setIcon({ tabId: tabs[0], path: "1.png" });
+          browser.pageAction.setIcon({tabId: tabs[0], path: "1.png"});
           expect(details[1]);
         },
         expect => {
           browser.test.log("Create a new tab. No icon visible.");
-          browser.tabs.create({ active: true, url: "about:blank?0" }, tab => {
-            tabLoadPromise = promiseTabLoad({ url: "about:blank?0", id: tab.id });
+          browser.tabs.create({active: true, url: "about:blank?0"}, tab => {
+            tabLoadPromise = promiseTabLoad({url: "about:blank?0", id: tab.id});
             tabs.push(tab.id);
             expect(null);
           });
         },
         expect => {
           browser.test.log("Await tab load. No icon visible.");
           tabLoadPromise.then(() => {
             expect(null);
           });
         },
         expect => {
           browser.test.log("Change properties. Expect new properties.");
           let tabId = tabs[1];
           browser.pageAction.show(tabId);
-          browser.pageAction.setIcon({ tabId, path: "2.png" });
-          browser.pageAction.setPopup({ tabId, popup: "2.html" });
-          browser.pageAction.setTitle({ tabId, title: "Title 2" });
+          browser.pageAction.setIcon({tabId, path: "2.png"});
+          browser.pageAction.setPopup({tabId, popup: "2.html"});
+          browser.pageAction.setTitle({tabId, title: "Title 2"});
 
           expect(details[2]);
         },
         expect => {
           browser.test.log("Clear the title. Expect default title.");
-          browser.pageAction.setTitle({ tabId: tabs[1], title: "" });
+          browser.pageAction.setTitle({tabId: tabs[1], title: ""});
 
           expect(details[3]);
         },
         expect => {
           browser.test.log("Navigate to a new page. Expect icon hidden.");
 
           // TODO: This listener should not be necessary, but the |tabs.update|
           // callback currently fires too early in e10s windows.
-          promiseTabLoad({ id: tabs[1], url: "about:blank?1" }).then(() => {
+          promiseTabLoad({id: tabs[1], url: "about:blank?1"}).then(() => {
             expect(null);
           });
 
-          browser.tabs.update(tabs[1], { url: "about:blank?1" });
+          browser.tabs.update(tabs[1], {url: "about:blank?1"});
         },
         expect => {
           browser.test.log("Show the icon. Expect default properties again.");
           browser.pageAction.show(tabs[1]);
           expect(details[0]);
         },
         expect => {
           browser.test.log("Switch back to the first tab. Expect previously set properties.");
-          browser.tabs.update(tabs[0], { active: true }, () => {
+          browser.tabs.update(tabs[0], {active: true}, () => {
             expect(details[1]);
           });
         },
         expect => {
           browser.test.log("Hide the icon on tab 2. Switch back, expect hidden.");
           browser.pageAction.hide(tabs[1]);
-          browser.tabs.update(tabs[1], { active: true }, () => {
+          browser.tabs.update(tabs[1], {active: true}, () => {
             expect(null);
           });
         },
         expect => {
           browser.test.log("Switch back to tab 1. Expect previous results again.");
           browser.tabs.remove(tabs[1], () => {
             expect(details[1]);
           });
@@ -288,40 +288,40 @@ add_task(function* testDefaultTitle() {
         "default_icon": "icon.png",
       },
 
       "permissions": ["tabs"],
     },
 
     getTests(tabs) {
       let details = [
-        { "title": "Foo Extension",
-          "popup": "",
-          "icon": browser.runtime.getURL("icon.png") },
-        { "title": "Foo Title",
-          "popup": "",
-          "icon": browser.runtime.getURL("icon.png") },
+        {"title": "Foo Extension",
+         "popup": "",
+         "icon": browser.runtime.getURL("icon.png")},
+        {"title": "Foo Title",
+         "popup": "",
+         "icon": browser.runtime.getURL("icon.png")},
       ];
 
       return [
         expect => {
           browser.test.log("Initial state. No icon visible.");
           expect(null);
         },
         expect => {
           browser.test.log("Show the icon on the first tab, expect extension title as default title.");
           browser.pageAction.show(tabs[0]);
           expect(details[0]);
         },
         expect => {
           browser.test.log("Change the title. Expect new title.");
-          browser.pageAction.setTitle({ tabId: tabs[0], title: "Foo Title" });
+          browser.pageAction.setTitle({tabId: tabs[0], title: "Foo Title"});
           expect(details[1]);
         },
         expect => {
           browser.test.log("Clear the title. Expect extension title.");
-          browser.pageAction.setTitle({ tabId: tabs[0], title: "" });
+          browser.pageAction.setTitle({tabId: tabs[0], title: ""});
           expect(details[0]);
         },
       ];
     },
   });
 });
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
@@ -29,44 +29,44 @@ add_task(function* testPageActionPopup()
       "data/background.html": scriptPage("background.js"),
 
       "data/background.js": function() {
         let tabId;
 
         let sendClick;
         let tests = [
           () => {
-            sendClick({ expectEvent: false, expectPopup: "a" });
+            sendClick({expectEvent: false, expectPopup: "a"});
           },
           () => {
-            sendClick({ expectEvent: false, expectPopup: "a" });
+            sendClick({expectEvent: false, expectPopup: "a"});
           },
           () => {
-            browser.pageAction.setPopup({ tabId, popup: "popup-b.html" });
-            sendClick({ expectEvent: false, expectPopup: "b" });
+            browser.pageAction.setPopup({tabId, popup: "popup-b.html"});
+            sendClick({expectEvent: false, expectPopup: "b"});
           },
           () => {
-            sendClick({ expectEvent: false, expectPopup: "b" });
+            sendClick({expectEvent: false, expectPopup: "b"});
           },
           () => {
-            browser.pageAction.setPopup({ tabId, popup: "" });
-            sendClick({ expectEvent: true, expectPopup: null });
+            browser.pageAction.setPopup({tabId, popup: ""});
+            sendClick({expectEvent: true, expectPopup: null});
           },
           () => {
-            sendClick({ expectEvent: true, expectPopup: null });
+            sendClick({expectEvent: true, expectPopup: null});
           },
           () => {
-            browser.pageAction.setPopup({ tabId, popup: "/popup-a.html" });
-            sendClick({ expectEvent: false, expectPopup: "a" });
+            browser.pageAction.setPopup({tabId, popup: "/popup-a.html"});
+            sendClick({expectEvent: false, expectPopup: "a"});
           },
         ];
 
         let expect = {};
-        sendClick = ({ expectEvent, expectPopup }) => {
-          expect = { event: expectEvent, popup: expectPopup };
+        sendClick = ({expectEvent, expectPopup}) => {
+          expect = {event: expectEvent, popup: expectPopup};
           browser.test.sendMessage("send-click");
         };
 
         browser.runtime.onMessage.addListener(msg => {
           if (expect.popup) {
             browser.test.assertEq(msg, `from-popup-${expect.popup}`,
                                   "expected popup opened");
           } else {
@@ -96,17 +96,17 @@ add_task(function* testPageActionPopup()
           if (tests.length) {
             let test = tests.shift();
             test();
           } else {
             browser.test.notifyPass("pageaction-tests-done");
           }
         });
 
-        browser.tabs.query({ active: true, currentWindow: true }, tabs => {
+        browser.tabs.query({active: true, currentWindow: true}, tabs => {
           tabId = tabs[0].id;
 
           browser.pageAction.show(tabId);
           browser.test.sendMessage("next-test");
         });
       },
     },
   });
@@ -160,17 +160,17 @@ add_task(function* testPageActionSecurit
       // if we don't call it.
       SimpleTest.waitForExplicitFinish();
 
       SimpleTest.monitorConsole(resolve, messages);
     });
 
     let extension = ExtensionTestUtils.loadExtension({
       manifest: {
-        [api]: { "default_popup": URL },
+        [api]: {"default_popup": URL},
       },
     });
 
     yield Assert.rejects(extension.startup(),
                          null,
                          "Manifest rejected");
 
     SimpleTest.endMonitorConsole();
--- a/browser/components/extensions/test/browser/browser_ext_popup_api_injection.js
+++ b/browser/components/extensions/test/browser/browser_ext_popup_api_injection.js
@@ -22,25 +22,25 @@ add_task(function* testPageActionPopup()
 
       "popup-b.html": String.raw`<html><head><meta charset="utf-8"><script type="application/javascript">
         browser.test.sendMessage("from-popup-b");
       </script></head></html>`,
     },
 
     background: function() {
       let tabId;
-      browser.tabs.query({ active: true, currentWindow: true }, tabs => {
+      browser.tabs.query({active: true, currentWindow: true}, tabs => {
         tabId = tabs[0].id;
         browser.pageAction.show(tabId);
         browser.test.sendMessage("ready");
       });
 
       browser.test.onMessage.addListener(() => {
-        browser.browserAction.setPopup({ popup: "/popup-a.html" });
-        browser.pageAction.setPopup({ tabId, popup: "popup-b.html" });
+        browser.browserAction.setPopup({popup: "/popup-a.html"});
+        browser.pageAction.setPopup({tabId, popup: "popup-b.html"});
 
         browser.test.sendMessage("ok");
       });
     },
   });
 
   let promiseConsoleMessage = pattern => new Promise(resolve => {
     Services.console.registerListener(function listener(msg) {
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_runtime_setUninstallURL.js
@@ -0,0 +1,114 @@
+"use strict";
+
+let {AddonManager} = Components.utils.import("resource://gre/modules/AddonManager.jsm", {});
+let {Extension} = Components.utils.import("resource://gre/modules/Extension.jsm", {});
+
+function install(url) {
+  return new Promise((resolve, reject) => {
+    AddonManager.getInstallForURL(url, (install) => {
+      install.addListener({
+        onInstallEnded: (i, addon) => resolve(addon),
+        onInstallFailed: () => reject(),
+      });
+      install.install();
+    }, "application/x-xpinstall");
+  });
+}
+
+function* makeAndInstallXPI(id, backgroundScript, loadedURL) {
+  let xpi = Extension.generateXPI(id, {
+    background: "(" + backgroundScript.toString() + ")()",
+  });
+  SimpleTest.registerCleanupFunction(function cleanupXPI() {
+    Services.obs.notifyObservers(xpi, "flush-cache-entry", null);
+    xpi.remove(false);
+  });
+
+  let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, loadedURL);
+
+  let fileURI = Services.io.newFileURI(xpi);
+  info(`installing ${fileURI.spec}`);
+  let addon = yield install(fileURI.spec);
+  info("installed");
+
+  // A WebExtension is started asynchronously, we have our test extension
+  // open a new tab to signal that the background script has executed.
+  let loadTab = yield loadPromise;
+  yield BrowserTestUtils.removeTab(loadTab);
+
+  return addon;
+}
+
+
+add_task(function* test_setuninstallurl_badargs() {
+  function backgroundScript() {
+    let promises = [
+      browser.runtime.setUninstallURL("this is not a url")
+        .then(() => {
+          browser.test.notifyFail("setUninstallURL should have failed with bad url");
+        })
+        .catch(error => {
+          browser.test.assertTrue(/Invalid URL/.test(error.message), "error message indicates malformed url");
+        }),
+
+      browser.runtime.setUninstallURL("file:///etc/passwd")
+        .then(() => {
+          browser.test.notifyFail("setUninstallURL should have failed with non-http[s] url");
+        })
+        .catch(error => {
+          browser.test.assertTrue(/must have the scheme http or https/.test(error.message), "error message indicates bad scheme");
+        }),
+    ];
+
+    Promise.all(promises)
+      .then(() => browser.test.notifyPass("setUninstallURL bad params"));
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    background: "(" + backgroundScript.toString() + ")()",
+  });
+  yield extension.startup();
+  yield extension.awaitFinish();
+  yield extension.unload();
+});
+
+// Test the documented behavior of setUninstallURL() that passing an
+// empty string is equivalent to not setting an uninstall URL
+// (i.e., no new tab is opened upon uninstall)
+add_task(function* test_setuninstall_empty_url() {
+  function backgroundScript() {
+    browser.runtime.setUninstallURL("")
+      .then(() => browser.tabs.create({url: "http://example.com/addon_loaded"}));
+  }
+
+  let addon = yield makeAndInstallXPI("test_uinstallurl2@tests.mozilla.org",
+                                      backgroundScript,
+                                      "http://example.com/addon_loaded");
+
+  addon.uninstall(true);
+  info("uninstalled");
+
+  // no need to explicitly check for the absence of a new tab,
+  // BrowserTestUtils will eventually complain if one is opened.
+});
+
+add_task(function* test_setuninstallurl() {
+  function backgroundScript() {
+    browser.runtime.setUninstallURL("http://example.com/addon_uninstalled")
+      .then(() => browser.tabs.create({url: "http://example.com/addon_loaded"}));
+  }
+
+  let addon = yield makeAndInstallXPI("test_uinstallurl@tests.mozilla.org",
+                                      backgroundScript,
+                                      "http://example.com/addon_loaded");
+
+  // look for a new tab with the uninstall url.
+  let uninstallPromise = BrowserTestUtils.waitForNewTab(gBrowser, "http://example.com/addon_uninstalled");
+
+  addon.uninstall(true);
+  info("uninstalled");
+
+  let uninstalledTab = yield uninstallPromise;
+  isnot(uninstalledTab, null, "opened tab with uninstall url");
+  yield BrowserTestUtils.removeTab(uninstalledTab);
+});
--- a/browser/components/extensions/test/browser/browser_ext_tab_runtimeConnect.js
+++ b/browser/components/extensions/test/browser/browser_ext_tab_runtimeConnect.js
@@ -33,26 +33,26 @@ add_task(function* () {
             browser.test.assertTrue(!!msg.tabReceived, "'background to tab' reply port message received");
             browser.test.assertEq("background to tab port message", msg.tabReceived, "reply port content contains the message received");
 
             browser.test.notifyPass("tabRuntimeConnect.pass");
           }
         });
       });
 
-      browser.tabs.create({ url: "tab.html" },
+      browser.tabs.create({url: "tab.html"},
                           (tab) => { tabId = tab.id; });
     },
 
     files: {
       "tab.js": function() {
-        let port = browser.runtime.connect({ name: "tab-connection-name"});
+        let port = browser.runtime.connect({name: "tab-connection-name"});
         port.postMessage("tab to background port message");
         port.onMessage.addListener((msg) => {
-          port.postMessage({ tabReceived: msg });
+          port.postMessage({tabReceived: msg});
         });
       },
       "tab.html": `
         <!DOCTYPE html>
         <html>
           <head>
             <title>test tab extension page</title>
             <meta charset="utf-8">
--- a/browser/components/extensions/test/browser/browser_ext_tabs_audio.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_audio.js
@@ -44,36 +44,36 @@ add_task(function* () {
         deferred[tabId] = {resolve, reject};
         browser.test.sendMessage("change-tab", tabId, attr, on);
       });
     }
 
 
     let windowId;
     let tabIds;
-    promiseTabs.query({ lastFocusedWindow: true }).then(tabs => {
+    promiseTabs.query({lastFocusedWindow: true}).then(tabs => {
       browser.test.assertEq(tabs.length, 3, "We have three tabs");
 
       for (let tab of tabs) {
         // Note: We want to check that these are actual boolean values, not
         // just that they evaluate as false.
         browser.test.assertEq(false, tab.mutedInfo.muted, "Tab is not muted");
         browser.test.assertEq(undefined, tab.mutedInfo.reason, "Tab has no muted info reason");
         browser.test.assertEq(false, tab.audible, "Tab is not audible");
       }
 
       windowId = tabs[0].windowId;
       tabIds = [tabs[1].id, tabs[2].id];
 
       browser.test.log("Test initial queries for muted and audible return no tabs");
       return Promise.all([
-        promiseTabs.query({ windowId, audible: false }),
-        promiseTabs.query({ windowId, audible: true }),
-        promiseTabs.query({ windowId, muted: true }),
-        promiseTabs.query({ windowId, muted: false }),
+        promiseTabs.query({windowId, audible: false}),
+        promiseTabs.query({windowId, audible: true}),
+        promiseTabs.query({windowId, muted: true}),
+        promiseTabs.query({windowId, muted: false}),
       ]);
     }).then(([silent, audible, muted, nonMuted]) => {
       browser.test.assertEq(3, silent.length, "Three silent tabs");
       browser.test.assertEq(0, audible.length, "No audible tabs");
 
       browser.test.assertEq(0, muted.length, "No muted tabs");
       browser.test.assertEq(3, nonMuted.length, "Three non-muted tabs");
 
@@ -90,20 +90,20 @@ add_task(function* () {
         browser.test.assertEq("user", obj.mutedInfo.reason, "Tab was muted by the user");
       }
 
       browser.test.assertEq(true, audible.changeInfo.audible, "Tab audible state changed");
       browser.test.assertEq(true, audible.tab.audible, "Tab is audible");
 
       browser.test.log("Re-check queries. Expect one audible and one muted tab");
       return Promise.all([
-        promiseTabs.query({ windowId, audible: false }),
-        promiseTabs.query({ windowId, audible: true }),
-        promiseTabs.query({ windowId, muted: true }),
-        promiseTabs.query({ windowId, muted: false }),
+        promiseTabs.query({windowId, audible: false}),
+        promiseTabs.query({windowId, audible: true}),
+        promiseTabs.query({windowId, muted: true}),
+        promiseTabs.query({windowId, muted: false}),
       ]);
     }).then(([silent, audible, muted, nonMuted]) => {
       browser.test.assertEq(2, silent.length, "Two silent tabs");
       browser.test.assertEq(1, audible.length, "One audible tab");
 
       browser.test.assertEq(1, muted.length, "One muted tab");
       browser.test.assertEq(2, nonMuted.length, "Two non-muted tabs");
 
@@ -111,18 +111,18 @@ add_task(function* () {
       browser.test.assertEq("user", muted[0].mutedInfo.reason, "Tab was muted by the user");
 
       browser.test.assertEq(true, audible[0].audible, "Tab is audible");
 
       browser.test.log("Toggle muted internally on two tabs, and check results");
       return Promise.all([
         promiseUpdated(tabIds[0], "mutedInfo"),
         promiseUpdated(tabIds[1], "mutedInfo"),
-        promiseTabs.update(tabIds[0], { muted: false }),
-        promiseTabs.update(tabIds[1], { muted: true }),
+        promiseTabs.update(tabIds[0], {muted: false}),
+        promiseTabs.update(tabIds[1], {muted: true}),
       ]);
     }).then(([unmuted, muted]) => {
       for (let obj of [unmuted.changeInfo, unmuted.tab]) {
         browser.test.assertEq(false, obj.mutedInfo.muted, "Tab is not muted");
       }
       for (let obj of [muted.changeInfo, muted.tab]) {
         browser.test.assertEq(true, obj.mutedInfo.muted, "Tab is muted");
       }
--- a/browser/components/extensions/test/browser/browser_ext_tabs_captureVisibleTab.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_captureVisibleTab.js
@@ -34,38 +34,38 @@ function* runTest(options) {
         return new Promise(resolve => {
           browser.tabs[method](...args, resolve);
         });
       };
     });
 
     browser.test.log(`Test color ${options.color} at fullZoom=${options.fullZoom}`);
 
-    promiseTabs.query({ currentWindow: true, active: true }).then(([tab]) => {
+    promiseTabs.query({currentWindow: true, active: true}).then(([tab]) => {
       return Promise.all([
-        promiseTabs.captureVisibleTab(tab.windowId, { format: "jpeg", quality: 95 }),
-        promiseTabs.captureVisibleTab(tab.windowId, { format: "png", quality: 95 }),
-        promiseTabs.captureVisibleTab(tab.windowId, { quality: 95 }),
+        promiseTabs.captureVisibleTab(tab.windowId, {format: "jpeg", quality: 95}),
+        promiseTabs.captureVisibleTab(tab.windowId, {format: "png", quality: 95}),
+        promiseTabs.captureVisibleTab(tab.windowId, {quality: 95}),
         promiseTabs.captureVisibleTab(tab.windowId),
       ]).then(([jpeg, png, ...pngs]) => {
         browser.test.assertTrue(pngs.every(url => url == png), "All PNGs are identical");
 
         browser.test.assertTrue(jpeg.startsWith("data:image/jpeg;base64,"), "jpeg is JPEG");
         browser.test.assertTrue(png.startsWith("data:image/png;base64,"), "png is PNG");
 
         let promises = [jpeg, png].map(url => new Promise(resolve => {
           let img = new Image();
           img.src = url;
           img.onload = () => resolve(img);
         }));
         return Promise.all(promises);
       }).then(([jpeg, png]) => {
         let tabDims = `${tab.width}\u00d7${tab.height}`;
 
-        let images = { jpeg, png };
+        let images = {jpeg, png};
         for (let format of Object.keys(images)) {
           let img = images[format];
 
           let dims = `${img.width}\u00d7${img.height}`;
           browser.test.assertEq(tabDims, dims, `${format} dimensions are correct`);
 
           let canvas = document.createElement("canvas");
           canvas.width = img.width;
@@ -73,27 +73,27 @@ function* runTest(options) {
           canvas.mozOpaque = true;
 
           let ctx = canvas.getContext("2d");
           ctx.drawImage(img, 0, 0);
 
           // Check the colors of the first and last pixels of the image, to make
           // sure we capture the entire frame, and scale it correctly.
           let coords = [
-            { x: 0, y: 0,
-              color: options.color },
-            { x: img.width - 1,
-              y: img.height - 1,
-              color: options.color },
-            { x: img.width / 2 | 0,
-              y: img.height / 2 | 0,
-              color: options.neutral },
+            {x: 0, y: 0,
+             color: options.color},
+            {x: img.width - 1,
+             y: img.height - 1,
+             color: options.color},
+            {x: img.width / 2 | 0,
+             y: img.height / 2 | 0,
+             color: options.neutral},
           ];
 
-          for (let { x, y, color } of coords) {
+          for (let {x, y, color} of coords) {
             let imageData = ctx.getImageData(x, y, 1, 1).data;
 
             if (format == "png") {
               browser.test.assertEq(`rgba(${color},255)`, `rgba(${[...imageData]})`, `${format} image color is correct at (${x}, ${y})`);
             } else {
               // Allow for some deviation in JPEG version due to lossy compression.
               const SLOP = 2;
 
@@ -128,33 +128,33 @@ function* runTest(options) {
   yield extension.awaitFinish("captureVisibleTab");
 
   yield extension.unload();
 
   yield BrowserTestUtils.removeTab(tab);
 }
 
 add_task(function* testCaptureVisibleTab() {
-  yield runTest({ color: [0, 0, 0], fullZoom: 1 });
+  yield runTest({color: [0, 0, 0], fullZoom: 1});
 
-  yield runTest({ color: [0, 0, 0], fullZoom: 2 });
+  yield runTest({color: [0, 0, 0], fullZoom: 2});
 
-  yield runTest({ color: [0, 0, 0], fullZoom: 0.5 });
+  yield runTest({color: [0, 0, 0], fullZoom: 0.5});
 
-  yield runTest({ color: [255, 255, 255], fullZoom: 1 });
+  yield runTest({color: [255, 255, 255], fullZoom: 1});
 });
 
 add_task(function* testCaptureVisibleTabPermissions() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
     },
 
     background: function(x) {
-      browser.tabs.query({ currentWindow: true, active: true }, tab => {
+      browser.tabs.query({currentWindow: true, active: true}, tab => {
         browser.tabs.captureVisibleTab(tab.windowId).then(
           () => {
             browser.test.notifyFail("captureVisibleTabPermissions");
           },
           (e) => {
             browser.test.assertEq("The <all_urls> permission is required to use the captureVisibleTab API",
                                   e.message, "Expected permissions error message");
             browser.test.notifyPass("captureVisibleTabPermissions");
--- a/browser/components/extensions/test/browser/browser_ext_tabs_create.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_create.js
@@ -14,17 +14,17 @@ add_task(function* () {
   registerCleanupFunction(() => {
     SpecialPowers.clearUserPref("browser.newtab.preload");
   });
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
 
-      "background": { "page": "bg/background.html" },
+      "background": {"page": "bg/background.html"},
     },
 
     files: {
       "bg/blank.html": `<html><head><meta charset="utf-8"></head></html>`,
 
       "bg/background.html": `<html><head>
         <meta charset="utf-8">
         <script src="background.js"></script>
@@ -40,58 +40,58 @@ add_task(function* () {
             windowId: activeWindow,
             active: true,
             pinned: false,
             url: "about:newtab",
           };
 
           let tests = [
             {
-              create: { url: "http://example.com/" },
-              result: { url: "http://example.com/" },
+              create: {url: "http://example.com/"},
+              result: {url: "http://example.com/"},
             },
             {
-              create: { url: "blank.html" },
-              result: { url: browser.runtime.getURL("bg/blank.html") },
+              create: {url: "blank.html"},
+              result: {url: browser.runtime.getURL("bg/blank.html")},
             },
             {
               create: {},
-              result: { url: "about:newtab" },
+              result: {url: "about:newtab"},
             },
             {
-              create: { active: false },
-              result: { active: false },
+              create: {active: false},
+              result: {active: false},
             },
             {
-              create: { active: true },
-              result: { active: true },
+              create: {active: true},
+              result: {active: true},
             },
             {
-              create: { pinned: true },
-              result: { pinned: true, index: 0 },
+              create: {pinned: true},
+              result: {pinned: true, index: 0},
             },
             {
-              create: { pinned: true, active: true },
-              result: { pinned: true, active: true, index: 0 },
+              create: {pinned: true, active: true},
+              result: {pinned: true, active: true, index: 0},
             },
             {
-              create: { pinned: true, active: false },
-              result: { pinned: true, active: false, index: 0 },
+              create: {pinned: true, active: false},
+              result: {pinned: true, active: false, index: 0},
             },
             {
-              create: { index: 1 },
-              result: { index: 1 },
+              create: {index: 1},
+              result: {index: 1},
             },
             {
-              create: { index: 1, active: false },
-              result: { index: 1, active: false },
+              create: {index: 1, active: false},
+              result: {index: 1, active: false},
             },
             {
-              create: { windowId: activeWindow },
-              result: { windowId: activeWindow },
+              create: {windowId: activeWindow},
+              result: {windowId: activeWindow},
             },
           ];
 
           function nextTest() {
             if (!tests.length) {
               browser.test.notifyPass("tabs.create");
               return;
             }
@@ -136,26 +136,26 @@ add_task(function* () {
               }
 
               return updatedPromise;
             }).then(url => {
               browser.test.assertEq(expected.url, url, `Expected value for tab.url`);
 
               return browser.tabs.remove(tabId);
             }).then(() => {
-              return browser.tabs.update(activeTab, { active: true });
+              return browser.tabs.update(activeTab, {active: true});
             }).then(() => {
               nextTest();
             });
           }
 
           nextTest();
         }
 
-        browser.tabs.query({ active: true, currentWindow: true }, tabs => {
+        browser.tabs.query({active: true, currentWindow: true}, tabs => {
           activeTab = tabs[0].id;
           activeWindow = tabs[0].windowId;
 
           runTests();
         });
       },
     },
   });
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
@@ -9,17 +9,17 @@ function* testHasNoPermission(params) {
     browser.runtime.onMessage.addListener((msg, sender) => {
       browser.test.assertEq(msg, "second script ran", "second script ran");
       browser.test.notifyPass("executeScript");
     });
 
     browser.test.onMessage.addListener(msg => {
       browser.test.assertEq(msg, "execute-script");
 
-      browser.tabs.query({ currentWindow: true }, tabs => {
+      browser.tabs.query({currentWindow: true}, tabs => {
         browser.tabs.executeScript({
           file: "script.js",
         });
 
         // Execute a script we know we have permissions for in the
         // second tab, in the hopes that it will execute after the
         // first one. This has intermittent failure written all over
         // it, but it's just about the best we can do until we
@@ -65,22 +65,22 @@ function* testHasNoPermission(params) {
 }
 
 add_task(function* testBadPermissions() {
   let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
   let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
 
   info("Test no special permissions");
   yield testHasNoPermission({
-    manifest: { "permissions": ["http://example.com/"] },
+    manifest: {"permissions": ["http://example.com/"]},
   });
 
   info("Test tabs permissions");
   yield testHasNoPermission({
-    manifest: { "permissions": ["http://example.com/", "tabs"] },
+    manifest: {"permissions": ["http://example.com/", "tabs"]},
   });
 
   info("Test active tab, browser action, no click");
   yield testHasNoPermission({
     manifest: {
       "permissions": ["http://example.com/", "activeTab"],
       "browser_action": {},
     },
@@ -89,31 +89,31 @@ add_task(function* testBadPermissions() 
   info("Test active tab, page action, no click");
   yield testHasNoPermission({
     manifest: {
       "permissions": ["http://example.com/", "activeTab"],
       "page_action": {},
     },
     contentSetup() {
       return new Promise(resolve => {
-        browser.tabs.query({ active: true, currentWindow: true }, tabs => {
+        browser.tabs.query({active: true, currentWindow: true}, tabs => {
           browser.pageAction.show(tabs[0].id);
           resolve();
         });
       });
     },
   });
 
   yield BrowserTestUtils.removeTab(tab2);
   yield BrowserTestUtils.removeTab(tab1);
 });
 
 add_task(function* testBadURL() {
   function background() {
-    browser.tabs.query({ currentWindow: true }, tabs => {
+    browser.tabs.query({currentWindow: true}, tabs => {
       let promises = [
         new Promise(resolve => {
           browser.tabs.executeScript({
             file: "http://example.com/script.js",
           }, result => {
             browser.test.assertEq(undefined, result, "Result value");
 
             browser.test.assertTrue(browser.extension.lastError instanceof Error,
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js
@@ -56,27 +56,27 @@ function* testHasPermission(params) {
   yield extension.unload();
 }
 
 add_task(function* testGoodPermissions() {
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/", true);
 
   info("Test explicit host permission");
   yield testHasPermission({
-    manifest: { "permissions": ["http://mochi.test/"] },
+    manifest: {"permissions": ["http://mochi.test/"]},
   });
 
   info("Test explicit host subdomain permission");
   yield testHasPermission({
-    manifest: { "permissions": ["http://*.mochi.test/"] },
+    manifest: {"permissions": ["http://*.mochi.test/"]},
   });
 
   info("Test explicit <all_urls> permission");
   yield testHasPermission({
-    manifest: { "permissions": ["<all_urls>"] },
+    manifest: {"permissions": ["<all_urls>"]},
   });
 
   info("Test activeTab permission with a browser action click");
   yield testHasPermission({
     manifest: {
       "permissions": ["activeTab"],
       "browser_action": {},
     },
@@ -93,69 +93,69 @@ add_task(function* testGoodPermissions()
   info("Test activeTab permission with a page action click");
   yield testHasPermission({
     manifest: {
       "permissions": ["activeTab"],
       "page_action": {},
     },
     contentSetup() {
       return new Promise(resolve => {
-        browser.tabs.query({ active: true, currentWindow: true }, tabs => {
+        browser.tabs.query({active: true, currentWindow: true}, tabs => {
           browser.pageAction.show(tabs[0].id);
           resolve();
         });
       });
     },
     setup: clickPageAction,
     tearDown: closePageAction,
   });
 
   info("Test activeTab permission with a browser action w/popup click");
   yield testHasPermission({
     manifest: {
       "permissions": ["activeTab"],
-      "browser_action": { "default_popup": "_blank.html" },
+      "browser_action": {"default_popup": "_blank.html"},
     },
     setup: clickBrowserAction,
     tearDown: closeBrowserAction,
   });
 
   info("Test activeTab permission with a page action w/popup click");
   yield testHasPermission({
     manifest: {
       "permissions": ["activeTab"],
-      "page_action": { "default_popup": "_blank.html" },
+      "page_action": {"default_popup": "_blank.html"},
     },
     contentSetup() {
       return new Promise(resolve => {
-        browser.tabs.query({ active: true, currentWindow: true }, tabs => {
+        browser.tabs.query({active: true, currentWindow: true}, tabs => {
           browser.pageAction.show(tabs[0].id);
           resolve();
         });
       });
     },
     setup: clickPageAction,
     tearDown: closePageAction,
   });
 
   info("Test activeTab permission with a context menu click");
   yield testHasPermission({
     manifest: {
       "permissions": ["activeTab", "contextMenus"],
     },
     contentSetup() {
-      browser.contextMenus.create({ title: "activeTab", contexts: ["all"] });
+      browser.contextMenus.create({title: "activeTab", contexts: ["all"]});
       return Promise.resolve();
     },
     setup: function* (extension) {
       let contextMenu = document.getElementById("contentAreaContextMenu");
       let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
       let awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
 
-      yield BrowserTestUtils.synthesizeMouseAtCenter("a[href]", { type: "contextmenu", button: 2 },
+      yield BrowserTestUtils.synthesizeMouseAtCenter("a[href]", {type: "contextmenu", button: 2},
                                                      gBrowser.selectedBrowser);
       yield awaitPopupShown;
 
       let item = contextMenu.querySelector("[label=activeTab]");
 
       yield EventUtils.synthesizeMouseAtCenter(item, {}, window);
 
       yield awaitPopupHidden;
--- a/browser/components/extensions/test/browser/browser_ext_tabs_getCurrent.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_getCurrent.js
@@ -2,41 +2,41 @@
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 add_task(function* () {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
 
-      "browser_action": { "default_popup": "popup.html" },
+      "browser_action": {"default_popup": "popup.html"},
     },
 
     files: {
       "tab.js": function() {
         let url = document.location.href;
 
         browser.tabs.getCurrent(currentTab => {
           browser.test.assertEq(currentTab.url, url, "getCurrent in non-active background tab");
 
           // Activate the tab.
-          browser.tabs.onActivated.addListener(function listener({ tabId }) {
+          browser.tabs.onActivated.addListener(function listener({tabId}) {
             if (tabId == currentTab.id) {
               browser.tabs.onActivated.removeListener(listener);
 
               browser.tabs.getCurrent(currentTab => {
                 browser.test.assertEq(currentTab.id, tabId, "in active background tab");
                 browser.test.assertEq(currentTab.url, url, "getCurrent in non-active background tab");
 
                 browser.test.sendMessage("tab-finished");
                 browser.tabs.remove(tabId);
               });
             }
           });
-          browser.tabs.update(currentTab.id, { active: true });
+          browser.tabs.update(currentTab.id, {active: true});
         });
       },
 
       "popup.js": function() {
         browser.tabs.getCurrent(tab => {
           browser.test.assertEq(tab, undefined, "getCurrent in popup script");
           browser.test.sendMessage("popup-finished");
         });
@@ -47,17 +47,17 @@ add_task(function* () {
     },
 
     background: function() {
       browser.tabs.getCurrent(tab => {
         browser.test.assertEq(tab, undefined, "getCurrent in background script");
         browser.test.sendMessage("background-finished");
       });
 
-      browser.tabs.create({ url: "tab.html", active: false });
+      browser.tabs.create({url: "tab.html", active: false});
     },
   });
 
   yield extension.startup();
 
   yield extension.awaitMessage("background-finished");
   yield extension.awaitMessage("tab-finished");
 
--- a/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js
@@ -57,17 +57,17 @@ add_task(function* testExecuteScript() {
     }
 
     function next() {
       if (!promises.length) {
         browser.test.notifyPass("insertCSS");
         return;
       }
 
-      let { promise, background, foreground } = promises.shift();
+      let {promise, background, foreground} = promises.shift();
       new Promise(promise).then(() => {
         browser.tabs.executeScript({
           code: `(${checkCSS})()`,
         }, result => {
           browser.test.assertEq(background, result[0], "Expected background color");
           browser.test.assertEq(foreground, result[1], "Expected foreground color");
           next();
         });
--- a/browser/components/extensions/test/browser/browser_ext_tabs_move.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_move.js
@@ -15,17 +15,17 @@ add_task(function* () {
 
     background: function() {
       browser.tabs.query({
         lastFocusedWindow: true,
       }, function(tabs) {
         let tab = tabs[0];
         browser.tabs.move(tab.id, {index: 0});
         browser.tabs.query(
-          { lastFocusedWindow: true },
+          {lastFocusedWindow: true},
           tabs => {
             browser.test.assertEq(tabs[0].url, tab.url, "should be first tab");
             browser.test.notifyPass("tabs.move.single");
           });
       });
     },
   });
 
@@ -35,22 +35,22 @@ add_task(function* () {
 
   extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
     },
 
     background: function() {
       browser.tabs.query(
-        { lastFocusedWindow: true },
+        {lastFocusedWindow: true},
         tabs => {
           tabs.sort(function(a, b) { return a.url > b.url; });
           browser.tabs.move(tabs.map(tab => tab.id), {index: 0});
           browser.tabs.query(
-            { lastFocusedWindow: true },
+            {lastFocusedWindow: true},
             tabs => {
               browser.test.assertEq(tabs[0].url, "about:blank", "should be first tab");
               browser.test.assertEq(tabs[1].url, "about:config", "should be second tab");
               browser.test.assertEq(tabs[2].url, "about:robots", "should be third tab");
               browser.test.notifyPass("tabs.move.multiple");
             });
         });
     },
@@ -62,23 +62,23 @@ add_task(function* () {
 
   extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
     },
 
     background: function() {
       browser.tabs.query(
-        { lastFocusedWindow: true },
+        {lastFocusedWindow: true},
         tabs => {
           let tab = tabs[0];
           // Assuming that tab.id of 12345 does not exist.
           browser.tabs.move([12345, tab.id], {index: 0});
           browser.tabs.query(
-            { lastFocusedWindow: true },
+            {lastFocusedWindow: true},
             tabs => {
               browser.test.assertEq(tabs[0].url, tab.url, "should be first tab");
               browser.test.notifyPass("tabs.move.invalid");
             });
         });
     },
   });
 
@@ -88,22 +88,22 @@ add_task(function* () {
 
   extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
     },
 
     background: function() {
       browser.tabs.query(
-        { lastFocusedWindow: true },
+        {lastFocusedWindow: true},
         tabs => {
           let tab = tabs[0];
           browser.tabs.move(tab.id, {index: -1});
           browser.tabs.query(
-            { lastFocusedWindow: true },
+            {lastFocusedWindow: true},
             tabs => {
               browser.test.assertEq(tabs[2].url, tab.url, "should be last tab");
               browser.test.notifyPass("tabs.move.last");
             });
         });
     },
   });
 
--- a/browser/components/extensions/test/browser/browser_ext_tabs_move_window.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_move_window.js
@@ -16,17 +16,17 @@ add_task(function* () {
       browser.tabs.query({
         url: "<all_urls>",
       }, function(tabs) {
         let destination = tabs[0];
         let source = tabs[1]; // skip over about:blank in window1
         browser.tabs.move(source.id, {windowId: destination.windowId, index: 0});
 
         browser.tabs.query(
-          { url: "<all_urls>" },
+          {url: "<all_urls>"},
           tabs => {
             browser.test.assertEq(tabs[0].url, "http://example.com/");
             browser.test.assertEq(tabs[0].windowId, destination.windowId);
             browser.test.notifyPass("tabs.move.window");
           });
       });
     },
   });
@@ -49,24 +49,24 @@ add_task(function* () {
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
     },
 
     background: function() {
       browser.tabs.query(
-        { url: "<all_urls>" },
+        {url: "<all_urls>"},
         tabs => {
           let destination = tabs[0];
           let source = tabs[1]; // remember, pinning moves it to the left.
           browser.tabs.move(source.id, {windowId: destination.windowId, index: 0});
 
           browser.tabs.query(
-            { url: "<all_urls>" },
+            {url: "<all_urls>"},
             tabs => {
               browser.test.assertEq(true, tabs[0].pinned);
               browser.test.notifyPass("tabs.move.pin");
             });
         });
     },
   });
 
@@ -89,23 +89,23 @@ add_task(function* () {
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
     },
 
     background: function() {
       browser.tabs.query(
-        { url: "<all_urls>" },
+        {url: "<all_urls>"},
         tabs => {
           let move1 = tabs[1];
           let move3 = tabs[3];
           browser.tabs.move([move1.id, move3.id], {index: 0});
           browser.tabs.query(
-            { url: "<all_urls>" },
+            {url: "<all_urls>"},
             tabs => {
               browser.test.assertEq(tabs[0].url, move1.url);
               browser.test.assertEq(tabs[2].url, move3.url);
               browser.test.notifyPass("tabs.move.multiple");
             });
         });
     },
   });
--- a/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js
@@ -18,19 +18,19 @@ add_task(function* () {
         "run_at": "document_start",
       }],
     },
 
     background: function() {
       let pageURL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context_tabs_onUpdated_page.html";
 
       let expectedSequence = [
-        { status: "loading" },
-        { status: "loading", url: pageURL },
-        { status: "complete" },
+        {status: "loading"},
+        {status: "loading", url: pageURL},
+        {status: "complete"},
       ];
       let collectedSequence = [];
 
       browser.tabs.onUpdated.addListener(function(tabId, updatedInfo) {
         collectedSequence.push(updatedInfo);
       });
 
       browser.runtime.onMessage.addListener(function() {
@@ -55,17 +55,17 @@ add_task(function* () {
               );
             }
           }
         }
 
         browser.test.notifyPass("tabs.onUpdated");
       });
 
-      browser.tabs.create({ url: pageURL });
+      browser.tabs.create({url: pageURL});
     },
     files: {
       "content-script.js": `
         window.addEventListener("message", function(evt) {
           if (evt.data == "frame-updated") {
             browser.runtime.sendMessage("load-completed");
           }
         }, true);
--- a/browser/components/extensions/test/browser/browser_ext_tabs_sendMessage.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_sendMessage.js
@@ -4,17 +4,17 @@
 
 add_task(function* tabsSendMessageNoExceptionOnNonExistentTab() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
     },
 
     background: function() {
-      browser.tabs.create({ url: "about:robots"}, tab => {
+      browser.tabs.create({url: "about:robots"}, tab => {
         let exception;
         try {
           browser.tabs.sendMessage(tab.id, "message");
           browser.tabs.sendMessage(tab.id + 100, "message");
         } catch (e) {
           exception = e;
         }
 
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_getFrames.js
@@ -0,0 +1,175 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(function* testWebNavigationGetNonExistentTab() {
+  let extension = ExtensionTestUtils.loadExtension({
+    background: "(" + function() {
+      let results = [
+        // There is no "tabId = 0" because the id assigned by TabManager (defined in ext-utils.js)
+        // starts from 1.
+        browser.webNavigation.getAllFrames({tabId: 0}).then(() => {
<