diff --git a/accessible/tests/mochitest/relations/test_embeds.xul b/accessible/tests/mochitest/relations/test_embeds.xul index f6d1ba83633..f66b1f4daad 100644 --- a/accessible/tests/mochitest/relations/test_embeds.xul +++ b/accessible/tests/mochitest/relations/test_embeds.xul @@ -99,7 +99,7 @@ //////////////////////////////////////////////////////////////////////////// // Testing - //gA11yEventDumpToConsole = true; // debug + gA11yEventDumpToConsole = true; // debug var gQueue = null; function doTests() @@ -107,7 +107,7 @@ testRelation(browserDocument(), RELATION_EMBEDS, getAccessible(currentTabDocument())); - //enableLogging("docload"); + enableLogging("docload"); gQueue = new eventQueue(); gQueue.push(new loadURI("about:about")); @@ -115,7 +115,7 @@ gQueue.onFinish = function() { - //disableLogging(); + disableLogging(); closeBrowserWindow(); } gQueue.invoke(); diff --git a/b2g/config/emulator-ics/releng-emulator-ics.tt b/b2g/config/emulator-ics/releng-emulator-ics.tt index 4f97e6b2db8..c4c963838bc 100644 --- a/b2g/config/emulator-ics/releng-emulator-ics.tt +++ b/b2g/config/emulator-ics/releng-emulator-ics.tt @@ -7,10 +7,11 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", "algorithm": "sha512", "filename": "gtk3.tar.xz", +"setup": "setup.sh", "unpack": true } ] diff --git a/b2g/config/emulator-jb/releng-emulator-jb.tt b/b2g/config/emulator-jb/releng-emulator-jb.tt index 4f97e6b2db8..c4c963838bc 100644 --- a/b2g/config/emulator-jb/releng-emulator-jb.tt +++ b/b2g/config/emulator-jb/releng-emulator-jb.tt @@ -7,10 +7,11 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", "algorithm": "sha512", "filename": "gtk3.tar.xz", +"setup": "setup.sh", "unpack": true } ] diff --git a/b2g/config/emulator-kk/releng-emulator-kk.tt b/b2g/config/emulator-kk/releng-emulator-kk.tt index 4f97e6b2db8..c4c963838bc 100644 --- a/b2g/config/emulator-kk/releng-emulator-kk.tt +++ b/b2g/config/emulator-kk/releng-emulator-kk.tt @@ -7,10 +7,11 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", "algorithm": "sha512", "filename": "gtk3.tar.xz", +"setup": "setup.sh", "unpack": true } ] diff --git a/b2g/config/emulator-l/releng-emulator-l.tt b/b2g/config/emulator-l/releng-emulator-l.tt index 4f97e6b2db8..c4c963838bc 100644 --- a/b2g/config/emulator-l/releng-emulator-l.tt +++ b/b2g/config/emulator-l/releng-emulator-l.tt @@ -7,10 +7,11 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", "algorithm": "sha512", "filename": "gtk3.tar.xz", +"setup": "setup.sh", "unpack": true } ] diff --git a/b2g/config/emulator-x86-kk/releng-emulator-kk.tt b/b2g/config/emulator-x86-kk/releng-emulator-kk.tt index 4f97e6b2db8..c4c963838bc 100644 --- a/b2g/config/emulator-x86-kk/releng-emulator-kk.tt +++ b/b2g/config/emulator-x86-kk/releng-emulator-kk.tt @@ -7,10 +7,11 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", "algorithm": "sha512", "filename": "gtk3.tar.xz", +"setup": "setup.sh", "unpack": true } ] diff --git a/b2g/config/emulator-x86-l/releng-emulator-l.tt b/b2g/config/emulator-x86-l/releng-emulator-l.tt index 4f97e6b2db8..c4c963838bc 100644 --- a/b2g/config/emulator-x86-l/releng-emulator-l.tt +++ b/b2g/config/emulator-x86-l/releng-emulator-l.tt @@ -7,10 +7,11 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", "algorithm": "sha512", "filename": "gtk3.tar.xz", +"setup": "setup.sh", "unpack": true } ] diff --git a/b2g/config/emulator/releng-emulator.tt b/b2g/config/emulator/releng-emulator.tt index 4f97e6b2db8..c4c963838bc 100644 --- a/b2g/config/emulator/releng-emulator.tt +++ b/b2g/config/emulator/releng-emulator.tt @@ -7,10 +7,11 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", "algorithm": "sha512", "filename": "gtk3.tar.xz", +"setup": "setup.sh", "unpack": true } ] diff --git a/b2g/config/tooltool-manifests/linux32/releng.manifest b/b2g/config/tooltool-manifests/linux32/releng.manifest index c4ca7bcd97a..e2cddb3bbd3 100644 --- a/b2g/config/tooltool-manifests/linux32/releng.manifest +++ b/b2g/config/tooltool-manifests/linux32/releng.manifest @@ -7,8 +7,8 @@ "unpack": true }, { -"size": 11179576, -"digest": "91567ce8e2bb8ab0ebc60c31e90731d88a1ea889fb71bcf55c735746a60fa7610b7e040ea3d8f727b6f692ae3ee703d6f3b30cdbd76fdf5617f77d9c38aa20ed", +"size": 11189216, +"digest": "18bc52b0599b1308b667e282abb45f47597bfc98a5140cfcab8da71dacf89dd76d0dee22a04ce26fe7ad1f04e2d6596991f9e5b01fd2aaaab5542965f596b0e6", "algorithm": "sha512", "filename": "gtk3.tar.xz", "setup": "setup.sh", diff --git a/b2g/dev/config/tooltool-manifests/linux64/releng.manifest b/b2g/dev/config/tooltool-manifests/linux64/releng.manifest index 95da0349d19..c4bf68b2348 100644 --- a/b2g/dev/config/tooltool-manifests/linux64/releng.manifest +++ b/b2g/dev/config/tooltool-manifests/linux64/releng.manifest @@ -7,8 +7,8 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", "algorithm": "sha512", "filename": "gtk3.tar.xz", "setup": "setup.sh", diff --git a/b2g/graphene/graphene.js b/b2g/graphene/graphene.js index 45d8e62ab0e..3a00ea04b52 100644 --- a/b2g/graphene/graphene.js +++ b/b2g/graphene/graphene.js @@ -30,9 +30,6 @@ 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"); diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 207418dcd1b..37df1bd5e5a 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -62,6 +62,12 @@ pref("extensions.blocklist.url", "https://blocklist.addons.mozilla.org/blocklist 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"); diff --git a/browser/config/mozconfigs/linux64/beta b/browser/config/mozconfigs/linux64/beta index 24492992d6b..01c4421bcfd 100644 --- a/browser/config/mozconfigs/linux64/beta +++ b/browser/config/mozconfigs/linux64/beta @@ -11,4 +11,5 @@ ac_add_options --enable-verify-mar mk_add_options MOZ_PGO=1 +. "$topsrcdir/build/mozconfig.rust" . "$topsrcdir/build/mozconfig.common.override" diff --git a/browser/config/mozconfigs/linux64/release b/browser/config/mozconfigs/linux64/release index 6c59868be84..e8bea0eabe0 100644 --- a/browser/config/mozconfigs/linux64/release +++ b/browser/config/mozconfigs/linux64/release @@ -18,4 +18,5 @@ mk_add_options MOZ_PGO=1 # defines.sh during the beta cycle export BUILDING_RELEASE=1 +. "$topsrcdir/build/mozconfig.rust" . "$topsrcdir/build/mozconfig.common.override" diff --git a/browser/config/mozconfigs/macosx-universal/beta b/browser/config/mozconfigs/macosx-universal/beta index 64e7ca84781..07f089a679a 100644 --- a/browser/config/mozconfigs/macosx-universal/beta +++ b/browser/config/mozconfigs/macosx-universal/beta @@ -10,5 +10,6 @@ fi ac_add_options --enable-official-branding ac_add_options --enable-verify-mar +. "$topsrcdir/build/mozconfig.rust" . "$topsrcdir/build/mozconfig.common.override" . "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/mozconfigs/macosx-universal/release b/browser/config/mozconfigs/macosx-universal/release index 8942f6229ee..c352022e361 100644 --- a/browser/config/mozconfigs/macosx-universal/release +++ b/browser/config/mozconfigs/macosx-universal/release @@ -16,5 +16,6 @@ ac_add_options --enable-verify-mar # defines.sh during the beta cycle export BUILDING_RELEASE=1 +. "$topsrcdir/build/mozconfig.rust" . "$topsrcdir/build/mozconfig.common.override" . "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/mozconfigs/whitelist b/browser/config/mozconfigs/whitelist index 02a33b0048b..c4482d87872 100644 --- a/browser/config/mozconfigs/whitelist +++ b/browser/config/mozconfigs/whitelist @@ -40,7 +40,6 @@ whitelist['nightly']['linux64'] += [ 'STRIP_FLAGS="--strip-debug"', 'ac_add_options --with-ccache=/usr/bin/ccache', '. "$topsrcdir/build/mozconfig.cache"', - '. "$topsrcdir/build/mozconfig.rust"', ] whitelist['nightly']['macosx-universal'] += [ @@ -53,7 +52,6 @@ whitelist['nightly']['macosx-universal'] += [ 'ac_add_options --disable-install-strip', 'ac_add_options --enable-instruments', 'ac_add_options --enable-dtrace', - '. "$topsrcdir/build/mozconfig.rust"', ] whitelist['nightly']['win32'] += [ diff --git a/browser/config/tooltool-manifests/linux32/releng.manifest b/browser/config/tooltool-manifests/linux32/releng.manifest index 47465cfa085..ed34f760272 100644 --- a/browser/config/tooltool-manifests/linux32/releng.manifest +++ b/browser/config/tooltool-manifests/linux32/releng.manifest @@ -7,10 +7,11 @@ "unpack": true }, { -"size": 11179576, -"digest": "91567ce8e2bb8ab0ebc60c31e90731d88a1ea889fb71bcf55c735746a60fa7610b7e040ea3d8f727b6f692ae3ee703d6f3b30cdbd76fdf5617f77d9c38aa20ed", +"size": 11189216, +"digest": "18bc52b0599b1308b667e282abb45f47597bfc98a5140cfcab8da71dacf89dd76d0dee22a04ce26fe7ad1f04e2d6596991f9e5b01fd2aaaab5542965f596b0e6", "algorithm": "sha512", "filename": "gtk3.tar.xz", +"setup": "setup.sh", "unpack": true }, { diff --git a/browser/config/tooltool-manifests/linux64/asan.manifest b/browser/config/tooltool-manifests/linux64/asan.manifest index 6c174581bfe..5e602fc9982 100644 --- a/browser/config/tooltool-manifests/linux64/asan.manifest +++ b/browser/config/tooltool-manifests/linux64/asan.manifest @@ -10,8 +10,8 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", "algorithm": "sha512", "filename": "gtk3.tar.xz", "setup": "setup.sh", diff --git a/browser/config/tooltool-manifests/linux64/clang.manifest b/browser/config/tooltool-manifests/linux64/clang.manifest index 55f5c054fb4..d232c5bb0f7 100644 --- a/browser/config/tooltool-manifests/linux64/clang.manifest +++ b/browser/config/tooltool-manifests/linux64/clang.manifest @@ -10,10 +10,11 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", "algorithm": "sha512", "filename": "gtk3.tar.xz", +"setup": "setup.sh", "unpack": true }, { diff --git a/browser/config/tooltool-manifests/linux64/clang.manifest.centos6 b/browser/config/tooltool-manifests/linux64/clang.manifest.centos6 index 68a88b3f466..758c51e90ab 100644 --- a/browser/config/tooltool-manifests/linux64/clang.manifest.centos6 +++ b/browser/config/tooltool-manifests/linux64/clang.manifest.centos6 @@ -10,10 +10,11 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", "algorithm": "sha512", "filename": "gtk3.tar.xz", +"setup": "setup.sh", "unpack": true } ] diff --git a/browser/config/tooltool-manifests/linux64/releng.manifest b/browser/config/tooltool-manifests/linux64/releng.manifest index bbd91ceace0..fc8a238fd93 100644 --- a/browser/config/tooltool-manifests/linux64/releng.manifest +++ b/browser/config/tooltool-manifests/linux64/releng.manifest @@ -7,10 +7,11 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", "algorithm": "sha512", "filename": "gtk3.tar.xz", +"setup": "setup.sh", "unpack": true }, { diff --git a/browser/config/tooltool-manifests/linux64/tsan.manifest b/browser/config/tooltool-manifests/linux64/tsan.manifest index 4bb5c3fd37b..5f8b985b633 100644 --- a/browser/config/tooltool-manifests/linux64/tsan.manifest +++ b/browser/config/tooltool-manifests/linux64/tsan.manifest @@ -10,10 +10,11 @@ "unpack": true }, { -"size": 12057960, -"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", +"size": 12072532, +"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9", "algorithm": "sha512", "filename": "gtk3.tar.xz", +"setup": "setup.sh", "unpack": true } ] diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py index dd52fb91be6..325bfacabd3 100644 --- a/build/mach_bootstrap.py +++ b/build/mach_bootstrap.py @@ -5,10 +5,13 @@ from __future__ import print_function, unicode_literals import errno +import json import os import platform +import random import sys import time +import uuid import __builtin__ from types import ModuleType @@ -67,6 +70,7 @@ SEARCH_PATHS = [ 'config', 'dom/bindings', 'dom/bindings/parser', + 'dom/media/test/external', 'layout/tools/reftest', 'other-licenses/ply', 'testing', @@ -109,6 +113,7 @@ MACH_MODULES = [ 'addon-sdk/mach_commands.py', 'build/valgrind/mach_commands.py', 'dom/bindings/mach_commands.py', + 'dom/media/test/external/mach_commands.py', 'layout/tools/reftest/mach_commands.py', 'python/mach_commands.py', 'python/mach/mach/commands/commandinfo.py', @@ -181,6 +186,14 @@ CATEGORIES = { } +# Server to which to submit telemetry data +BUILD_TELEMETRY_SERVER = 'http://52.88.27.118/build-metrics-dev' + + +# We submit data to telemetry approximately every this many mach invocations +TELEMETRY_SUBMISSION_FREQUENCY = 10 + + def get_state_dir(): """Obtain the path to a directory to hold state. @@ -222,6 +235,47 @@ def bootstrap(topsrcdir, mozilla_dir=None): sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS] import mach.main + def telemetry_handler(context, data): + # We have not opted-in to telemetry + if 'BUILD_SYSTEM_TELEMETRY' not in os.environ: + return + + telemetry_dir = os.path.join(get_state_dir()[0], 'telemetry') + try: + os.mkdir(telemetry_dir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + outgoing_dir = os.path.join(telemetry_dir, 'outgoing') + try: + os.mkdir(outgoing_dir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + # Add common metadata to help submit sorted data later on. + # For now, we'll just record the mach command that was invoked. + data['argv'] = sys.argv + + with open(os.path.join(outgoing_dir, str(uuid.uuid4()) + '.json'), + 'w') as f: + json.dump(data, f, sort_keys=True) + + def should_skip_dispatch(context, handler): + # The user is performing a maintenance command. + if handler.name in ('bootstrap', 'doctor', 'mach-commands', 'mercurial-setup'): + return True + + # We are running in automation. + if 'MOZ_AUTOMATION' in os.environ or 'TASK_ID' in os.environ: + return True + + # The environment is likely a machine invocation. + if sys.stdin.closed or not sys.stdin.isatty(): + return True + + return False + def pre_dispatch_handler(context, handler, args): """Perform global checks before command dispatch. @@ -230,13 +284,7 @@ def bootstrap(topsrcdir, mozilla_dir=None): tools are up to date. """ # Don't do anything when... - - # The user is performing a maintenance command. - if handler.name in ('bootstrap', 'doctor', 'mach-commands', 'mercurial-setup'): - return - - # We are running in automation. - if 'MOZ_AUTOMATION' in os.environ or 'TASK_ID' in os.environ: + if should_skip_dispatch(context, handler): return # User has disabled first run check. @@ -245,10 +293,6 @@ def bootstrap(topsrcdir, mozilla_dir=None): if 'NO_MERCURIAL_SETUP_CHECK' in os.environ: return - # The environment is likely a machine invocation. - if sys.stdin.closed or not sys.stdin.isatty(): - return - # Mercurial isn't managing this source checkout. if not os.path.exists(os.path.join(topsrcdir, '.hg')): return @@ -269,6 +313,68 @@ def bootstrap(topsrcdir, mozilla_dir=None): print(NO_MERCURIAL_SETUP.format(mach=sys.argv[0]), file=sys.stderr) sys.exit(2) + def post_dispatch_handler(context, handler, args): + """Perform global operations after command dispatch. + + + For now, we will use this to handle build system telemetry. + """ + # Don't do anything when... + if should_skip_dispatch(context, handler): + return + + # We have not opted-in to telemetry + if 'BUILD_SYSTEM_TELEMETRY' not in os.environ: + return + + # Every n-th operation + if random.randint(1, TELEMETRY_SUBMISSION_FREQUENCY) != 1: + return + + # No data to work with anyway + outgoing = os.path.join(get_state_dir()[0], 'telemetry', 'outgoing') + if not os.path.isdir(outgoing): + return + + # We can't import requests until after it has been added during the + # bootstrapping below. + import requests + + submitted = os.path.join(get_state_dir()[0], 'telemetry', 'submitted') + try: + os.mkdir(submitted) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + session = requests.Session() + for filename in os.listdir(outgoing): + path = os.path.join(outgoing, filename) + if os.path.isdir(path) or not path.endswith('.json'): + continue + with open(path, 'r') as f: + data = f.read() + r = session.post(BUILD_TELEMETRY_SERVER, data=data, + headers={'Content-Type': 'application/json'}) + # TODO: some of these errors are likely not recoverable, as + # written, we'll retry indefinitely + if r.status_code != 200: + print('Error posting to telemetry: %s %s' % + (r.status_code, r.text)) + continue + + os.rename(os.path.join(outgoing, filename), + os.path.join(submitted, filename)) + + session.close() + + # Discard submitted data that is >= 30 days old + now = time.time() + for filename in os.listdir(submitted): + ctime = os.stat(os.path.join(submitted, filename)).st_ctime + if now - ctime >= 60*60*24*30: + os.remove(os.path.join(submitted, filename)) + def populate_context(context, key=None): if key is None: return @@ -305,6 +411,12 @@ def bootstrap(topsrcdir, mozilla_dir=None): if key == 'pre_dispatch_handler': return pre_dispatch_handler + if key == 'telemetry_handler': + return telemetry_handler + + if key == 'post_dispatch_handler': + return post_dispatch_handler + raise AttributeError(key) mach = mach.main.Mach(os.getcwd()) diff --git a/build/mobile/remoteautomation.py b/build/mobile/remoteautomation.py index bf9342c480a..343f2e7bc39 100644 --- a/build/mobile/remoteautomation.py +++ b/build/mobile/remoteautomation.py @@ -13,7 +13,7 @@ import subprocess import sys from automation import Automation -from devicemanager import DMError, DeviceManager +from mozdevice import DMError, DeviceManager from mozlog import get_default_logger import mozcrash diff --git a/build/unix/build-gtk3/build-gtk3.sh b/build/unix/build-gtk3/build-gtk3.sh index 9e8d5c0c8f3..6e03d4a52d5 100644 --- a/build/unix/build-gtk3/build-gtk3.sh +++ b/build/unix/build-gtk3/build-gtk3.sh @@ -118,18 +118,20 @@ cat < $root_dir/gtk3/setup.sh #!/bin/sh cd \$(dirname \$0) +HERE=\$(pwd) # pango expects absolute paths in pango.modules, and TOOLTOOL_DIR may vary... -LD_LIBRARY_PATH=./usr/local/lib \ -PANGO_SYSCONFDIR=./usr/local/etc \ -PANGO_LIBDIR=./usr/local/lib \ -./usr/local/bin/pango-querymodules > ./usr/local/etc/pango/pango.modules +LD_LIBRARY_PATH=\$HERE/usr/local/lib \ +PANGO_SYSCONFDIR=\$HERE/usr/local/etc \ +PANGO_LIBDIR=\$HERE/usr/local/lib \ +\$HERE/usr/local/bin/pango-querymodules > \$HERE/usr/local/etc/pango/pango.modules # same with gdb-pixbuf and loaders.cache -LD_LIBRARY_PATH=./usr/local/lib \ -GDK_PIXBUF_MODULE_FILE=./usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache \ -GDK_PIXBUF_MODULEDIR=./usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \ -./usr/local/bin/gdk-pixbuf-query-loaders > ./usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache +LD_LIBRARY_PATH=\$HERE/usr/local/lib \ +GDK_PIXBUF_MODULE_FILE=\$HERE/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache \ +GDK_PIXBUF_MODULEDIR=\$HERE/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \ +\$HERE/usr/local/bin/gdk-pixbuf-query-loaders > \ +\$HERE/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache # The fontconfig version in the tooltool package has known uses of # uninitialized memory when creating its cache, and while most users @@ -137,7 +139,9 @@ GDK_PIXBUF_MODULEDIR=./usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \ # will create it. Combined with valgrind, this generates irrelevant # errors. # So create the fontconfig cache beforehand. -./usr/local/bin/fc-cache +FONTCONFIG_PATH=\$HERE/usr/local/etc/fonts \ +LD_LIBRARY_PATH=\$HERE/usr/local/lib \ +\$HERE/usr/local/bin/fc-cache EOF chmod +x $root_dir/gtk3/setup.sh diff --git a/build/unix/mozconfig.gtk b/build/unix/mozconfig.gtk index 0414ba859a0..46a34b87308 100644 --- a/build/unix/mozconfig.gtk +++ b/build/unix/mozconfig.gtk @@ -24,6 +24,3 @@ mk_add_options "export PANGO_LIBDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib" mk_add_options "export GDK_PIXBUF_MODULE_FILE=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" mk_add_options "export GDK_PIXBUF_MODULEDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders" mk_add_options "export LD_LIBRARY_PATH=$TOOLTOOL_DIR/gtk3/usr/local/lib" - -# Until a tooltool with bug 1188571 landed is available everywhere -$TOOLTOOL_DIR/gtk3/setup.sh diff --git a/config/check_macroassembler_style.py b/config/check_macroassembler_style.py index 1cd0c71fb6a..19ddd09f595 100644 --- a/config/check_macroassembler_style.py +++ b/config/check_macroassembler_style.py @@ -41,6 +41,8 @@ reAfterArg = "(?=[,)])" reMatchArg = re.compile(reBeforeArg + reArgType + reArgName + reArgDefault + reAfterArg) def get_normalized_signatures(signature, fileAnnot = None): + # Remove static + signature = signature.replace('static', '') # Remove semicolon. signature = signature.replace(';', ' ') # Normalize spaces. diff --git a/dom/apps/AppsUtils.jsm b/dom/apps/AppsUtils.jsm index 1604c369ddb..96bf9e11299 100644 --- a/dom/apps/AppsUtils.jsm +++ b/dom/apps/AppsUtils.jsm @@ -226,7 +226,7 @@ this.AppsUtils = { deferred.resolve(file); } }); - aRequestChannel.asyncOpen(listener, null); + aRequestChannel.asyncOpen2(listener); return deferred.promise; }, diff --git a/dom/apps/Webapps.jsm b/dom/apps/Webapps.jsm index 688a975d2d1..1bf122777ca 100644 --- a/dom/apps/Webapps.jsm +++ b/dom/apps/Webapps.jsm @@ -3490,21 +3490,15 @@ this.DOMApplicationRegistry = { let requestChannel; let appURI = NetUtil.newURI(aNewApp.origin, null, null); - let principal = - Services.scriptSecurityManager.createCodebasePrincipal(appURI, - {appId: aNewApp.localId}); - if (aIsLocalFileInstall) { requestChannel = NetUtil.newChannel({ uri: aFullPackagePath, - loadingPrincipal: principal, - contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER} + loadUsingSystemPrincipal: true} ).QueryInterface(Ci.nsIFileChannel); } else { requestChannel = NetUtil.newChannel({ uri: aFullPackagePath, - loadingPrincipal: principal, - contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER} + loadUsingSystemPrincipal: true} ).QueryInterface(Ci.nsIHttpChannel); requestChannel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING; } diff --git a/dom/base/ShadowRoot.cpp b/dom/base/ShadowRoot.cpp index 968602f6de6..ed60e55ae27 100644 --- a/dom/base/ShadowRoot.cpp +++ b/dom/base/ShadowRoot.cpp @@ -219,7 +219,7 @@ ShadowRoot::RemoveFromIdTable(Element* aElement, nsIAtom* aId) if (entry) { entry->RemoveIdElement(aElement); if (entry->IsEmpty()) { - mIdentifierMap.RawRemoveEntry(entry); + mIdentifierMap.RemoveEntry(entry); } } } diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 86e80fc08a9..ab915ab4c75 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -7270,13 +7270,13 @@ nsContentUtils::GetInnerWindowID(nsIRequest* aRequest) return inner ? inner->WindowID() : 0; } -void +nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI, nsCString& aHost) { aHost.Truncate(); nsresult rv = aURI->GetHost(aHost); if (NS_FAILED(rv)) { // Some URIs do not have a host - return; + return rv; } if (aHost.FindChar(':') != -1) { // Escape IPv6 address @@ -7285,14 +7285,20 @@ nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI, nsCString& aHost) aHost.Insert('[', 0); aHost.Append(']'); } + + return NS_OK; } -void +nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI, nsAString& aHost) { nsAutoCString hostname; - GetHostOrIPv6WithBrackets(aURI, hostname); + nsresult rv = GetHostOrIPv6WithBrackets(aURI, hostname); + if (NS_FAILED(rv)) { + return rv; + } CopyUTF8toUTF16(hostname, aHost); + return NS_OK; } void diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 521e30db025..81e48c71ea2 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -2402,8 +2402,8 @@ public: * If the hostname for aURI is an IPv6 it encloses it in brackets, * otherwise it just outputs the hostname in aHost. */ - static void GetHostOrIPv6WithBrackets(nsIURI* aURI, nsAString& aHost); - static void GetHostOrIPv6WithBrackets(nsIURI* aURI, nsCString& aHost); + static nsresult GetHostOrIPv6WithBrackets(nsIURI* aURI, nsAString& aHost); + static nsresult GetHostOrIPv6WithBrackets(nsIURI* aURI, nsCString& aHost); /* * Call the given callback on all remote children of the given top-level diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 6f48a316103..c09f23f4181 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -3003,7 +3003,7 @@ nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId) ++mExpandoAndGeneration.generation; } if (entry->IsEmpty()) { - mIdentifierMap.RawRemoveEntry(entry); + mIdentifierMap.RemoveEntry(entry); } } diff --git a/dom/base/nsDocumentEncoder.cpp b/dom/base/nsDocumentEncoder.cpp index 09b7195b37d..7e0e556728d 100644 --- a/dom/base/nsDocumentEncoder.cpp +++ b/dom/base/nsDocumentEncoder.cpp @@ -1438,7 +1438,7 @@ nsHTMLCopyEncoder::SetSelection(nsISelection* aSelection) mIsTextWidget = true; break; } -#ifdef MOZ_THUNDERBIRD +#if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE) else if (selContent->IsHTMLElement(nsGkAtoms::body)) { // Currently, setting mIsTextWidget to 'true' will result in the selection // being encoded/copied as pre-formatted plain text. diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 9fa8b7e45f5..6ee8ecb18ef 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -104,7 +104,6 @@ GK_ATOM(applet, "applet") GK_ATOM(applyImports, "apply-imports") GK_ATOM(applyTemplates, "apply-templates") GK_ATOM(mozapptype, "mozapptype") -GK_ATOM(apz, "apz") GK_ATOM(archive, "archive") GK_ATOM(area, "area") GK_ATOM(arrow, "arrow") @@ -2270,13 +2269,20 @@ GK_ATOM(SendMail, "SendMail") GK_ATOM(ForwardMail, "ForwardMail") GK_ATOM(ReplyToMail, "ReplyToMail") -// Smooth scroll events origins +// Scroll origins (these are used in various scrolling functions in +// nsIScrollableFrame and ScrollFrameHelper). These are divided into two lists +// - origins in the first one have smooth-scrolling prefs associated with them, +// under the "general.smoothScroll..*" pref branch. Origins in the +// second one do not. GK_ATOM(mouseWheel, "mouseWheel") // For discrete wheel events (e.g. not OSX magic mouse) GK_ATOM(pixels, "pixels") GK_ATOM(lines, "lines") GK_ATOM(pages, "pages") GK_ATOM(scrollbars, "scrollbars") GK_ATOM(other, "other") +// Scroll origins without smooth-scrolling prefs +GK_ATOM(apz, "apz") +GK_ATOM(restore, "restore") #ifdef ACCESSIBILITY GK_ATOM(alert, "alert") diff --git a/dom/bindings/BindingDeclarations.h b/dom/bindings/BindingDeclarations.h index 51b0bcc40e1..5cd8e761eb5 100644 --- a/dom/bindings/BindingDeclarations.h +++ b/dom/bindings/BindingDeclarations.h @@ -40,7 +40,7 @@ protected: JS::MutableHandle aVal); bool StringifyToJSON(JSContext* aCx, - JS::MutableHandle aValue, + JS::Handle aObj, nsAString& aJSON) const; // Struct used as a way to force a dictionary constructor to not init the diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index bb47f8997b8..cc7b717ef4d 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -45,8 +45,9 @@ #include "mozilla/dom/HTMLAppletElementBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/ResolveSystemBinding.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerScope.h" #include "mozilla/jsipc/CrossProcessObjectWrappers.h" -#include "WorkerPrivate.h" #include "nsDOMClassInfo.h" #include "ipc/ErrorIPCUtils.h" #include "mozilla/UseCounter.h" @@ -1867,11 +1868,10 @@ DictionaryBase::ParseJSON(JSContext* aCx, bool DictionaryBase::StringifyToJSON(JSContext* aCx, - JS::MutableHandle aValue, + JS::Handle aObj, nsAString& aJSON) const { - return JS_Stringify(aCx, aValue, nullptr, JS::NullHandleValue, - AppendJSONToString, &aJSON); + return JS::ToJSONMaybeSafely(aCx, aObj, AppendJSONToString, &aJSON); } /* static */ @@ -3239,5 +3239,18 @@ DeprecationWarning(JSContext* aCx, JSObject* aObject, } } +namespace binding_detail { +JSObject* +UnprivilegedJunkScopeOrWorkerGlobal() +{ + if (NS_IsMainThread()) { + return xpc::UnprivilegedJunkScope(); + } + + return workers::GetCurrentThreadWorkerPrivate()-> + GlobalScope()->GetGlobalJSObject(); +} +} // namespace binding_detail + } // namespace dom } // namespace mozilla diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index acc0008abbb..afe626cd282 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -3327,6 +3327,26 @@ DeprecationWarning(JSContext* aCx, JSObject* aObject, JSString* InterfaceObjectToString(JSContext* aCx, JS::Handle aObject, unsigned /* indent */); + +namespace binding_detail { +// Get a JS global object that can be used for some temporary allocations. The +// idea is that this should be used for situations when you need to operate in +// _some_ compartment but don't care which one. A typical example is when you +// have non-JS input, non-JS output, but have to go through some sort of JS +// representation in the middle, so need a compartment to allocate things in. +// +// It's VERY important that any consumers of this function only do things that +// are guaranteed to be side-effect-free, even in the face of a script +// environment controlled by a hostile adversary. This is because in the worker +// case the global is in fact the worker global, so it and its standard objects +// are controlled by the worker script. This is why this function is in the +// binding_detail namespace. Any use of this function MUST be very carefully +// reviewed by someone who is sufficiently devious and has a very good +// understanding of all the code that will run while we're using the return +// value, including the SpiderMonkey parts. +JSObject* UnprivilegedJunkScopeOrWorkerGlobal(); +} // namespace binding_detail + } // namespace dom } // namespace mozilla diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 0a75760eade..1905377bcdb 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -12257,13 +12257,21 @@ class CGDictionary(CGThing): "ToJSON", "bool", [Argument('nsAString&', 'aJSON')], body=dedent(""" - MOZ_ASSERT(NS_IsMainThread()); AutoJSAPI jsapi; jsapi.Init(); JSContext *cx = jsapi.cx(); - JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope()); // Usage approved by bholley - JS::Rooted obj(cx); - return ToObjectInternal(cx, &obj) && StringifyToJSON(cx, &obj, aJSON); + // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here + // because we'll only be creating objects, in ways that have no + // side-effects, followed by a call to JS::ToJSONMaybeSafely, + // which likewise guarantees no side-effects for the sorts of + // things we will pass it. + JSAutoCompartment ac(cx, binding_detail::UnprivilegedJunkScopeOrWorkerGlobal()); + JS::Rooted val(cx); + if (!ToObjectInternal(cx, &val)) { + return false; + } + JS::Rooted obj(cx, &val.toObject()); + return StringifyToJSON(cx, obj, aJSON); """), const=True) def toObjectInternalMethod(self): @@ -12403,7 +12411,8 @@ class CGDictionary(CGThing): methods.append(self.initFromJSONMethod()) try: methods.append(self.toObjectInternalMethod()) - methods.append(self.toJSONMethod()) + if self.dictionarySafeToJSONify(self.dictionary): + methods.append(self.toJSONMethod()) except MethodNotNewObjectError: # If we can't have a ToObjectInternal() because one of our members # can only be returned from [NewObject] methods, then just skip @@ -12710,6 +12719,52 @@ class CGDictionary(CGThing): return False return all(isTypeCopyConstructible(m.type) for m in dictionary.members) + @staticmethod + def typeSafeToJSONify(type): + """ + Determine whether the given type is safe to convert to JSON. The + restriction is that this needs to be safe while in a global controlled + by an adversary, and "safe" means no side-effects when the JS + representation of this type is converted to JSON. That means that we + have to be pretty restrictive about what things we can allow. For + example, "object" is out, because it may have accessor properties on it. + """ + if type.nullable(): + # Converting null to JSON is always OK. + return CGDictionary.typeSafeToJSONify(type.inner) + + if type.isSequence(): + # Sequences are arrays we create ourselves, with no holes. They + # should be safe if their contents are safe, as long as we suppress + # invocation of .toJSON on objects. + return CGDictionary.typeSafeToJSONify(type.inner) + + if type.isUnion(): + # OK if everything in it is ok. + return all(CGDictionary.typeSafeToJSONify(t) + for t in type.flatMemberTypes) + + if type.isDictionary(): + # OK if the dictionary is OK + return CGDictionary.dictionarySafeToJSONify(type.inner) + + if type.isString() or type.isEnum(): + # Strings are always OK. + return True + + if type.isPrimitive(): + # Primitives (numbers and booleans) are ok, as long as + # they're not unrestricted float/double. + return not type.isFloat() or not type.isUnrestricted() + + return False + + @staticmethod + def dictionarySafeToJSONify(dictionary): + # The dictionary itself is OK, so we're good if all our types are. + return all(CGDictionary.typeSafeToJSONify(m.type) + for m in dictionary.members) + class CGRegisterWorkerBindings(CGAbstractMethod): def __init__(self, config): @@ -13191,7 +13246,6 @@ class CGBindingRoot(CGThing): bindingHeaders["WrapperFactory.h"] = descriptors bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries # AutoJSAPI - bindingHeaders["xpcpublic.h"] = dictionaries # xpc::UnprivilegedJunkScope # Ensure we see our enums in the generated .cpp file, for the ToJSValue # method body. Also ensure that we see jsapi.h. if enums: @@ -15637,7 +15691,10 @@ class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember): jsapi.Init(); jsapi.TakeOwnershipOfErrorReporting(); JSContext* cx = jsapi.cx(); - JSAutoCompartment tempCompartment(cx, xpc::UnprivilegedJunkScope()); + // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here because + // all we want is to wrap into _some_ scope and then unwrap to find + // the reflector, and wrapping has no side-effects. + JSAutoCompartment tempCompartment(cx, binding_detail::UnprivilegedJunkScopeOrWorkerGlobal()); JS::Rooted v(cx); if(!ToJSValue(cx, self, &v)) { aRv.Throw(NS_ERROR_UNEXPECTED); diff --git a/dom/browser-element/BrowserElementParent.js b/dom/browser-element/BrowserElementParent.js index 423aae49aea..0b92d62696c 100644 --- a/dom/browser-element/BrowserElementParent.js +++ b/dom/browser-element/BrowserElementParent.js @@ -15,6 +15,7 @@ var Cr = Components.results; */ Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/BrowserElementPromptService.jsm"); @@ -1001,36 +1002,17 @@ BrowserElementParent.prototype = { Ci.nsIRequestObserver]) }; - // If we have a URI we'll use it to get the triggering principal to use, - // if not available a null principal is acceptable. - let referrer = null; - let principal = null; - if (_options.referrer) { - // newURI can throw on malformed URIs. - try { - referrer = Services.io.newURI(_options.referrer, null, null); - } - catch(e) { - debug('Malformed referrer -- ' + e); - } + let referrer = Services.io.newURI(_options.referrer, null, null); + let principal = + Services.scriptSecurityManager.createCodebasePrincipal( + referrer, this._frameLoader.loadContext.originAttributes); - // This simply returns null if there is no principal available - // for the requested uri. This is an acceptable fallback when - // calling newChannelFromURI2. - principal = - Services.scriptSecurityManager.createCodebasePrincipal( - referrer, this._frameLoader.loadContext.originAttributes); - } - - debug('Using principal? ' + !!principal); - - let channel = - Services.io.newChannelFromURI2(url, - null, // No document. - principal, // Loading principal - principal, // Triggering principal - Ci.nsILoadInfo.SEC_NORMAL, - Ci.nsIContentPolicy.TYPE_OTHER); + let channel = NetUtil.newChannel({ + uri: url, + loadingPrincipal: principal, + securityFlags: SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS, + contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER + }); // XXX We would set private browsing information prior to calling this. channel.notificationCallbacks = interfaceRequestor; @@ -1055,7 +1037,7 @@ BrowserElementParent.prototype = { } // Set-up complete, let's get things started. - channel.asyncOpen(new DownloadListener(), null); + channel.asyncOpen2(new DownloadListener()); return req; }, diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp index 1a28acad16a..3bd143dd42d 100644 --- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -48,6 +48,7 @@ #include "nsPIDOMWindow.h" #include "nsSandboxFlags.h" #include "xpcpublic.h" +#include "nsIFrame.h" namespace mozilla { @@ -416,10 +417,7 @@ EventListenerManager::AddEventListenerInternal( } if (IsApzAwareEvent(aTypeAtom)) { - nsCOMPtr node = do_QueryInterface(mTarget); - if (node) { - node->SetMayHaveApzAwareListeners(); - } + ProcessApzAwareEventListenerAdd(); } if (aTypeAtom && mTarget) { @@ -432,6 +430,44 @@ EventListenerManager::AddEventListenerInternal( } } +void +EventListenerManager::ProcessApzAwareEventListenerAdd() +{ + // Mark the node as having apz aware listeners + nsCOMPtr node = do_QueryInterface(mTarget); + if (node) { + node->SetMayHaveApzAwareListeners(); + } + + // Schedule a paint so event regions on the layer tree gets updated + nsIDocument* doc = nullptr; + if (node) { + doc = node->OwnerDoc(); + } + if (!doc) { + if (nsCOMPtr window = GetTargetAsInnerWindow()) { + doc = window->GetExtantDoc(); + } + } + if (!doc) { + if (nsCOMPtr helper = do_QueryInterface(mTarget)) { + if (nsPIDOMWindowInner* window = helper->GetOwner()) { + doc = window->GetExtantDoc(); + } + } + } + + if (doc) { + nsIPresShell* ps = doc->GetShell(); + if (ps) { + nsIFrame* f = ps->GetRootFrame(); + if (f) { + f->SchedulePaint(); + } + } + } +} + bool EventListenerManager::IsDeviceType(EventMessage aEventMessage) { diff --git a/dom/events/EventListenerManager.h b/dom/events/EventListenerManager.h index 57095bfb1d5..7a9b7679287 100644 --- a/dom/events/EventListenerManager.h +++ b/dom/events/EventListenerManager.h @@ -467,6 +467,8 @@ protected: nsIDocShell* GetDocShellForTarget(); + void ProcessApzAwareEventListenerAdd(); + /** * Compile the "inline" event listener for aListener. The * body of the listener can be provided in aBody; if this is null we diff --git a/dom/events/test/test_legacy_event.html b/dom/events/test/test_legacy_event.html index c877975e080..d772be10663 100644 --- a/dom/events/test/test_legacy_event.html +++ b/dom/events/test/test_legacy_event.html @@ -187,7 +187,7 @@ function mpTestModernBeatsLegacy(eventInfo) { // Test that an event which bubbles may fire listeners of different flavors // (modern vs. legacy) at each bubbling level, depending on what's registered // at that level. -function mpTestAncestorsWithDiffListeners(eventInfo) { +function mpTestDiffListenersEventBubbling(eventInfo) { return new Promise( function(resolve, reject) { var grandparent = createChildDiv(); @@ -198,35 +198,81 @@ function mpTestAncestorsWithDiffListeners(eventInfo) { var eventSentToTarget; target.addEventListener(eventInfo.modern_name, - createHandlerWithTypeCheck(eventInfo.modern_name, - function(e) { - ok(e.bubbles, "Expecting event to bubble"); - eventSentToTarget = e; - didEventFireOnTarget = true; - })); + createHandlerWithTypeCheck(eventInfo.modern_name, function(e) { + ok(e.bubbles, "Expecting event to bubble"); + eventSentToTarget = e; + didEventFireOnTarget = true; + })); parent.addEventListener(eventInfo.legacy_name, - createHandlerWithTypeCheck(eventInfo.legacy_name, - function(e) { - is(e, eventSentToTarget, - "Same event object should bubble, " + - "despite difference in type"); - didEventFireOnParent = true; - })); + createHandlerWithTypeCheck(eventInfo.legacy_name, function(e) { + is(e, eventSentToTarget, + "Same event object should bubble, despite difference in type"); + didEventFireOnParent = true; + })); grandparent.addEventListener(eventInfo.modern_name, - createHandlerWithTypeCheck(eventInfo.modern_name, - function(e) { - ok(didEventFireOnTarget, - "Event should have fired on child"); - ok(didEventFireOnParent, - "Event should have fired on parent"); - is(e, eventSentToTarget, - "Same event object should bubble, " + - "despite difference in type"); - parent.removeChild(target); - resolve(); - })); + createHandlerWithTypeCheck(eventInfo.modern_name, function(e) { + ok(didEventFireOnTarget, + "Event should have fired on child"); + ok(didEventFireOnParent, + "Event should have fired on parent"); + is(e, eventSentToTarget, + "Same event object should bubble, despite difference in type"); + // Clean up. + grandparent.parentNode.removeChild(grandparent); + resolve(); + })); + + eventInfo.trigger_event(target); + } + ); +} + +// Test that an event in the capture phase may fire listeners of different +// flavors (modern vs. legacy) at each level, depending on what's registered +// at that level. +function mpTestDiffListenersEventCapturing(eventInfo) { + return new Promise( + function(resolve, reject) { + var grandparent = createChildDiv(); + var parent = createChildDiv(grandparent); + var target = createChildDiv(parent); + var didEventFireOnTarget = false; + var didEventFireOnParent = false; + var didEventFireOnGrandparent = false; + var eventSentToGrandparent; + + grandparent.addEventListener(eventInfo.modern_name, + createHandlerWithTypeCheck(eventInfo.modern_name, function(e) { + eventSentToGrandparent = e; + didEventFireOnGrandparent = true; + }), true); + + parent.addEventListener(eventInfo.legacy_name, + createHandlerWithTypeCheck(eventInfo.legacy_name, function(e) { + is(e.eventPhase, Event.CAPTURING_PHASE, + "event should be in capturing phase"); + is(e, eventSentToGrandparent, + "Same event object should capture, despite difference in type"); + ok(didEventFireOnGrandparent, + "Event should have fired on grandparent"); + didEventFireOnParent = true; + }), true); + + target.addEventListener(eventInfo.modern_name, + createHandlerWithTypeCheck(eventInfo.modern_name, function(e) { + is(e.eventPhase, Event.AT_TARGET, + "event should be at target phase"); + is(e, eventSentToGrandparent, + "Same event object should capture, despite difference in type"); + ok(didEventFireOnParent, + "Event should have fired on parent"); + // Clean up. + grandparent.parentNode.removeChild(grandparent); + resolve(); + }), true); + eventInfo.trigger_event(target); } ); @@ -239,7 +285,9 @@ function main() { }).then(function() { return Promise.all(gLegacyEventInfo.map(mpTestModernBeatsLegacy)); }).then(function() { - return Promise.all(gLegacyEventInfo.map(mpTestAncestorsWithDiffListeners)); + return Promise.all(gLegacyEventInfo.map(mpTestDiffListenersEventCapturing)); + }).then(function() { + return Promise.all(gLegacyEventInfo.map(mpTestDiffListenersEventBubbling)); }).then(function() { SimpleTest.finish(); }).catch(function(reason) { diff --git a/dom/events/test/test_moz_mouse_pixel_scroll_event.html b/dom/events/test/test_moz_mouse_pixel_scroll_event.html index 32bc03399e6..25c7bb94340 100644 --- a/dom/events/test/test_moz_mouse_pixel_scroll_event.html +++ b/dom/events/test/test_moz_mouse_pixel_scroll_event.html @@ -4,6 +4,7 @@ Test for MozMousePixelScroll events + + + +Mozilla Bug 1235899 +

+
+

You should be able to fling this list without it stopping abruptly

+
+
+
    +
  1. Some text
  2. +
  3. Some text
  4. +
  5. Some text
  6. +
  7. Some text
  8. +
  9. Some text
  10. +
  11. Some text
  12. +
  13. Some text
  14. +
  15. Some text
  16. +
  17. Some text
  18. +
  19. Some text
  20. +
  21. Some text
  22. +
  23. Some text
  24. +
  25. Some text
  26. +
  27. Some text
  28. +
  29. Some text
  30. +
  31. Some text
  32. +
  33. Some text
  34. +
  35. Some text
  36. +
  37. Some text
  38. +
  39. Some text
  40. +
  41. Some text
  42. +
  43. Some text
  44. +
  45. Some text
  46. +
  47. Some text
  48. +
  49. Some text
  50. +
  51. Some text
  52. +
  53. Some text
  54. +
  55. Some text
  56. +
  57. Some text
  58. +
  59. Some text
  60. +
  61. Some text
  62. +
  63. Some text
  64. +
  65. Some text
  66. +
  67. Some text
  68. +
  69. Some text
  70. +
  71. Some text
  72. +
  73. Some text
  74. +
  75. Some text
  76. +
  77. Some text
  78. +
  79. Some text
  80. +
  81. Some text
  82. +
  83. Some text
  84. +
  85. Some text
  86. +
  87. Some text
  88. +
  89. Some text
  90. +
  91. Some text
  92. +
  93. Some text
  94. +
  95. Some text
  96. +
  97. Some text
  98. +
  99. Some text
  100. +
  101. Some text
  102. +
  103. Some text
  104. +
  105. Some text
  106. +
  107. Some text
  108. +
  109. Some text
  110. +
  111. Some text
  112. +
  113. Some text
  114. +
  115. Some text
  116. +
  117. Some text
  118. +
  119. Some text
  120. +
  121. Some text
  122. +
  123. Some text
  124. +
  125. Some text
  126. +
  127. Some text
  128. +
  129. Some text
  130. +
  131. Some text
  132. +
  133. Some text
  134. +
  135. Some text
  136. +
  137. Some text
  138. +
  139. Some text
  140. +
  141. Some text
  142. +
  143. Some text
  144. +
  145. Some text
  146. +
  147. Some text
  148. +
  149. Some text
  150. +
  151. Some text
  152. +
  153. Some text
  154. +
  155. Some text
  156. +
  157. Some text
  158. +
  159. Some text
  160. +
  161. Some text
  162. +
  163. Some text
  164. +
  165. Some text
  166. +
  167. Some text
  168. +
  169. Some text
  170. +
  171. Some text
  172. +
  173. Some text
  174. +
  175. Some text
  176. +
  177. Some text
  178. +
  179. Some text
  180. +
  181. Some text
  182. +
  183. Some text
  184. +
  185. Some text
  186. +
  187. Some text
  188. +
  189. Some text
  190. +
  191. Some text
  192. +
  193. Some text
  194. +
  195. Some text
  196. +
  197. Some text
  198. +
  199. Some text
  200. +
  201. Some text
  202. +
  203. Some text
  204. +
+
+
+
+ +
+
+
+
diff --git a/gfx/layers/apz/test/mochitest/test_smoothness.html b/gfx/layers/apz/test/mochitest/test_smoothness.html
index ec0bd578509..88373957a77 100644
--- a/gfx/layers/apz/test/mochitest/test_smoothness.html
+++ b/gfx/layers/apz/test/mochitest/test_smoothness.html
@@ -61,12 +61,6 @@
         SimpleTest.finish();
       }
 
-      var hwVsyncEnabled = SpecialPowers.getBoolPref("gfx.vsync.hw-vsync.enabled");
-      if (!hwVsyncEnabled) {
-        SimpleTest.ok(true, "Hardware vsync not enabled, skipping test");
-        SimpleTest.finish();
-      }
-
       SpecialPowers.pushPrefEnv({
         "set" : [
           [testPref, true]
diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp
index 37dc8a47f27..2010bfa7d24 100644
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -92,11 +92,11 @@ ScrollFrameTo(nsIScrollableFrame* aFrame, const CSSPoint& aPoint, bool& aSuccess
 
   // If the scrollable frame is currently in the middle of an async or smooth
   // scroll then we don't want to interrupt it (see bug 961280).
-  // Also if the scrollable frame got a scroll request from something other than us
+  // Also if the scrollable frame got a scroll request from a higher priority origin
   // since the last layers update, then we don't want to push our scroll request
   // because we'll clobber that one, which is bad.
   bool scrollInProgress = aFrame->IsProcessingAsyncScroll()
-      || (aFrame->LastScrollOrigin() && aFrame->LastScrollOrigin() != nsGkAtoms::apz)
+      || nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin())
       || aFrame->LastSmoothScrollOrigin();
   if (!scrollInProgress) {
     aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition, nsGkAtoms::apz);
diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp
index f7a46eeeff9..317e9c5e37d 100755
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -564,8 +564,8 @@ RenderLayers(ContainerT* aContainer,
 
     if (layerToRender->HasLayerBeenComposited()) {
       // Composer2D will compose this layer so skip GPU composition
-      // this time & reset composition flag for next composition phase
-      layerToRender->SetLayerComposited(false);
+      // this time. The flag will be reset for the next composition phase
+      // at the beginning of LayerManagerComposite::Rener().
       gfx::IntRect clearRect = layerToRender->GetClearRect();
       if (!clearRect.IsEmpty()) {
         // Clear layer's visible rect on FrameBuffer with transparent pixels
diff --git a/gfx/layers/composite/LayerManagerComposite.cpp b/gfx/layers/composite/LayerManagerComposite.cpp
index 1124e42bcbe..254e14a5e76 100644
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -762,6 +762,21 @@ LayerManagerComposite::PopGroupForLayerEffects(RefPtr a
                         Matrix4x4());
 }
 
+// Used to clear the 'mLayerComposited' flag at the beginning of each Render().
+static void
+ClearLayerFlags(Layer* aLayer) {
+  if (!aLayer) {
+    return;
+  }
+  if (aLayer->AsLayerComposite()) {
+    aLayer->AsLayerComposite()->SetLayerComposited(false);
+  }
+  for (Layer* child = aLayer->GetFirstChild(); child;
+       child = child->GetNextSibling()) {
+    ClearLayerFlags(child);
+  }
+}
+
 void
 LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion)
 {
@@ -773,6 +788,8 @@ LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion)
     return;
   }
 
+  ClearLayerFlags(mRoot);
+
   // At this time, it doesn't really matter if these preferences change
   // during the execution of the function; we should be safe in all
   // permutations. However, may as well just get the values onces and
diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp
index edd9edd16ce..46b3d8336f2 100644
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -58,6 +58,37 @@ using namespace mozilla::media;
 typedef std::vector OpVector;
 typedef nsTArray OpDestroyVector;
 
+namespace {
+class ImageBridgeThread : public Thread {
+public:
+
+  ImageBridgeThread() : Thread("ImageBridgeChild") {
+  }
+
+protected:
+
+  void Init() {
+#ifdef MOZ_ENABLE_PROFILER_SPS
+    mPseudoStackHack = mozilla_get_pseudo_stack();
+#endif
+  }
+
+  void CleanUp() {
+#ifdef MOZ_ENABLE_PROFILER_SPS
+    mPseudoStackHack = nullptr;
+#endif
+  }
+
+private:
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+  // This is needed to avoid a spurious leak report.  There's no other
+  // use for it.  See bug 1239504 and bug 1215265.
+  PseudoStack* mPseudoStackHack;
+#endif
+};
+}
+
 struct CompositableTransaction
 {
   CompositableTransaction()
@@ -376,7 +407,7 @@ bool ImageBridgeChild::IsCreated()
 void ImageBridgeChild::StartUp()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
-  ImageBridgeChild::StartUpOnThread(new Thread("ImageBridgeChild"));
+  ImageBridgeChild::StartUpOnThread(new ImageBridgeThread());
 }
 
 #ifdef MOZ_NUWA_PROCESS
@@ -709,7 +740,7 @@ ImageBridgeChild::StartUpInChildProcess(Transport* aTransport,
 
   gfxPlatform::GetPlatform();
 
-  sImageBridgeChildThread = new Thread("ImageBridgeChild");
+  sImageBridgeChildThread = new ImageBridgeThread();
   if (!sImageBridgeChildThread->Start()) {
     return nullptr;
   }
diff --git a/gfx/thebes/gfxFontEntry.cpp b/gfx/thebes/gfxFontEntry.cpp
index 528a869497e..8d7ed4653b4 100644
--- a/gfx/thebes/gfxFontEntry.cpp
+++ b/gfx/thebes/gfxFontEntry.cpp
@@ -590,11 +590,12 @@ ShareTableAndGetBlob(nsTArray&& aTable,
     Clear();
     // adopts elements of aTable
     mSharedBlobData = new FontTableBlobData(Move(aTable));
+
     mBlob = hb_blob_create(mSharedBlobData->GetTable(),
                            mSharedBlobData->GetTableLength(),
                            HB_MEMORY_MODE_READONLY,
                            mSharedBlobData, DeleteFontTableBlobData);
-    if (!mSharedBlobData) {
+    if (mBlob == hb_blob_get_empty() ) {
         // The FontTableBlobData was destroyed during hb_blob_create().
         // The (empty) blob is still be held in the hashtable with a strong
         // reference.
diff --git a/gfx/thebes/gfxFontconfigUtils.cpp b/gfx/thebes/gfxFontconfigUtils.cpp
index 867f60747ed..28f22db2bbc 100644
--- a/gfx/thebes/gfxFontconfigUtils.cpp
+++ b/gfx/thebes/gfxFontconfigUtils.cpp
@@ -617,13 +617,13 @@ gfxFontconfigUtils::UpdateFontListInternal(bool aForce)
                     bool added = entry->AddFont(font);
 
                     if (!entry->mKey) {
-                        // The reference to the font pattern keeps the pointer to
-                        // string for the key valid.  If adding the font failed
-                        // then the entry must be removed.
+                        // The reference to the font pattern keeps the pointer
+                        // to string for the key valid.  If adding the font
+                        // failed then the entry must be removed.
                         if (added) {
                             entry->mKey = family;
                         } else {
-                            mFontsByFamily.RawRemoveEntry(entry);
+                            mFontsByFamily.RemoveEntry(entry);
                         }
                     }
                 }
diff --git a/ipc/glue/BackgroundParentImpl.cpp b/ipc/glue/BackgroundParentImpl.cpp
index 48e6d6334e7..3cbb899105b 100644
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -217,6 +217,15 @@ BackgroundParentImpl::DeallocPBackgroundIndexedDBUtilsParent(
     mozilla::dom::indexedDB::DeallocPBackgroundIndexedDBUtilsParent(aActor);
 }
 
+bool
+BackgroundParentImpl::RecvFlushPendingFileDeletions()
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  return mozilla::dom::indexedDB::RecvFlushPendingFileDeletions();
+}
+
 auto
 BackgroundParentImpl::AllocPBlobParent(const BlobConstructorParams& aParams)
   -> PBlobParent*
diff --git a/ipc/glue/BackgroundParentImpl.h b/ipc/glue/BackgroundParentImpl.h
index 2dc5b2953d2..36bc02b0113 100644
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -58,6 +58,9 @@ protected:
                                         PBackgroundIndexedDBUtilsParent* aActor)
                                         override;
 
+  virtual bool
+  RecvFlushPendingFileDeletions() override;
+
   virtual PBlobParent*
   AllocPBlobParent(const BlobConstructorParams& aParams) override;
 
diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp
index a4b5b7d7cab..e71146f5de5 100644
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -157,6 +157,7 @@ public:
         MOZ_RELEASE_ASSERT(aOther.mMessageName);
         mMessageName = aOther.mMessageName;
         aOther.mMessageName = nullptr;
+        mMoved = aOther.mMoved;
         aOther.mMoved = true;
 
         mMessageRoutingId = aOther.mMessageRoutingId;
diff --git a/ipc/glue/PBackground.ipdl b/ipc/glue/PBackground.ipdl
index 9f7c2a19653..562f2d972a6 100644
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -66,6 +66,9 @@ parent:
 
   async PBackgroundIndexedDBUtils();
 
+  // Use only for testing!
+  async FlushPendingFileDeletions();
+
   async PVsync();
 
   async PCameras();
diff --git a/js/public/MemoryMetrics.h b/js/public/MemoryMetrics.h
index 9b5818ab054..5157dfa613c 100644
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -468,7 +468,6 @@ struct RuntimeSizes
     macro(_, MallocHeap, object) \
     macro(_, MallocHeap, atomsTable) \
     macro(_, MallocHeap, contexts) \
-    macro(_, MallocHeap, dtoa) \
     macro(_, MallocHeap, temporary) \
     macro(_, MallocHeap, interpreterStack) \
     macro(_, MallocHeap, mathCache) \
diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h
index b29e90f1c6c..a5571da7f0f 100644
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -934,7 +934,7 @@ MutableHandle::MutableHandle(PersistentRooted* root)
  * These roots can be used in heap-allocated data structures, so they are not
  * associated with any particular JSContext or stack. They are registered with
  * the JSRuntime itself, without locking, so they require a full JSContext to be
- * initialized, not one of its more restricted superclasses.  Initialization may
+ * initialized, not one of its more restricted superclasses. Initialization may
  * take place on construction, or in two phases if the no-argument constructor
  * is called followed by init().
  *
@@ -1048,9 +1048,10 @@ class PersistentRooted : public js::PersistentRootedBase,
     }
 
   private:
-    void set(T value) {
+    template 
+    void set(U&& value) {
         MOZ_ASSERT(initialized());
-        ptr = value;
+        ptr = mozilla::Forward(value);
     }
 
     // See the comment above Rooted::ptr.
diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp
index c0244582aea..17940444d08 100644
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -30,6 +30,8 @@
 using namespace js;
 using namespace js::wasm;
 
+using mozilla::IsNaN;
+
 typedef Handle HandleWasmModule;
 typedef MutableHandle MutableHandleWasmModule;
 
@@ -952,6 +954,9 @@ DecodeDataSection(JSContext* cx, Decoder& d, Handle heap)
     if (!d.readCStringIf(DataSection))
         return true;
 
+    if (!heap)
+        return Fail(cx, d, "data section requires a memory section");
+
     uint32_t sectionStart;
     if (!d.startSection(§ionStart))
         return Fail(cx, d, "expected data section byte size");
diff --git a/js/src/asmjs/WasmFrameIterator.cpp b/js/src/asmjs/WasmFrameIterator.cpp
index 0c9d251ddca..ef22146d3bd 100644
--- a/js/src/asmjs/WasmFrameIterator.cpp
+++ b/js/src/asmjs/WasmFrameIterator.cpp
@@ -96,6 +96,7 @@ FrameIterator::settle()
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
       case CodeRange::Inline:
+      case CodeRange::CallThunk:
         MOZ_CRASH("Should not encounter an exit during iteration");
     }
 }
@@ -491,6 +492,7 @@ ProfilingFrameIterator::initFromFP(const WasmActivation& activation)
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
       case CodeRange::Inline:
+      case CodeRange::CallThunk:
         MOZ_CRASH("Unexpected CodeRange kind");
     }
 
@@ -541,6 +543,7 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
     const CodeRange* codeRange = module_->lookupCodeRange(state.pc);
     switch (codeRange->kind()) {
       case CodeRange::Function:
+      case CodeRange::CallThunk:
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit: {
         // When the pc is inside the prologue/epilogue, the innermost
@@ -557,7 +560,7 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
         uint32_t offsetInCodeRange = offsetInModule - codeRange->begin();
         void** sp = (void**)state.sp;
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
-        if (offsetInCodeRange < PushedRetAddr) {
+        if (offsetInCodeRange < PushedRetAddr || codeRange->kind() == CodeRange::CallThunk) {
             // First instruction of the ARM/MIPS function; the return address is
             // still in lr and fp still holds the caller's fp.
             callerPC_ = state.lr;
@@ -571,7 +574,9 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
             AssertMatchesCallSite(*module_, callerPC_, callerFP_, sp);
         } else
 #endif
-        if (offsetInCodeRange < PushedFP || offsetInModule == codeRange->profilingReturn()) {
+        if (offsetInCodeRange < PushedFP || offsetInModule == codeRange->profilingReturn() ||
+            codeRange->kind() == CodeRange::CallThunk)
+        {
             // The return address has been pushed on the stack but not fp; fp
             // still points to the caller's fp.
             callerPC_ = *sp;
@@ -655,6 +660,7 @@ ProfilingFrameIterator::operator++()
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
       case CodeRange::Inline:
+      case CodeRange::CallThunk:
         stackAddress_ = callerFP_;
         callerPC_ = ReturnAddressFromFP(callerFP_);
         AssertMatchesCallSite(*module_, callerPC_, CallerFPFromFP(callerFP_), callerFP_);
@@ -696,6 +702,7 @@ ProfilingFrameIterator::label() const
       case CodeRange::ImportJitExit:    return importJitDescription;
       case CodeRange::ImportInterpExit: return importInterpDescription;
       case CodeRange::Inline:           return "inline stub (in asm.js)";
+      case CodeRange::CallThunk:        return "call thunk (in asm.js)";
     }
 
     MOZ_CRASH("bad code range kind");
@@ -771,6 +778,14 @@ wasm::EnableProfilingPrologue(const Module& module, const CallSite& callSite, bo
 #endif
 }
 
+void
+wasm::EnableProfilingThunk(const Module& module, const CallThunk& callThunk, bool enabled)
+{
+    const CodeRange& cr = module.codeRanges()[callThunk.u.codeRangeIndex];
+    uint32_t calleeOffset = enabled ? cr.funcProfilingEntry() : cr.funcNonProfilingEntry();
+    MacroAssembler::repatchThunk(module.code(), callThunk.offset, calleeOffset);
+}
+
 // Replace all the nops in all the epilogues of asm.js functions with jumps
 // to the profiling epilogues.
 void
diff --git a/js/src/asmjs/WasmFrameIterator.h b/js/src/asmjs/WasmFrameIterator.h
index 8deee28e37b..d60b06b20bf 100644
--- a/js/src/asmjs/WasmFrameIterator.h
+++ b/js/src/asmjs/WasmFrameIterator.h
@@ -33,6 +33,7 @@ namespace wasm {
 class CallSite;
 class CodeRange;
 class Module;
+struct CallThunk;
 struct FuncOffsets;
 struct ProfilingOffsets;
 
@@ -112,6 +113,9 @@ GenerateFunctionEpilogue(jit::MacroAssembler& masm, unsigned framePushed, FuncOf
 void
 EnableProfilingPrologue(const Module& module, const CallSite& callSite, bool enabled);
 
+void
+EnableProfilingThunk(const Module& module, const CallThunk& callThunk, bool enabled);
+
 void
 EnableProfilingEpilogue(const Module& module, const CodeRange& codeRange, bool enabled);
 
diff --git a/js/src/asmjs/WasmGenerator.cpp b/js/src/asmjs/WasmGenerator.cpp
index 4b99f5d8d19..0a1ba18a2f2 100644
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -18,6 +18,8 @@
 
 #include "asmjs/WasmGenerator.h"
 
+#include "mozilla/EnumeratedRange.h"
+
 #include "asmjs/WasmStubs.h"
 
 #include "jit/MacroAssembler-inl.h"
@@ -26,6 +28,8 @@ using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
+using mozilla::MakeEnumeratedRange;
+
 // ****************************************************************************
 // ModuleGenerator
 
@@ -41,6 +45,8 @@ ModuleGenerator::ModuleGenerator(ExclusiveContext* cx)
     alloc_(&lifo_),
     masm_(MacroAssembler::AsmJSToken(), alloc_),
     funcIndexToExport_(cx),
+    lastPatchedCallsite_(0),
+    startOfUnpatchedBranches_(0),
     parallel_(false),
     outstanding_(0),
     tasks_(cx),
@@ -173,13 +179,111 @@ ModuleGenerator::finishOutstandingTask()
     return finishTask(task);
 }
 
-static const uint32_t BadEntry = UINT32_MAX;
+static const uint32_t BadCodeRange = UINT32_MAX;
 
 bool
 ModuleGenerator::funcIsDefined(uint32_t funcIndex) const
 {
-    return funcIndex < funcEntryOffsets_.length() &&
-           funcEntryOffsets_[funcIndex] != BadEntry;
+    return funcIndex < funcIndexToCodeRange_.length() &&
+           funcIndexToCodeRange_[funcIndex] != BadCodeRange;
+}
+
+uint32_t
+ModuleGenerator::funcEntry(uint32_t funcIndex) const
+{
+    MOZ_ASSERT(funcIsDefined(funcIndex));
+    return module_->codeRanges[funcIndexToCodeRange_[funcIndex]].funcNonProfilingEntry();
+}
+
+static uint32_t
+JumpRange()
+{
+    return Min(JitOptions.jumpThreshold, JumpImmediateRange);
+}
+
+typedef HashMap OffsetMap;
+
+bool
+ModuleGenerator::convertOutOfRangeBranchesToThunks()
+{
+    masm_.haltingAlign(CodeAlignment);
+
+    // Create thunks for callsites that have gone out of range. Use a map to
+    // create one thunk for each callee since there is often high reuse.
+
+    OffsetMap alreadyThunked(cx_);
+    if (!alreadyThunked.init())
+        return false;
+
+    for (; lastPatchedCallsite_ < masm_.callSites().length(); lastPatchedCallsite_++) {
+        const CallSiteAndTarget& cs = masm_.callSites()[lastPatchedCallsite_];
+        if (!cs.isInternal())
+            continue;
+
+        uint32_t callerOffset = cs.returnAddressOffset();
+        MOZ_RELEASE_ASSERT(callerOffset < INT32_MAX);
+
+        if (funcIsDefined(cs.targetIndex())) {
+            uint32_t calleeOffset = funcEntry(cs.targetIndex());
+            MOZ_RELEASE_ASSERT(calleeOffset < INT32_MAX);
+
+            if (uint32_t(abs(int32_t(calleeOffset) - int32_t(callerOffset))) < JumpRange()) {
+                masm_.patchCall(callerOffset, calleeOffset);
+                continue;
+            }
+        }
+
+        OffsetMap::AddPtr p = alreadyThunked.lookupForAdd(cs.targetIndex());
+        if (!p) {
+            Offsets offsets;
+            offsets.begin = masm_.currentOffset();
+            uint32_t thunkOffset = masm_.thunkWithPatch().offset();
+            if (masm_.oom())
+                return false;
+            offsets.end = masm_.currentOffset();
+
+            if (!module_->codeRanges.emplaceBack(CodeRange::CallThunk, offsets))
+                return false;
+            if (!module_->callThunks.emplaceBack(thunkOffset, cs.targetIndex()))
+                return false;
+            if (!alreadyThunked.add(p, cs.targetIndex(), offsets.begin))
+                return false;
+        }
+
+        masm_.patchCall(callerOffset, p->value());
+    }
+
+    // Create thunks for jumps to stubs. Stubs are always generated at the end
+    // so unconditionally thunk all existing jump sites.
+
+    for (JumpTarget target : MakeEnumeratedRange(JumpTarget::Limit)) {
+        if (masm_.jumpSites()[target].empty())
+            continue;
+
+        for (uint32_t jumpSite : masm_.jumpSites()[target]) {
+            RepatchLabel label;
+            label.use(jumpSite);
+            masm_.bind(&label);
+        }
+
+        Offsets offsets;
+        offsets.begin = masm_.currentOffset();
+        uint32_t thunkOffset = masm_.thunkWithPatch().offset();
+        if (masm_.oom())
+            return false;
+        offsets.end = masm_.currentOffset();
+
+        if (!module_->codeRanges.emplaceBack(CodeRange::Inline, offsets))
+            return false;
+        if (!jumpThunks_[target].append(thunkOffset))
+            return false;
+    }
+
+    // Unlike callsites, which need to be persisted in the Module, we can simply
+    // flush jump sites after each patching pass.
+    masm_.clearJumpSites();
+
+    return true;
 }
 
 bool
@@ -188,19 +292,33 @@ ModuleGenerator::finishTask(IonCompileTask* task)
     const FuncBytecode& func = task->func();
     FuncCompileResults& results = task->results();
 
+    // Before merging in the new function's code, if jumps/calls in a previous
+    // function's body might go out of range, patch these to thunks which have
+    // full range.
+    if ((masm_.size() - startOfUnpatchedBranches_) + results.masm().size() > JumpRange()) {
+        startOfUnpatchedBranches_ = masm_.size();
+        if (!convertOutOfRangeBranchesToThunks())
+            return false;
+    }
+
     // Offset the recorded FuncOffsets by the offset of the function in the
     // whole module's code segment.
     uint32_t offsetInWhole = masm_.size();
     results.offsets().offsetBy(offsetInWhole);
 
-    // Record the non-profiling entry for whole-module linking later.
-    // Cannot simply append because funcIndex order is nonlinear.
-    if (func.index() >= funcEntryOffsets_.length()) {
-        if (!funcEntryOffsets_.appendN(BadEntry, func.index() - funcEntryOffsets_.length() + 1))
+    // Add the CodeRange for this function.
+    uint32_t funcCodeRangeIndex = module_->codeRanges.length();
+    if (!module_->codeRanges.emplaceBack(func.index(), func.lineOrBytecode(), results.offsets()))
+        return false;
+
+    // Maintain a mapping from function index to CodeRange index.
+    if (func.index() >= funcIndexToCodeRange_.length()) {
+        uint32_t n = func.index() - funcIndexToCodeRange_.length() + 1;
+        if (!funcIndexToCodeRange_.appendN(BadCodeRange, n))
             return false;
     }
     MOZ_ASSERT(!funcIsDefined(func.index()));
-    funcEntryOffsets_[func.index()] = results.offsets().nonProfilingEntry;
+    funcIndexToCodeRange_[func.index()] = funcCodeRangeIndex;
 
     // Merge the compiled results into the whole-module masm.
     DebugOnly sizeBefore = masm_.size();
@@ -208,10 +326,6 @@ ModuleGenerator::finishTask(IonCompileTask* task)
         return false;
     MOZ_ASSERT(masm_.size() == offsetInWhole + results.masm().size());
 
-    // Add the CodeRange for this function.
-    if (!module_->codeRanges.emplaceBack(func.index(), func.lineOrBytecode(), results.offsets()))
-        return false;
-
     // Keep a record of slow functions for printing in the final console message.
     unsigned totalTime = func.generateTime() + results.compileTime();
     if (totalTime >= SlowFunction::msThreshold) {
@@ -223,6 +337,113 @@ ModuleGenerator::finishTask(IonCompileTask* task)
     return true;
 }
 
+bool
+ModuleGenerator::finishCodegen()
+{
+    uint32_t offsetInWhole = masm_.size();
+
+    // Generate stubs in a separate MacroAssembler since, otherwise, for modules
+    // larger than the JumpImmediateRange, even local uses of Label will fail
+    // due to the large absolute offsets temporarily stored by Label::bind().
+
+    Vector entries(cx_);
+    Vector interpExits(cx_);
+    Vector jitExits(cx_);
+    EnumeratedArray jumpTargets;
+    Offsets interruptExit;
+
+    {
+        TempAllocator alloc(&lifo_);
+        MacroAssembler masm(MacroAssembler::AsmJSToken(), alloc);
+
+        if (!entries.resize(numExports()))
+            return false;
+        for (uint32_t i = 0; i < numExports(); i++) {
+            uint32_t target = exportMap_->exportFuncIndices[i];
+            const Sig& sig = module_->exports[i].sig();
+            entries[i] = GenerateEntry(masm, target, sig, usesHeap());
+        }
+
+        if (!interpExits.resize(numImports()))
+            return false;
+        if (!jitExits.resize(numImports()))
+            return false;
+        for (uint32_t i = 0; i < numImports(); i++) {
+            interpExits[i] = GenerateInterpExit(masm, module_->imports[i], i);
+            jitExits[i] = GenerateJitExit(masm, module_->imports[i], usesHeap());
+        }
+
+        for (JumpTarget target : MakeEnumeratedRange(JumpTarget::Limit))
+            jumpTargets[target] = GenerateJumpTarget(masm, target);
+
+        interruptExit = GenerateInterruptStub(masm);
+
+        if (masm.oom() || !masm_.asmMergeWith(masm))
+            return false;
+    }
+
+    // Adjust each of the resulting Offsets (to account for being merged into
+    // masm_) and then create code ranges for all the stubs.
+
+    for (uint32_t i = 0; i < numExports(); i++) {
+        entries[i].offsetBy(offsetInWhole);
+        module_->exports[i].initStubOffset(entries[i].begin);
+        if (!module_->codeRanges.emplaceBack(CodeRange::Entry, entries[i]))
+            return false;
+    }
+
+    for (uint32_t i = 0; i < numImports(); i++) {
+        interpExits[i].offsetBy(offsetInWhole);
+        module_->imports[i].initInterpExitOffset(interpExits[i].begin);
+        if (!module_->codeRanges.emplaceBack(CodeRange::ImportInterpExit, interpExits[i]))
+            return false;
+
+        jitExits[i].offsetBy(offsetInWhole);
+        module_->imports[i].initJitExitOffset(jitExits[i].begin);
+        if (!module_->codeRanges.emplaceBack(CodeRange::ImportJitExit, jitExits[i]))
+            return false;
+    }
+
+    for (JumpTarget target : MakeEnumeratedRange(JumpTarget::Limit)) {
+        jumpTargets[target].offsetBy(offsetInWhole);
+        if (!module_->codeRanges.emplaceBack(CodeRange::Inline, jumpTargets[target]))
+            return false;
+    }
+
+    interruptExit.offsetBy(offsetInWhole);
+    if (!module_->codeRanges.emplaceBack(CodeRange::Inline, interruptExit))
+        return false;
+
+    // The signal handler redirects PC to the out-of-bounds and interrupt stubs.
+
+    link_->pod.outOfBoundsOffset = jumpTargets[JumpTarget::OutOfBounds].begin;
+    link_->pod.interruptOffset = interruptExit.begin;
+
+    // Only call convertOutOfRangeBranchesToThunks after all other codegen that may
+    // emit new jumps to JumpTargets has finished.
+
+    if (!convertOutOfRangeBranchesToThunks())
+        return false;
+
+    // Now that all thunks have been generated, patch all the thunks.
+
+    for (CallThunk& callThunk : module_->callThunks) {
+        uint32_t funcIndex = callThunk.u.funcIndex;
+        callThunk.u.codeRangeIndex = funcIndexToCodeRange_[funcIndex];
+        masm_.patchThunk(callThunk.offset, funcEntry(funcIndex));
+    }
+
+    for (JumpTarget target : MakeEnumeratedRange(JumpTarget::Limit)) {
+        for (uint32_t thunkOffset : jumpThunks_[target])
+            masm_.patchThunk(thunkOffset, jumpTargets[target].begin);
+    }
+
+    // Code-generation is complete!
+
+    masm_.finish();
+    return !masm_.oom();
+}
+
 bool
 ModuleGenerator::addImport(const Sig& sig, uint32_t globalDataOffset)
 {
@@ -335,11 +556,12 @@ ModuleGenerator::funcSig(uint32_t funcIndex) const
 bool
 ModuleGenerator::initImport(uint32_t importIndex, uint32_t sigIndex)
 {
+    MOZ_ASSERT(isAsmJS());
+
     uint32_t globalDataOffset;
     if (!allocateGlobalBytes(Module::SizeOfImportExit, sizeof(void*), &globalDataOffset))
         return false;
 
-    MOZ_ASSERT(isAsmJS());
     MOZ_ASSERT(importIndex == module_->imports.length());
     if (!addImport(sig(sigIndex), globalDataOffset))
         return false;
@@ -364,16 +586,6 @@ ModuleGenerator::import(uint32_t index) const
     return shared_->imports[index];
 }
 
-bool
-ModuleGenerator::defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit)
-{
-    Import& import = module_->imports[index];
-    import.initInterpExitOffset(interpExit.begin);
-    import.initJitExitOffset(jitExit.begin);
-    return module_->codeRanges.emplaceBack(CodeRange::ImportInterpExit, interpExit) &&
-           module_->codeRanges.emplaceBack(CodeRange::ImportJitExit, jitExit);
-}
-
 bool
 ModuleGenerator::declareExport(UniqueChars fieldName, uint32_t funcIndex, uint32_t* exportIndex)
 {
@@ -403,39 +615,12 @@ ModuleGenerator::declareExport(UniqueChars fieldName, uint32_t funcIndex, uint32
            exportMap_->exportFuncIndices.append(funcIndex);
 }
 
-uint32_t
-ModuleGenerator::exportFuncIndex(uint32_t index) const
-{
-    return exportMap_->exportFuncIndices[index];
-}
-
-uint32_t
-ModuleGenerator::exportEntryOffset(uint32_t index) const
-{
-    uint32_t funcIndex = exportMap_->exportFuncIndices[index];
-    MOZ_ASSERT(funcIsDefined(funcIndex));
-    return funcEntryOffsets_[funcIndex];
-}
-
-const Sig&
-ModuleGenerator::exportSig(uint32_t index) const
-{
-    return module_->exports[index].sig();
-}
-
 uint32_t
 ModuleGenerator::numExports() const
 {
     return module_->exports.length();
 }
 
-bool
-ModuleGenerator::defineExport(uint32_t index, Offsets offsets)
-{
-    module_->exports[index].initStubOffset(offsets.begin);
-    return module_->codeRanges.emplaceBack(CodeRange::Entry, offsets);
-}
-
 bool
 ModuleGenerator::addMemoryExport(UniqueChars fieldName)
 {
@@ -561,24 +746,9 @@ ModuleGenerator::finishFuncDefs()
             return false;
     }
 
-    for (uint32_t funcIndex = 0; funcIndex < funcEntryOffsets_.length(); funcIndex++)
+    for (uint32_t funcIndex = 0; funcIndex < funcIndexToCodeRange_.length(); funcIndex++)
         MOZ_ASSERT(funcIsDefined(funcIndex));
 
-    // During codegen, all wasm->wasm (internal) calls use AsmJSInternalCallee
-    // as the call target, which contains the function-index of the target.
-    // These get recorded in a CallSiteAndTargetVector in the MacroAssembler
-    // so that we can patch them now that all the function entry offsets are
-    // known.
-
-    for (CallSiteAndTarget& cs : masm_.callSites()) {
-        if (!cs.isInternal())
-            continue;
-        MOZ_ASSERT(cs.kind() == CallSiteDesc::Relative);
-        uint32_t callerOffset = cs.returnAddressOffset();
-        uint32_t calleeOffset = funcEntryOffsets_[cs.targetIndex()];
-        masm_.patchCall(callerOffset, calleeOffset);
-    }
-
     module_->functionBytes = masm_.size();
     finishedFuncs_ = true;
     return true;
@@ -628,31 +798,10 @@ ModuleGenerator::defineFuncPtrTable(uint32_t index, const Vector& elem
     for (size_t i = 0; i < elemFuncIndices.length(); i++) {
         uint32_t funcIndex = elemFuncIndices[i];
         MOZ_ASSERT(funcIsDefined(funcIndex));
-        table.elemOffsets[i] = funcEntryOffsets_[funcIndex];
+        table.elemOffsets[i] = funcEntry(funcIndex);
     }
 }
 
-bool
-ModuleGenerator::defineInlineStub(Offsets offsets)
-{
-    MOZ_ASSERT(finishedFuncs_);
-    return module_->codeRanges.emplaceBack(CodeRange::Inline, offsets);
-}
-
-void
-ModuleGenerator::defineInterruptExit(uint32_t offset)
-{
-    MOZ_ASSERT(finishedFuncs_);
-    link_->pod.interruptOffset = offset;
-}
-
-void
-ModuleGenerator::defineOutOfBoundsExit(uint32_t offset)
-{
-    MOZ_ASSERT(finishedFuncs_);
-    link_->pod.outOfBoundsOffset = offset;
-}
-
 bool
 ModuleGenerator::finish(CacheableCharsVector&& prettyFuncNames,
                         UniqueModuleData* module,
@@ -663,15 +812,11 @@ ModuleGenerator::finish(CacheableCharsVector&& prettyFuncNames,
     MOZ_ASSERT(!activeFunc_);
     MOZ_ASSERT(finishedFuncs_);
 
+    if (!finishCodegen())
+        return false;
+
     module_->prettyFuncNames = Move(prettyFuncNames);
 
-    if (!GenerateStubs(*this))
-        return false;
-
-    masm_.finish();
-    if (masm_.oom())
-        return false;
-
     // Start global data on a new page so JIT code may be given independent
     // protection flags. Note assumption that global data starts right after
     // code below.
diff --git a/js/src/asmjs/WasmGenerator.h b/js/src/asmjs/WasmGenerator.h
index dee5477ec21..1d0dee0db04 100644
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -142,8 +142,11 @@ class MOZ_STACK_CLASS ModuleGenerator
     LifoAlloc                       lifo_;
     jit::TempAllocator              alloc_;
     jit::MacroAssembler             masm_;
-    Uint32Vector                    funcEntryOffsets_;
+    Uint32Vector                    funcIndexToCodeRange_;
     FuncIndexMap                    funcIndexToExport_;
+    uint32_t                        lastPatchedCallsite_;
+    uint32_t                        startOfUnpatchedBranches_;
+    JumpSiteArray                   jumpThunks_;
 
     // Parallel compilation
     bool                            parallel_;
@@ -158,7 +161,10 @@ class MOZ_STACK_CLASS ModuleGenerator
 
     bool finishOutstandingTask();
     bool funcIsDefined(uint32_t funcIndex) const;
+    uint32_t funcEntry(uint32_t funcIndex) const;
+    bool convertOutOfRangeBranchesToThunks();
     bool finishTask(IonCompileTask* task);
+    bool finishCodegen();
     bool addImport(const Sig& sig, uint32_t globalDataOffset);
     bool startedFuncDefs() const { return !!threadView_; }
     bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset);
@@ -195,15 +201,10 @@ class MOZ_STACK_CLASS ModuleGenerator
     bool initImport(uint32_t importIndex, uint32_t sigIndex);
     uint32_t numImports() const;
     const ModuleImportGeneratorData& import(uint32_t index) const;
-    bool defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit);
 
     // Exports:
     bool declareExport(UniqueChars fieldName, uint32_t funcIndex, uint32_t* exportIndex = nullptr);
     uint32_t numExports() const;
-    uint32_t exportFuncIndex(uint32_t index) const;
-    uint32_t exportEntryOffset(uint32_t index) const;
-    const Sig& exportSig(uint32_t index) const;
-    bool defineExport(uint32_t index, Offsets offsets);
     bool addMemoryExport(UniqueChars fieldName);
 
     // Function definitions:
@@ -217,11 +218,6 @@ class MOZ_STACK_CLASS ModuleGenerator
     uint32_t funcPtrTableGlobalDataOffset(uint32_t index) const;
     void defineFuncPtrTable(uint32_t index, const Vector& elemFuncIndices);
 
-    // Stubs:
-    bool defineInlineStub(Offsets offsets);
-    void defineInterruptExit(uint32_t offset);
-    void defineOutOfBoundsExit(uint32_t offset);
-
     // Return a ModuleData object which may be used to construct a Module, the
     // StaticLinkData required to call Module::staticallyLink, and the list of
     // functions that took a long time to compile.
diff --git a/js/src/asmjs/WasmModule.cpp b/js/src/asmjs/WasmModule.cpp
index df3ff29ad69..02eb64b3483 100644
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -360,7 +360,7 @@ CodeRange::CodeRange(Kind kind, Offsets offsets)
     u.kind_ = kind;
 
     MOZ_ASSERT(begin_ <= end_);
-    MOZ_ASSERT(u.kind_ == Entry || u.kind_ == Inline);
+    MOZ_ASSERT(u.kind_ == Entry || u.kind_ == Inline || u.kind_ == CallThunk);
 }
 
 CodeRange::CodeRange(Kind kind, ProfilingOffsets offsets)
@@ -508,6 +508,7 @@ ModuleData::serializedSize() const
            SerializedPodVectorSize(heapAccesses) +
            SerializedPodVectorSize(codeRanges) +
            SerializedPodVectorSize(callSites) +
+           SerializedPodVectorSize(callThunks) +
            SerializedVectorSize(prettyFuncNames) +
            filename.serializedSize();
 }
@@ -522,6 +523,7 @@ ModuleData::serialize(uint8_t* cursor) const
     cursor = SerializePodVector(cursor, heapAccesses);
     cursor = SerializePodVector(cursor, codeRanges);
     cursor = SerializePodVector(cursor, callSites);
+    cursor = SerializePodVector(cursor, callThunks);
     cursor = SerializeVector(cursor, prettyFuncNames);
     cursor = filename.serialize(cursor);
     return cursor;
@@ -542,6 +544,7 @@ ModuleData::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
     (cursor = DeserializePodVector(cx, cursor, &heapAccesses)) &&
     (cursor = DeserializePodVector(cx, cursor, &codeRanges)) &&
     (cursor = DeserializePodVector(cx, cursor, &callSites)) &&
+    (cursor = DeserializePodVector(cx, cursor, &callThunks)) &&
     (cursor = DeserializeVector(cx, cursor, &prettyFuncNames)) &&
     (cursor = filename.deserialize(cx, cursor));
     return cursor;
@@ -562,6 +565,7 @@ ModuleData::clone(JSContext* cx, ModuleData* out) const
            ClonePodVector(cx, heapAccesses, &out->heapAccesses) &&
            ClonePodVector(cx, codeRanges, &out->codeRanges) &&
            ClonePodVector(cx, callSites, &out->callSites) &&
+           ClonePodVector(cx, callThunks, &out->callThunks) &&
            CloneVector(cx, prettyFuncNames, &out->prettyFuncNames) &&
            filename.clone(cx, &out->filename);
 }
@@ -575,6 +579,7 @@ ModuleData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
            heapAccesses.sizeOfExcludingThis(mallocSizeOf) +
            codeRanges.sizeOfExcludingThis(mallocSizeOf) +
            callSites.sizeOfExcludingThis(mallocSizeOf) +
+           callThunks.sizeOfExcludingThis(mallocSizeOf) +
            prettyFuncNames.sizeOfExcludingThis(mallocSizeOf) +
            filename.sizeOfExcludingThis(mallocSizeOf);
 }
@@ -792,6 +797,9 @@ Module::setProfilingEnabled(JSContext* cx, bool enabled)
         for (const CallSite& callSite : module_->callSites)
             EnableProfilingPrologue(*this, callSite, enabled);
 
+        for (const CallThunk& callThunk : module_->callThunks)
+            EnableProfilingThunk(*this, callThunk, enabled);
+
         for (const CodeRange& codeRange : module_->codeRanges)
             EnableProfilingEpilogue(*this, codeRange, enabled);
     }
diff --git a/js/src/asmjs/WasmModule.h b/js/src/asmjs/WasmModule.h
index ba82933475c..0e8273553e8 100644
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -67,9 +67,7 @@ struct StaticLinkData
     typedef Vector InternalLinkVector;
 
     typedef Vector OffsetVector;
-    struct SymbolicLinkArray : mozilla::EnumeratedArray {
+    struct SymbolicLinkArray : EnumeratedArray {
         WASM_DECLARE_SERIALIZABLE(SymbolicLinkArray)
     };
 
@@ -213,7 +211,7 @@ class CodeRange
     void assertValid();
 
   public:
-    enum Kind { Function, Entry, ImportJitExit, ImportInterpExit, Inline };
+    enum Kind { Function, Entry, ImportJitExit, ImportInterpExit, Inline, CallThunk };
 
     CodeRange() = default;
     CodeRange(Kind kind, Offsets offsets);
@@ -237,7 +235,7 @@ class CodeRange
     // which is used for asynchronous profiling to determine the frame pointer.
 
     uint32_t profilingReturn() const {
-        MOZ_ASSERT(kind() != Entry && kind() != Inline);
+        MOZ_ASSERT(isFunction() || isImportExit());
         return profilingReturn_;
     }
 
@@ -247,6 +245,9 @@ class CodeRange
     bool isFunction() const {
         return kind() == Function;
     }
+    bool isImportExit() const {
+        return kind() == ImportJitExit || kind() == ImportInterpExit;
+    }
     uint32_t funcProfilingEntry() const {
         MOZ_ASSERT(isFunction());
         return begin();
@@ -288,6 +289,25 @@ class CodeRange
 
 typedef Vector CodeRangeVector;
 
+// A CallThunk describes the offset and target of thunks so that they may be
+// patched at runtime when profiling is toggled. Thunks are emitted to connect
+// callsites that are too far away from callees to fit in a single call
+// instruction's relative offset.
+
+struct CallThunk
+{
+    uint32_t offset;
+    union {
+        uint32_t funcIndex;
+        uint32_t codeRangeIndex;
+    } u;
+
+    CallThunk(uint32_t offset, uint32_t funcIndex) : offset(offset) { u.funcIndex = funcIndex; }
+    CallThunk() = default;
+};
+
+typedef Vector CallThunkVector;
+
 // CacheableChars is used to cacheably store UniqueChars.
 
 struct CacheableChars : UniqueChars
@@ -389,6 +409,7 @@ struct ModuleData : ModuleCacheablePod
     HeapAccessVector      heapAccesses;
     CodeRangeVector       codeRanges;
     CallSiteVector        callSites;
+    CallThunkVector       callThunks;
     CacheableCharsVector  prettyFuncNames;
     CacheableChars        filename;
     bool                  loadedFromCache;
@@ -496,6 +517,7 @@ class Module
     CompileArgs compileArgs() const { return module_->compileArgs; }
     const ImportVector& imports() const { return module_->imports; }
     const ExportVector& exports() const { return module_->exports; }
+    const CodeRangeVector& codeRanges() const { return module_->codeRanges; }
     const char* filename() const { return module_->filename.get(); }
     bool loadedFromCache() const { return module_->loadedFromCache; }
     bool staticallyLinked() const { return staticallyLinked_; }
diff --git a/js/src/asmjs/WasmStubs.cpp b/js/src/asmjs/WasmStubs.cpp
index 46eef572f28..663057fc693 100644
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -19,7 +19,6 @@
 #include "asmjs/WasmStubs.h"
 
 #include "mozilla/ArrayUtils.h"
-#include "mozilla/EnumeratedRange.h"
 
 #include "jit/MacroAssembler-inl.h"
 
@@ -28,7 +27,6 @@ using namespace js::jit;
 using namespace js::wasm;
 
 using mozilla::ArrayLength;
-using mozilla::MakeEnumeratedRange;
 
 static void
 AssertStackAlignment(MacroAssembler& masm, uint32_t alignment, uint32_t addBeforeAssert = 0)
@@ -94,12 +92,9 @@ static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void
 // The signature of the entry point is Module::CodePtr. The exported wasm
 // function has an ABI derived from its specific signature, so this function
 // must map from the ABI of CodePtr to the export's signature's ABI.
-static bool
-GenerateEntry(ModuleGenerator& mg, unsigned exportIndex)
+Offsets
+wasm::GenerateEntry(MacroAssembler& masm, unsigned target, const Sig& sig, bool usesHeap)
 {
-    MacroAssembler& masm = mg.masm();
-    const Sig& sig = mg.exportSig(exportIndex);
-
     masm.haltingAlign(CodeAlignment);
 
     Offsets offsets;
@@ -131,7 +126,7 @@ GenerateEntry(ModuleGenerator& mg, unsigned exportIndex)
     // ARM, MIPS/MIPS64 and x64 have a globally-pinned HeapReg (x86 uses immediates in
     // effective addresses). Loading the heap register depends on the global
     // register already having been loaded.
-    if (mg.usesHeap())
+    if (usesHeap)
         masm.loadAsmJSHeapRegisterFromGlobalData();
 
     // Put the 'argv' argument into a non-argument/return register so that we
@@ -235,9 +230,7 @@ GenerateEntry(ModuleGenerator& mg, unsigned exportIndex)
 
     // Call into the real function.
     masm.assertStackAlignment(AsmJSStackAlignment);
-    Label target;
-    target.bind(mg.exportEntryOffset(exportIndex));
-    masm.call(CallSiteDesc(CallSiteDesc::Relative), &target);
+    masm.call(CallSiteDesc(CallSiteDesc::Relative), AsmJSInternalCallee(target));
 
     // Recover the stack pointer value before dynamic alignment.
     masm.loadWasmActivation(scratch);
@@ -283,11 +276,8 @@ GenerateEntry(ModuleGenerator& mg, unsigned exportIndex)
     masm.move32(Imm32(true), ReturnReg);
     masm.ret();
 
-    if (masm.oom())
-        return false;
-
     offsets.end = masm.currentOffset();
-    return mg.defineExport(exportIndex, offsets);
+    return offsets;
 }
 
 static void
@@ -333,11 +323,10 @@ FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args, unsigned argO
 // Generate a stub that is called via the internal ABI derived from the
 // signature of the import and calls into an appropriate InvokeImport C++
 // function, having boxed all the ABI arguments into a homogeneous Value array.
-static bool
-GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets* offsets)
+ProfilingOffsets
+wasm::GenerateInterpExit(MacroAssembler& masm, const Import& import, uint32_t importIndex)
 {
-    MacroAssembler& masm = mg.masm();
-    const Sig& sig = *mg.import(importIndex).sig;
+    const Sig& sig = import.sig();
 
     masm.setFramePushed(0);
 
@@ -356,7 +345,8 @@ GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffse
     unsigned argBytes = Max(1, sig.args().length()) * sizeof(Value);
     unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, argOffset + argBytes);
 
-    GenerateExitPrologue(masm, framePushed, ExitReason::ImportInterp, offsets);
+    ProfilingOffsets offsets;
+    GenerateExitPrologue(masm, framePushed, ExitReason::ImportInterp, &offsets);
 
     // Fill the argument array.
     unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed();
@@ -407,7 +397,6 @@ GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffse
       case ExprType::I64:
         MOZ_CRASH("no int64 in asm.js");
       case ExprType::F32:
-        MOZ_ASSERT(!mg.isAsmJS(), "import can't return float32 in asm.js");
         masm.call(SymbolicAddress::InvokeImport_F64);
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
         masm.loadDouble(argv, ReturnDoubleReg);
@@ -426,13 +415,10 @@ GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffse
         MOZ_CRASH("Limit");
     }
 
-    GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, offsets);
+    GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, &offsets);
 
-    if (masm.oom())
-        return false;
-
-    offsets->end = masm.currentOffset();
-    return true;
+    offsets.end = masm.currentOffset();
+    return offsets;
 }
 
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
@@ -444,11 +430,10 @@ static const unsigned MaybeSavedGlobalReg = 0;
 // Generate a stub that is called via the internal ABI derived from the
 // signature of the import and calls into a compatible JIT function,
 // having boxed all the ABI arguments into the JIT stack frame layout.
-static bool
-GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets* offsets)
+ProfilingOffsets
+wasm::GenerateJitExit(MacroAssembler& masm, const Import& import, bool usesHeap)
 {
-    MacroAssembler& masm = mg.masm();
-    const Sig& sig = *mg.import(importIndex).sig;
+    const Sig& sig = import.sig();
 
     masm.setFramePushed(0);
 
@@ -465,7 +450,8 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets*
     unsigned jitFramePushed = StackDecrementForCall(masm, JitStackAlignment, totalJitFrameBytes) -
                               sizeOfRetAddr;
 
-    GenerateExitPrologue(masm, jitFramePushed, ExitReason::ImportJit, offsets);
+    ProfilingOffsets offsets;
+    GenerateExitPrologue(masm, jitFramePushed, ExitReason::ImportJit, &offsets);
 
     // 1. Descriptor
     size_t argOffset = 0;
@@ -479,7 +465,7 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets*
     Register scratch = ABIArgGenerator::NonArgReturnReg1;  // repeatedly clobbered
 
     // 2.1. Get ExitDatum
-    unsigned globalDataOffset = mg.import(importIndex).globalDataOffset;
+    uint32_t globalDataOffset = import.exitGlobalDataOffset();
 #if defined(JS_CODEGEN_X64)
     masm.append(AsmJSGlobalAccess(masm.leaRipRelative(callee), globalDataOffset));
 #elif defined(JS_CODEGEN_X86)
@@ -663,7 +649,6 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets*
       case ExprType::I64:
         MOZ_CRASH("no int64 in asm.js");
       case ExprType::F32:
-        MOZ_ASSERT(!mg.isAsmJS(), "import can't return float32 in asm.js");
         masm.convertValueToFloat(JSReturnOperand, ReturnFloat32Reg, &oolConvert);
         break;
       case ExprType::F64:
@@ -682,10 +667,10 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets*
 
     // Ion code does not respect system callee-saved register conventions so
     // reload the heap register.
-    if (mg.usesHeap())
+    if (usesHeap)
         masm.loadAsmJSHeapRegisterFromGlobalData();
 
-    GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::ImportJit, offsets);
+    GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::ImportJit, &offsets);
 
     if (oolConvert.used()) {
         masm.bind(&oolConvert);
@@ -728,7 +713,6 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets*
             masm.loadDouble(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnDoubleReg);
             break;
           case ExprType::F32:
-            MOZ_ASSERT(!mg.isAsmJS(), "import can't return float32 in asm.js");
             masm.call(SymbolicAddress::CoerceInPlace_ToNumber);
             masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
             masm.loadDouble(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnDoubleReg);
@@ -744,37 +728,18 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets*
 
     MOZ_ASSERT(masm.framePushed() == 0);
 
-    if (masm.oom())
-        return false;
-
-    offsets->end = masm.currentOffset();
-    return true;
-}
-
-static void
-BindJumps(MacroAssembler& masm, JumpTarget target)
-{
-    for (uint32_t offset : masm.jumpSites()[target]) {
-        RepatchLabel label;
-        label.use(offset);
-        masm.bind(&label);
-    }
+    offsets.end = masm.currentOffset();
+    return offsets;
 }
 
 // Generate a stub that is called immediately after the prologue when there is a
 // stack overflow. This stub calls a C++ function to report the error and then
 // jumps to the throw stub to pop the activation.
-static bool
-GenerateStackOverflowStub(ModuleGenerator& mg)
+static Offsets
+GenerateStackOverflow(MacroAssembler& masm)
 {
-    MacroAssembler& masm = mg.masm();
     masm.haltingAlign(CodeAlignment);
 
-    if (masm.jumpSites()[JumpTarget::StackOverflow].empty())
-        return true;
-
-    BindJumps(masm, JumpTarget::StackOverflow);
-
     Offsets offsets;
     offsets.begin = masm.currentOffset();
 
@@ -797,27 +762,18 @@ GenerateStackOverflowStub(ModuleGenerator& mg)
     masm.call(SymbolicAddress::ReportOverRecursed);
     masm.jump(JumpTarget::Throw);
 
-    if (masm.oom())
-        return false;
-
     offsets.end = masm.currentOffset();
-    return mg.defineInlineStub(offsets);
+    return offsets;
 }
 
 // Generate a stub that is jumped to from an out-of-bounds heap access when
 // there are throwing semantics. This stub calls a C++ function to report an
 // error and then jumps to the throw stub to pop the activation.
-static bool
-GenerateConversionErrorStub(ModuleGenerator& mg)
+static Offsets
+GenerateConversionError(MacroAssembler& masm)
 {
-    MacroAssembler& masm = mg.masm();
     masm.haltingAlign(CodeAlignment);
 
-    if (masm.jumpSites()[JumpTarget::ConversionError].empty())
-        return true;
-
-    BindJumps(masm, JumpTarget::ConversionError);
-
     Offsets offsets;
     offsets.begin = masm.currentOffset();
 
@@ -830,28 +786,18 @@ GenerateConversionErrorStub(ModuleGenerator& mg)
     masm.call(SymbolicAddress::OnImpreciseConversion);
     masm.jump(JumpTarget::Throw);
 
-    if (masm.oom())
-        return false;
-
     offsets.end = masm.currentOffset();
-    return mg.defineInlineStub(offsets);
+    return offsets;
 }
 
 // Generate a stub that is jumped to from an out-of-bounds heap access when
 // there are throwing semantics. This stub calls a C++ function to report an
 // error and then jumps to the throw stub to pop the activation.
-static bool
-GenerateOutOfBoundsStub(ModuleGenerator& mg)
+static Offsets
+GenerateOutOfBounds(MacroAssembler& masm)
 {
-    MacroAssembler& masm = mg.masm();
     masm.haltingAlign(CodeAlignment);
 
-    // Generate the out-of-bounds stub unconditionally since it may always be
-    // used by the signal handler.
-    mg.defineOutOfBoundsExit(masm.currentOffset());
-
-    BindJumps(masm, JumpTarget::OutOfBounds);
-
     Offsets offsets;
     offsets.begin = masm.currentOffset();
 
@@ -864,11 +810,54 @@ GenerateOutOfBoundsStub(ModuleGenerator& mg)
     masm.call(SymbolicAddress::OnOutOfBounds);
     masm.jump(JumpTarget::Throw);
 
-    if (masm.oom())
-        return false;
+    offsets.end = masm.currentOffset();
+    return offsets;
+}
+
+// If an exception is thrown, simply pop all frames (since asm.js does not
+// contain try/catch). To do this:
+//  1. Restore 'sp' to it's value right after the PushRegsInMask in GenerateEntry.
+//  2. PopRegsInMask to restore the caller's non-volatile registers.
+//  3. Return (to CallAsmJS).
+static Offsets
+GenerateThrow(MacroAssembler& masm)
+{
+    masm.haltingAlign(CodeAlignment);
+
+    Offsets offsets;
+    offsets.begin = masm.currentOffset();
+
+    // We are about to pop all frames in this WasmActivation. Set fp to null to
+    // maintain the invariant that fp is either null or pointing to a valid
+    // frame.
+    Register scratch = ABIArgGenerator::NonArgReturnReg0;
+    masm.loadWasmActivation(scratch);
+    masm.storePtr(ImmWord(0), Address(scratch, WasmActivation::offsetOfFP()));
+
+    masm.setFramePushed(FramePushedForEntrySP);
+    masm.loadStackPtr(Address(scratch, WasmActivation::offsetOfEntrySP()));
+    masm.Pop(scratch);
+    masm.PopRegsInMask(NonVolatileRegs);
+    MOZ_ASSERT(masm.framePushed() == 0);
+
+    masm.mov(ImmWord(0), ReturnReg);
+    masm.ret();
 
     offsets.end = masm.currentOffset();
-    return mg.defineInlineStub(offsets);
+    return offsets;
+}
+
+Offsets
+wasm::GenerateJumpTarget(MacroAssembler& masm, JumpTarget target)
+{
+    switch (target) {
+      case JumpTarget::StackOverflow: return GenerateStackOverflow(masm);
+      case JumpTarget::ConversionError: return GenerateConversionError(masm);
+      case JumpTarget::OutOfBounds: return GenerateOutOfBounds(masm);
+      case JumpTarget::Throw: return GenerateThrow(masm);
+      case JumpTarget::Limit: break;
+    }
+    MOZ_CRASH("bad JumpTarget");
 }
 
 static const LiveRegisterSet AllRegsExceptSP(
@@ -884,16 +873,11 @@ static const LiveRegisterSet AllRegsExceptSP(
 // Unfortunately, loading this requires a scratch register which we don't have
 // after restoring all registers. To hack around this, push the resumePC on the
 // stack so that it can be popped directly into PC.
-static bool
-GenerateInterruptStub(ModuleGenerator& mg)
+Offsets
+wasm::GenerateInterruptStub(MacroAssembler& masm)
 {
-    MacroAssembler& masm = mg.masm();
     masm.haltingAlign(CodeAlignment);
 
-    // Generate the interrupt stub unconditionally since it may always be used
-    // by the signal handler.
-    mg.defineInterruptExit(masm.currentOffset());
-
     Offsets offsets;
     offsets.begin = masm.currentOffset();
 
@@ -1040,89 +1024,6 @@ GenerateInterruptStub(ModuleGenerator& mg)
 # error "Unknown architecture!"
 #endif
 
-    if (masm.oom())
-        return false;
-
     offsets.end = masm.currentOffset();
-    return mg.defineInlineStub(offsets);
+    return offsets;
 }
-
-// If an exception is thrown, simply pop all frames (since asm.js does not
-// contain try/catch). To do this:
-//  1. Restore 'sp' to it's value right after the PushRegsInMask in GenerateEntry.
-//  2. PopRegsInMask to restore the caller's non-volatile registers.
-//  3. Return (to CallAsmJS).
-static bool
-GenerateThrowStub(ModuleGenerator& mg)
-{
-    MacroAssembler& masm = mg.masm();
-    masm.haltingAlign(CodeAlignment);
-
-    if (masm.jumpSites()[JumpTarget::Throw].empty())
-        return true;
-
-    BindJumps(masm, JumpTarget::Throw);
-
-    Offsets offsets;
-    offsets.begin = masm.currentOffset();
-
-    // We are about to pop all frames in this WasmActivation. Set fp to null to
-    // maintain the invariant that fp is either null or pointing to a valid
-    // frame.
-    Register scratch = ABIArgGenerator::NonArgReturnReg0;
-    masm.loadWasmActivation(scratch);
-    masm.storePtr(ImmWord(0), Address(scratch, WasmActivation::offsetOfFP()));
-
-    masm.setFramePushed(FramePushedForEntrySP);
-    masm.loadStackPtr(Address(scratch, WasmActivation::offsetOfEntrySP()));
-    masm.Pop(scratch);
-    masm.PopRegsInMask(NonVolatileRegs);
-    MOZ_ASSERT(masm.framePushed() == 0);
-
-    masm.mov(ImmWord(0), ReturnReg);
-    masm.ret();
-
-    if (masm.oom())
-        return false;
-
-    offsets.end = masm.currentOffset();
-    return mg.defineInlineStub(offsets);
-}
-
-bool
-wasm::GenerateStubs(ModuleGenerator& mg)
-{
-    for (unsigned i = 0; i < mg.numExports(); i++) {
-        if (!GenerateEntry(mg, i))
-            return false;
-    }
-
-    for (size_t i = 0; i < mg.numImports(); i++) {
-        ProfilingOffsets interp;
-        if (!GenerateInterpExitStub(mg, i, &interp))
-            return false;
-
-        ProfilingOffsets jit;
-        if (!GenerateJitExitStub(mg, i, &jit))
-            return false;
-
-        if (!mg.defineImport(i, interp, jit))
-            return false;
-    }
-
-    if (!GenerateStackOverflowStub(mg))
-        return false;
-
-    if (!GenerateConversionErrorStub(mg))
-        return false;
-
-    if (!GenerateOutOfBoundsStub(mg))
-        return false;
-
-    if (!GenerateInterruptStub(mg))
-        return false;
-
-    // The throw stub must go last since the other stubs use it.
-    return GenerateThrowStub(mg);
-}
-
diff --git a/js/src/asmjs/WasmStubs.h b/js/src/asmjs/WasmStubs.h
index c10bd8976f3..5ca511d1992 100644
--- a/js/src/asmjs/WasmStubs.h
+++ b/js/src/asmjs/WasmStubs.h
@@ -24,8 +24,20 @@
 namespace js {
 namespace wasm {
 
-bool
-GenerateStubs(ModuleGenerator& mg);
+extern Offsets
+GenerateEntry(jit::MacroAssembler& masm, uint32_t target, const Sig& sig, bool usesHeap);
+
+extern ProfilingOffsets
+GenerateInterpExit(jit::MacroAssembler& masm, const Import& import, uint32_t importIndex);
+
+extern ProfilingOffsets
+GenerateJitExit(jit::MacroAssembler& masm, const Import& import, bool usesHeap);
+
+extern Offsets
+GenerateJumpTarget(jit::MacroAssembler& masm, JumpTarget target);
+
+extern Offsets
+GenerateInterruptStub(jit::MacroAssembler& masm);
 
 } // namespace wasm
 } // namespace js
diff --git a/js/src/asmjs/WasmTypes.h b/js/src/asmjs/WasmTypes.h
index 0610ca17d72..8dc655ee3fc 100644
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -38,6 +38,7 @@ class PropertyName;
 
 namespace wasm {
 
+using mozilla::EnumeratedArray;
 using mozilla::Move;
 using mozilla::DebugOnly;
 using mozilla::MallocSizeOf;
@@ -266,7 +267,7 @@ typedef Vector DeclaredSigPtrVector;
 
 struct Offsets
 {
-    MOZ_IMPLICIT Offsets(uint32_t begin = 0, uint32_t end = 0)
+    explicit Offsets(uint32_t begin = 0, uint32_t end = 0)
       : begin(begin), end(end)
     {}
 
@@ -571,7 +572,7 @@ enum class JumpTarget
     Limit
 };
 
-typedef mozilla::EnumeratedArray JumpSiteArray;
+typedef EnumeratedArray JumpSiteArray;
 
 // The CompileArgs struct captures global parameters that affect all wasm code
 // generation. It also currently is the single source of truth for whether or
diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp
index eba0214656a..c9003e10968 100644
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -928,7 +928,7 @@ js::RegExpMatcher(JSContext* cx, unsigned argc, Value* vp)
  * This code cannot re-enter Ion code. */
 bool
 js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
-                     int32_t lastIndex, bool sticky,
+                     uint32_t lastIndex, bool sticky,
                      MatchPairs* maybeMatches, MutableHandleValue output)
 {
     MOZ_ASSERT(lastIndex <= INT32_MAX);
@@ -998,7 +998,7 @@ js::RegExpTester(JSContext* cx, unsigned argc, Value* vp)
  * This code cannot re-enter Ion code. */
 bool
 js::RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
-                    int32_t lastIndex, bool sticky, int32_t* endIndex)
+                    uint32_t lastIndex, bool sticky, int32_t* endIndex)
 {
     MOZ_ASSERT(lastIndex <= INT32_MAX);
 
diff --git a/js/src/builtin/RegExp.h b/js/src/builtin/RegExp.h
index ee3a2555b28..49885feb4d6 100644
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -48,7 +48,7 @@ RegExpMatcher(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
-                 int32_t lastIndex, bool sticky,
+                 uint32_t lastIndex, bool sticky,
                  MatchPairs* maybeMatches, MutableHandleValue output);
 
 extern bool
@@ -56,7 +56,7 @@ RegExpTester(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
-                int32_t lastIndex, bool sticky, int32_t* endIndex);
+                uint32_t lastIndex, bool sticky, int32_t* endIndex);
 
 /*
  * The following functions are for use by self-hosted code.
diff --git a/js/src/builtin/Sorting.js b/js/src/builtin/Sorting.js
index 26c6c367927..0bd9d7d349b 100644
--- a/js/src/builtin/Sorting.js
+++ b/js/src/builtin/Sorting.js
@@ -65,18 +65,18 @@ function SwapArrayElements(array, i, j) {
 }
 
 // A helper function for MergeSort.
-function Merge(array, start, mid, end, lBuffer, rBuffer, comparefn) {
+function Merge(list, start, mid, end, lBuffer, rBuffer, comparefn) {
     var i, j, k;
 
     var sizeLeft = mid - start + 1;
     var sizeRight =  end - mid;
 
-    // Copy our virtual arrays into separate buffers.
+    // Copy our virtual lists into separate buffers.
     for (i = 0; i < sizeLeft; i++)
-        lBuffer[i] = array[start + i];
+        lBuffer[i] = list[start + i];
 
     for (j = 0; j < sizeRight; j++)
-        rBuffer[j] = array[mid + 1 + j];
+        rBuffer[j] = list[mid + 1 + j];
 
 
     i = 0;
@@ -84,10 +84,10 @@ function Merge(array, start, mid, end, lBuffer, rBuffer, comparefn) {
     k = start;
     while (i < sizeLeft && j < sizeRight) {
         if (comparefn(lBuffer[i], rBuffer[j]) <= 0) {
-            _DefineDataProperty(array, k, lBuffer[i]);
+            list[k] = lBuffer[i];
             i++;
         } else {
-            _DefineDataProperty(array, k, rBuffer[j]);
+            list[k] = rBuffer[j];
             j++;
         }
         k++;
@@ -95,13 +95,13 @@ function Merge(array, start, mid, end, lBuffer, rBuffer, comparefn) {
 
     // Empty out any remaining elements in the buffer.
     while (i < sizeLeft) {
-        _DefineDataProperty(array, k, lBuffer[i]);
+        list[k] =lBuffer[i];
         i++;
         k++;
     }
 
     while (j < sizeRight) {
-        _DefineDataProperty(array, k, rBuffer[j]);
+        list[k] =rBuffer[j];
         j++;
         k++;
     }
@@ -110,7 +110,7 @@ function Merge(array, start, mid, end, lBuffer, rBuffer, comparefn) {
 // Helper function for overwriting a sparse array with a
 // dense array, filling remaining slots with holes.
 function MoveHoles(sparse, sparseLen, dense, denseLen) {
-    for (var i in dense)
+    for (var i = 0; i < denseLen; i++)
         _DefineDataProperty(sparse, i, dense[i]);
     for (var j = denseLen; j < sparseLen; j++)
         delete sparse[j];
@@ -118,22 +118,24 @@ function MoveHoles(sparse, sparseLen, dense, denseLen) {
 
 // Iterative, bottom up, mergesort.
 function MergeSort(array, len, comparefn) {
-    // To save effort we will do all of our work on a dense array,
+    // To save effort we will do all of our work on a dense list,
     // then create holes at the end.
-    var denseArray = [];
+    var denseList = new List();
     var denseLen = 0;
+
     for (var i = 0; i < len; i++) {
         if (i in array)
-            _DefineDataProperty(denseArray, denseLen++, array[i]);
+            denseList[denseLen++] = array[i];
     }
+
     if (denseLen < 1)
         return array;
 
     // Insertion sort for small arrays, where "small" is defined by performance
     // testing.
     if (len < 24) {
-        InsertionSort(denseArray, 0, denseLen - 1, comparefn);
-        MoveHoles(array, len, denseArray, denseLen);
+        InsertionSort(denseList, 0, denseLen - 1, comparefn);
+        MoveHoles(array, len, denseList, denseLen);
         return array;
     }
 
@@ -143,7 +145,7 @@ function MergeSort(array, len, comparefn) {
 
     var mid, end, endOne, endTwo;
     for (var windowSize = 1; windowSize < denseLen; windowSize = 2 * windowSize) {
-        for (var start = 0; start < denseLen - 1; start += 2*windowSize) {
+        for (var start = 0; start < denseLen - 1; start += 2 * windowSize) {
             assert(windowSize < denseLen, "The window size is larger than the array denseLength!");
             // The midpoint between the two subarrays.
             mid = start + windowSize - 1;
@@ -153,10 +155,10 @@ function MergeSort(array, len, comparefn) {
             // Skip lopsided runs to avoid doing useless work
             if (mid > end)
                 continue;
-            Merge(denseArray, start, mid, end, lBuffer, rBuffer, comparefn);
+            Merge(denseList, start, mid, end, lBuffer, rBuffer, comparefn);
         }
     }
-    MoveHoles(array, len, denseArray, denseLen);
+    MoveHoles(array, len, denseList, denseLen);
     return array;
 }
 
diff --git a/js/src/devtools/rootAnalysis/build/gcc.manifest b/js/src/devtools/rootAnalysis/build/gcc.manifest
index 684f5dd3f78..479e8368e6e 100644
--- a/js/src/devtools/rootAnalysis/build/gcc.manifest
+++ b/js/src/devtools/rootAnalysis/build/gcc.manifest
@@ -10,10 +10,11 @@
 "unpack": true
 },
 {
-"size": 12057960,
-"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
+"size": 12072532,
+"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
+"setup": "setup.sh",
 "unpack": true
 }
 ]
diff --git a/js/src/dtoa.c b/js/src/dtoa.c
index 24a4a8ab2ce..1df01cc2ae9 100644
--- a/js/src/dtoa.c
+++ b/js/src/dtoa.c
@@ -526,6 +526,14 @@ destroydtoa
 				FREE((void*)v);
 			}
 		}
+#ifdef Omit_Private_Memory
+	Bigint* p5 = GET_STATE(p5s);
+	while (p5) {
+		Bigint* tmp = p5;
+		p5 = p5->next;
+		FREE(tmp);
+		}
+#endif
 	FREE((void *)state);
 }
 
diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp
index 67033a2495a..54476acfb0f 100644
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -136,7 +136,10 @@ JS_FOR_EACH_TRACEKIND(FINISH_ROOT_LIST)
 #undef FINISH_ROOT_LIST
     FinishPersistentRootedChain(heapRoots_[JS::RootKind::Id]);
     FinishPersistentRootedChain(heapRoots_[JS::RootKind::Value]);
-    FinishPersistentRootedChain(heapRoots_[JS::RootKind::Traceable]);
+
+    // Note that we do not finalize the Traceable list as we do not know how to
+    // safely clear memebers. We instead assert that none escape the RootLists.
+    // See the comment on RootLists::~RootLists for details.
 }
 
 inline void
diff --git a/js/src/jit-test/tests/asm.js/testAtomics.js b/js/src/jit-test/tests/asm.js/testAtomics.js
index 8428e80a710..ad71aad15a4 100644
--- a/js/src/jit-test/tests/asm.js/testAtomics.js
+++ b/js/src/jit-test/tests/asm.js/testAtomics.js
@@ -1819,15 +1819,14 @@ var loadModule_misc = asmCompile('stdlib', 'foreign', 'heap', loadModule_misc_co
 function test_misc(heap) {
     var misc = loadModule_misc(this, {}, heap);
 
-    assertEq(misc.ilf1(), 1);
-    assertEq(misc.ilf2(), 1);
+    assertEq(misc.ilf1(), 1);   // Guaranteed by SpiderMonkey, not spec
+    assertEq(misc.ilf2(), 1);   // Guaranteed by SpiderMonkey, not spec
     assertEq(misc.ilf3(), 0);
-    assertEq(misc.ilf4(), 1);
+    assertEq(misc.ilf4(), 1);   // Guaranteed by SpiderMonkey, not spec
     assertEq(misc.ilf5(), 0);
     assertEq(misc.ilf6(), 0);
     assertEq(misc.ilf7(), 0);
-    var v = misc.ilf8();
-    assertEq(v === 0 || v === 1, true);
+    assertEq(misc.ilf8(), 0);   // Required by spec, for now
     assertEq(misc.ilf9(), 0);
 }
 
diff --git a/js/src/jit-test/tests/asm.js/testJumpRange.js b/js/src/jit-test/tests/asm.js/testJumpRange.js
new file mode 100644
index 00000000000..4487cd9cef8
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testJumpRange.js
@@ -0,0 +1,51 @@
+load(libdir + "asm.js");
+load(libdir + "asserts.js");
+
+var fatFunc = USE_ASM + '\n';
+for (var i = 0; i < 100; i++)
+    fatFunc += "function f" + i + "() { return ((f" + (i+1) + "()|0)+1)|0 }\n";
+fatFunc += "function f100() { return 42 }\n";
+fatFunc += "return f0";
+
+for (var signals = 0; signals <= 1; signals++) {
+    setJitCompilerOption("signals.enable", signals);
+
+    for (let threshold of [0, 50, 100, 5000, -1]) {
+        setJitCompilerOption("jump-threshold", threshold);
+
+        assertEq(asmCompile(
+            USE_ASM + `
+                function h() { return ((g()|0)+2)|0 }
+                function g() { return ((f()|0)+1)|0 }
+                function f() { return 42 }
+                return h
+            `)()(), 45);
+
+        if (isSimdAvailable() && this.SIMD) {
+            var buf = new ArrayBuffer(BUF_MIN);
+            new Int32Array(buf)[0] = 10;
+            new Float32Array(buf)[1] = 42;
+            assertEq(asmCompile('stdlib', 'ffis', 'buf',
+                USE_ASM + `
+                    var H = new stdlib.Uint8Array(buf);
+                    var i4 = stdlib.SIMD.Int32x4;
+                    var f4 = stdlib.SIMD.Float32x4;
+                    var i4load = i4.load;
+                    var f4load = f4.load;
+                    var toi4 = i4.fromFloat32x4;
+                    var i4ext = i4.extractLane;
+                    function f(i) { i=i|0; return i4ext(i4load(H, i), 0)|0 }
+                    function g(i) { i=i|0; return (i4ext(toi4(f4load(H, i)),1) + (f(i)|0))|0 }
+                    function h(i) { i=i|0; return g(i)|0 }
+                    return h
+                `)(this, null, buf)(0), 52);
+        }
+
+        enableSPSProfiling();
+        asmLink(asmCompile(USE_ASM + 'function f() {} function g() { f() } function h() { g() } return h'))();
+        disableSPSProfiling();
+
+        assertEq(asmCompile(fatFunc)()(), 142);
+    }
+}
+
diff --git a/js/src/jit-test/tests/asm.js/testProfiling.js b/js/src/jit-test/tests/asm.js/testProfiling.js
index 69d99467925..2de9adf9a70 100644
--- a/js/src/jit-test/tests/asm.js/testProfiling.js
+++ b/js/src/jit-test/tests/asm.js/testProfiling.js
@@ -215,6 +215,15 @@ if (isSimdAvailable() && typeof SIMD !== 'undefined') {
 }
 
 
+// Thunks
+setJitCompilerOption("jump-threshold", 0);
+var h = asmLink(asmCompile(USE_ASM + 'function f() {} function g() { f() } function h() { g() } return h'));
+enableSingleStepProfiling();
+h();
+var stacks = disableSingleStepProfiling();
+assertStackContainsSeq(stacks, ">,h,>,g,h,>,f,g,h,>,g,h,>,h,>,>");
+setJitCompilerOption("jump-threshold", -1);
+
 // This takes forever to run.
 // Stack-overflow exit test
 //var limit = -1;
diff --git a/js/src/jit-test/tests/atomics/basic-tests.js b/js/src/jit-test/tests/atomics/basic-tests.js
index 2ccbaef800e..c6f5f23e0b4 100644
--- a/js/src/jit-test/tests/atomics/basic-tests.js
+++ b/js/src/jit-test/tests/atomics/basic-tests.js
@@ -375,23 +375,25 @@ function adHocExchange() {
     assertEq(exchangeLoop(a), -100000);
 }
 
+// isLockFree(n) may return true only if there is an integer array
+// on which atomic operations is allowed whose byte size is n,
+// ie, it must return false for n=8.
+//
+// SpiderMonkey has isLockFree(1), isLockFree(2), isLockFree(4) on all
+// supported platforms, though this is not guaranteed by the spec.
+
 var sizes   = [    1,     2,     3,     4,     5,     6,     7,  8,
                    9,    10,    11,    12];
-var answers = [ true,  true, false,  true, false, false, false, {},
+var answers = [ true,  true, false,  true, false, false, false, false,
 	       false, false, false, false];
 
 function testIsLockFree() {
-    var saved8 = "Invalid";
-
     // This ought to defeat most compile-time resolution.
     for ( var i=0 ; i < sizes.length ; i++ ) {
 	var v = Atomics.isLockFree(sizes[i]);
 	var a = answers[i];
 	assertEq(typeof v, 'boolean');
-	if (typeof a == 'boolean')
-	    assertEq(v, a);
-	else
-	    saved8 = v;
+	assertEq(v, a);
     }
 
     // This ought to be optimizable.
@@ -402,7 +404,7 @@ function testIsLockFree() {
     assertEq(Atomics.isLockFree(5), false);
     assertEq(Atomics.isLockFree(6), false);
     assertEq(Atomics.isLockFree(7), false);
-    assertEq(Atomics.isLockFree(8), saved8);
+    assertEq(Atomics.isLockFree(8), false);
     assertEq(Atomics.isLockFree(9), false);
     assertEq(Atomics.isLockFree(10), false);
     assertEq(Atomics.isLockFree(11), false);
diff --git a/js/src/jit-test/tests/self-hosting/method-called-on-incompatible.js b/js/src/jit-test/tests/self-hosting/method-called-on-incompatible.js
new file mode 100644
index 00000000000..2d111cc3a4e
--- /dev/null
+++ b/js/src/jit-test/tests/self-hosting/method-called-on-incompatible.js
@@ -0,0 +1,9 @@
+load(libdir + "asserts.js");
+
+assertTypeErrorMessage(() => WeakSet.prototype.add.call({}), "add method called on incompatible Object");
+assertTypeErrorMessage(() => newGlobal().WeakSet.prototype.add.call({}), "add method called on incompatible Object");
+assertTypeErrorMessage(() => WeakSet.prototype.add.call(15), "add method called on incompatible number");
+
+assertTypeErrorMessage(() => Int8Array.prototype.find.call({}), "find method called on incompatible Object");
+assertTypeErrorMessage(() => newGlobal().Int8Array.prototype.find.call({}), "find method called on incompatible Object");
+assertTypeErrorMessage(() => Int8Array.prototype.find.call(15), "find method called on incompatible number");
diff --git a/js/src/jit-test/tests/wasm/binary.js b/js/src/jit-test/tests/wasm/binary.js
index cd23ff495e6..259625a25a0 100644
--- a/js/src/jit-test/tests/wasm/binary.js
+++ b/js/src/jit-test/tests/wasm/binary.js
@@ -18,6 +18,7 @@ const declSectionStr = "decl";
 const importSectionStr = "import";
 const exportSectionStr = "export";
 const codeSectionStr = "code";
+const dataSectionStr = "data";
 const funcSubsectionStr = "func";
 
 const magicError = /failed to match magic number/;
@@ -170,3 +171,5 @@ wasmEval(toBuf(moduleWithSections([
     importSection([{sigIndex:0, module:"a", func:""}]),
     trivialDeclSection,
     trivialCodeSection])), {a:()=>{}});
+
+assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: dataSectionStr, body: [], } ]))), Error, /data section requires a memory section/);
diff --git a/js/src/jit/AtomicOperations.h b/js/src/jit/AtomicOperations.h
index 718c57182ea..425ab0546df 100644
--- a/js/src/jit/AtomicOperations.h
+++ b/js/src/jit/AtomicOperations.h
@@ -147,13 +147,19 @@ class AtomicOperations
     static inline void memmoveSafeWhenRacy(void* dest, const void* src, size_t nbytes);
 
   public:
-    // Test lock-freedom for any integer value.
+    // Test lock-freedom for any int32 value.  This implements the
+    // Atomics::isLockFree() operation in the Shared Memory and
+    // Atomics specification, as follows:
     //
-    // This implements a platform-independent pattern, as follows:
+    // 1, 2, and 4 bytes are always lock free (in SpiderMonkey).
     //
-    // 1, 2, and 4 bytes are always lock free, lock-freedom for 8
-    // bytes is determined by the platform's isLockfree8(), and there
-    // is no lock-freedom for any other values on any platform.
+    // Lock-freedom for 8 bytes is determined by the platform's
+    // isLockfree8().  However, the spec stipulates that isLockFree(8)
+    // is true only if there is an integer array that admits atomic
+    // operations whose BYTES_PER_ELEMENT=8; at the moment (February
+    // 2016) there are no such arrays.
+    //
+    // There is no lock-freedom for any other values on any platform.
     static inline bool isLockfree(int32_t n);
 
     // If the return value is true then a call to the 64-bit (8-byte)
@@ -297,7 +303,12 @@ AtomicOperations::isLockfree(int32_t size)
       case 4:
         return true;
       case 8:
-        return AtomicOperations::isLockfree8();
+        // The spec requires Atomics.isLockFree(n) to return false
+        // unless n is the BYTES_PER_ELEMENT value of some integer
+        // TypedArray that admits atomic operations.  At the time of
+        // writing (February 2016) there is no such array with n=8.
+        // return AtomicOperations::isLockfree8();
+        return false;
       default:
         return false;
     }
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index 84ce2c306ec..2e8281e207a 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1751,7 +1751,7 @@ class OutOfLineRegExpMatcher : public OutOfLineCodeBase
 };
 
 typedef bool (*RegExpMatcherRawFn)(JSContext* cx, HandleObject regexp, HandleString input,
-                                   int32_t lastIndex, bool sticky,
+                                   uint32_t lastIndex, bool sticky,
                                    MatchPairs* pairs, MutableHandleValue output);
 static const VMFunction RegExpMatcherRawInfo = FunctionInfo(RegExpMatcherRaw);
 
@@ -1916,7 +1916,7 @@ class OutOfLineRegExpTester : public OutOfLineCodeBase
 };
 
 typedef bool (*RegExpTesterRawFn)(JSContext* cx, HandleObject regexp, HandleString input,
-                                  int32_t lastIndex, bool sticky, int32_t* result);
+                                  uint32_t lastIndex, bool sticky, int32_t* result);
 static const VMFunction RegExpTesterRawInfo = FunctionInfo(RegExpTesterRaw);
 
 void
@@ -9817,19 +9817,14 @@ CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir)
     Register value = ToRegister(lir->value());
     Register output = ToRegister(lir->output());
 
-    // Keep this in sync with isLockfree() in jit/AtomicOperations-inl.h.
+    // Keep this in sync with isLockfree() in jit/AtomicOperations.h.
+    MOZ_ASSERT(!AtomicOperations::isLockfree(8));
 
     Label Ldone, Lfailed;
     masm.move32(Imm32(1), output);
-    if (AtomicOperations::isLockfree8())
-        masm.branch32(Assembler::Equal, value, Imm32(8), &Ldone);
-    else
-        masm.branch32(Assembler::Equal, value, Imm32(8), &Lfailed);
     masm.branch32(Assembler::Equal, value, Imm32(4), &Ldone);
     masm.branch32(Assembler::Equal, value, Imm32(2), &Ldone);
     masm.branch32(Assembler::Equal, value, Imm32(1), &Ldone);
-    if (!AtomicOperations::isLockfree8())
-        masm.bind(&Lfailed);
     masm.move32(Imm32(0), output);
     masm.bind(&Ldone);
 }
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index a536f5a5e93..ec93970fbdc 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2458,7 +2458,7 @@ IonBuilder::finishLoop(CFGState& state, MBasicBlock* successor)
 }
 
 IonBuilder::ControlStatus
-IonBuilder::restartLoop(CFGState state)
+IonBuilder::restartLoop(const CFGState& state)
 {
     spew("New types at loop header, restarting loop body");
 
@@ -2486,6 +2486,12 @@ IonBuilder::restartLoop(CFGState state)
 
     loopDepth_++;
 
+    // Keep a local copy for these pointers since state will be overwritten in
+    // pushLoop since state is a reference to cfgStack_.back()
+    jsbytecode* condpc = state.loop.condpc;
+    jsbytecode* updatepc = state.loop.updatepc;
+    jsbytecode* updateEnd = state.loop.updateEnd;
+
     if (!pushLoop(state.loop.initialState, state.loop.initialStopAt, header, state.loop.osr,
                   state.loop.loopHead, state.loop.initialPc,
                   state.loop.bodyStart, state.loop.bodyEnd,
@@ -2496,9 +2502,9 @@ IonBuilder::restartLoop(CFGState state)
 
     CFGState& nstate = cfgStack_.back();
 
-    nstate.loop.condpc = state.loop.condpc;
-    nstate.loop.updatepc = state.loop.updatepc;
-    nstate.loop.updateEnd = state.loop.updateEnd;
+    nstate.loop.condpc = condpc;
+    nstate.loop.updatepc = updatepc;
+    nstate.loop.updateEnd = updateEnd;
 
     // Don't specializePhis(), as the header has been visited before and the
     // phis have already had their type set.
diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
index aeb4e249056..296883bf997 100644
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -317,7 +317,7 @@ class IonBuilder
 
     // Restarts processing of a loop if the type information at its header was
     // incomplete.
-    ControlStatus restartLoop(CFGState state);
+    ControlStatus restartLoop(const CFGState& state);
 
     void assertValidLoopHeadOp(jsbytecode* pc);
 
diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp
index 8d43464ad5f..32f52da7a94 100644
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -161,6 +161,10 @@ DefaultJitOptions::DefaultJitOptions()
     // The bytecode length limit for small function.
     SET_DEFAULT(smallFunctionMaxBytecodeLength_, 120);
 
+    // An artificial testing limit for the maximum supported offset of
+    // pc-relative jump and call instructions.
+    SET_DEFAULT(jumpThreshold, UINT32_MAX);
+
     // Force how many invocation or loop iterations are needed before compiling
     // a function with the highest ionmonkey optimization level.
     // (i.e. OptimizationLevel_Normal)
diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h
index 68a10b5a81a..84ab7b9a25d 100644
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -72,6 +72,7 @@ struct DefaultJitOptions
     uint32_t maxStackArgs;
     uint32_t osrPcMismatchesBeforeRecompile;
     uint32_t smallFunctionMaxBytecodeLength_;
+    uint32_t jumpThreshold;
     mozilla::Maybe forcedDefaultIonWarmUpThreshold;
     mozilla::Maybe forcedRegisterAllocator;
 
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index cae4920fd8d..fef1364bc6e 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2475,6 +2475,17 @@ LIRGenerator::visitMonitorTypes(MMonitorTypes* ins)
     add(lir, ins);
 }
 
+// Returns true iff |def| is a constant that's either not a GC thing or is not
+// allocated in the nursery.
+static bool
+IsNonNurseryConstant(MDefinition* def)
+{
+    if (!def->isConstant())
+        return false;
+    Value v = def->toConstant()->value();
+    return !v.isMarkable() || !IsInsideNursery(v.toGCThing());
+}
+
 void
 LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier* ins)
 {
@@ -2484,9 +2495,7 @@ LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier* ins)
     // object is tenured, and does not need to be tested for being in the
     // nursery. Ensure that assumption holds by lowering constant nursery
     // objects to a register.
-    bool useConstantObject =
-        ins->object()->isConstant() &&
-        !IsInsideNursery(&ins->object()->toConstant()->value().toObject());
+    bool useConstantObject = IsNonNurseryConstant(ins->object());
 
     switch (ins->value()->type()) {
       case MIRType_Object:
@@ -3641,6 +3650,7 @@ LIRGenerator::visitSetPropertyCache(MSetPropertyCache* ins)
     // If this is a SETPROP, the id is a constant string. Allow passing it as a
     // constant to reduce register allocation pressure.
     bool useConstId = id->type() == MIRType_String || id->type() == MIRType_Symbol;
+    bool useConstValue = IsNonNurseryConstant(ins->value());
 
     // Set the performs-call flag so that we don't omit the overrecursed check.
     // This is necessary because the cache can attach a scripted setter stub
@@ -3663,7 +3673,7 @@ LIRGenerator::visitSetPropertyCache(MSetPropertyCache* ins)
     LInstruction* lir = new(alloc()) LSetPropertyCache(useRegister(ins->object()), temp(),
                                                        tempToUnboxIndex, tempD, tempF32);
     useBoxOrTypedOrConstant(lir, LSetPropertyCache::Id, id, useConstId);
-    useBoxOrTypedOrConstant(lir, LSetPropertyCache::Value, ins->value(), /* useConstant = */ true);
+    useBoxOrTypedOrConstant(lir, LSetPropertyCache::Value, ins->value(), useConstValue);
 
     add(lir, ins);
     assignSafepoint(lir, ins);
diff --git a/js/src/jit/MacroAssembler-inl.h b/js/src/jit/MacroAssembler-inl.h
index e6d69f54112..6b1d53a2c01 100644
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -85,13 +85,6 @@ MacroAssembler::call(const wasm::CallSiteDesc& desc, const Register reg)
     append(desc, l, framePushed());
 }
 
-void
-MacroAssembler::call(const wasm::CallSiteDesc& desc, Label* label)
-{
-    CodeOffset l = call(label);
-    append(desc, l, framePushed());
-}
-
 void
 MacroAssembler::call(const wasm::CallSiteDesc& desc, AsmJSInternalCallee callee)
 {
diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h
index 5be1a77f1b5..784dc5c77bc 100644
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -498,7 +498,6 @@ class MacroAssembler : public MacroAssemblerSpecific
     void call(JitCode* c) PER_SHARED_ARCH;
 
     inline void call(const wasm::CallSiteDesc& desc, const Register reg);
-    inline void call(const wasm::CallSiteDesc& desc, Label* label);
     inline void call(const wasm::CallSiteDesc& desc, AsmJSInternalCallee callee);
 
     CodeOffset callWithPatch() PER_SHARED_ARCH;
@@ -509,6 +508,7 @@ class MacroAssembler : public MacroAssemblerSpecific
     // CodeOffset instead of a CodeOffsetJump).
     CodeOffset thunkWithPatch() PER_SHARED_ARCH;
     void patchThunk(uint32_t thunkOffset, uint32_t targetOffset) PER_SHARED_ARCH;
+    static void repatchThunk(uint8_t* code, uint32_t thunkOffset, uint32_t targetOffset) PER_SHARED_ARCH;
 
     // Push the return address and make a call. On platforms where this function
     // is not defined, push the link register (pushReturnAddress) at the entry
diff --git a/js/src/jit/arm/Architecture-arm.h b/js/src/jit/arm/Architecture-arm.h
index 237240ccf65..5feaf418c9e 100644
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -36,6 +36,10 @@ static const int32_t NUNBOX32_TYPE_OFFSET    = 4;
 static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0;
 
 static const uint32_t ShadowStackSpace = 0;
+
+// How far forward/back can a jump go? Provide a generous buffer for thunks.
+static const uint32_t JumpImmediateRange = 25 * 1024 * 1024;
+
 ////
 // These offsets are related to bailouts.
 ////
diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp
index 55501223a62..6e138a2ff82 100644
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -10,6 +10,7 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MathAlgorithms.h"
 
+#include "asmjs/WasmBinary.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
@@ -4957,6 +4958,9 @@ MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
 CodeOffset
 MacroAssembler::thunkWithPatch()
 {
+    static_assert(32 * 1024 * 1024 - JumpImmediateRange > wasm::MaxFuncs * 3 * sizeof(Instruction),
+                  "always enough space for thunks");
+
     // The goal of the thunk is to be able to jump to any address without the
     // usual 32MiB branch range limitation. Additionally, to make the thunk
     // simple to use, the thunk does not use the constant pool or require
@@ -4996,6 +5000,17 @@ MacroAssembler::patchThunk(uint32_t u32Offset, uint32_t targetOffset)
     *u32 = (targetOffset - addOffset) - 8;
 }
 
+void
+MacroAssembler::repatchThunk(uint8_t* code, uint32_t u32Offset, uint32_t targetOffset)
+{
+    uint32_t* u32 = reinterpret_cast(code + u32Offset);
+
+    uint32_t addOffset = u32Offset - 4;
+    MOZ_ASSERT(reinterpret_cast(code + addOffset)->is());
+
+    *u32 = (targetOffset - addOffset) - 8;
+}
+
 void
 MacroAssembler::pushReturnAddress()
 {
diff --git a/js/src/jit/arm64/Architecture-arm64.h b/js/src/jit/arm64/Architecture-arm64.h
index cc4180e58c9..374c02cdcc8 100644
--- a/js/src/jit/arm64/Architecture-arm64.h
+++ b/js/src/jit/arm64/Architecture-arm64.h
@@ -298,6 +298,11 @@ static const uint32_t ION_FRAME_SLACK_SIZE = 24;
 
 static const uint32_t ShadowStackSpace = 0;
 
+// TODO:
+// This constant needs to be updated to account for whatever near/far branching
+// strategy is used by ARM64.
+static const uint32_t JumpImmediateRange = UINT32_MAX;
+
 static const uint32_t ABIStackAlignment = 16;
 static const uint32_t CodeAlignment = 16;
 static const bool StackKeptAligned = false;
diff --git a/js/src/jit/arm64/MacroAssembler-arm64.cpp b/js/src/jit/arm64/MacroAssembler-arm64.cpp
index bc3ba76b87a..4ebfb2276b8 100644
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -578,6 +578,12 @@ MacroAssembler::patchThunk(uint32_t thunkOffset, uint32_t targetOffset)
     MOZ_CRASH("NYI");
 }
 
+void
+MacroAssembler::repatchThunk(uint8_t* code, uint32_t thunkOffset, uint32_t targetOffset)
+{
+    MOZ_CRASH("NYI");
+}
+
 void
 MacroAssembler::pushReturnAddress()
 {
diff --git a/js/src/jit/arm64/MacroAssembler-arm64.h b/js/src/jit/arm64/MacroAssembler-arm64.h
index ef347958017..2c8365f4961 100644
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -2474,14 +2474,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
     void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label* label);
     void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label);
 
-    void appendCallSite(const wasm::CallSiteDesc& desc) {
-        MOZ_CRASH("appendCallSite");
-    }
-
-    void callExit(wasm::SymbolicAddress imm, uint32_t stackArgBytes) {
-        MOZ_CRASH("callExit");
-    }
-
     void profilerEnterFrame(Register framePtr, Register scratch) {
         AbsoluteAddress activation(GetJitContext()->runtime->addressOfProfilingActivation());
         loadPtr(activation, scratch);
diff --git a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
index edcf400e8c0..151b854085d 100644
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
@@ -1168,6 +1168,12 @@ MacroAssembler::patchThunk(uint32_t callerOffset, uint32_t calleeOffset)
     MOZ_CRASH("NYI");
 }
 
+void
+MacroAssembler::repatchThunk(uint8_t* code, uint32_t callerOffset, uint32_t calleeOffset)
+{
+    MOZ_CRASH("NYI");
+}
+
 void
 MacroAssembler::call(wasm::SymbolicAddress target)
 {
diff --git a/js/src/jit/none/Architecture-none.h b/js/src/jit/none/Architecture-none.h
index 752a38adcd5..f6118bead5a 100644
--- a/js/src/jit/none/Architecture-none.h
+++ b/js/src/jit/none/Architecture-none.h
@@ -141,6 +141,7 @@ inline bool hasUnaliasedDouble() { MOZ_CRASH(); }
 inline bool hasMultiAlias() { MOZ_CRASH(); }
 
 static const uint32_t ShadowStackSpace = 0;
+static const uint32_t JumpImmediateRange = INT32_MAX;
 
 #ifdef JS_NUNBOX32
 static const int32_t NUNBOX32_TYPE_OFFSET = 4;
diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h
index f869e81979d..1f24ece9664 100644
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -754,7 +754,8 @@ class AssemblerShared
     void append(wasm::JumpTarget target, uint32_t offset) {
         enoughMemory_ &= jumpsites_[target].append(offset);
     }
-    const wasm::JumpSiteArray& jumpSites() const { return jumpsites_; }
+    const wasm::JumpSiteArray& jumpSites() { return jumpsites_; }
+    void clearJumpSites() { for (auto& v : jumpsites_) v.clear(); }
 
     void append(wasm::HeapAccess access) { enoughMemory_ &= heapAccesses_.append(access); }
     wasm::HeapAccessVector&& extractHeapAccesses() { return Move(heapAccesses_); }
diff --git a/js/src/jit/x86-shared/Architecture-x86-shared.h b/js/src/jit/x86-shared/Architecture-x86-shared.h
index ec4b0f60a12..8e58bd0131e 100644
--- a/js/src/jit/x86-shared/Architecture-x86-shared.h
+++ b/js/src/jit/x86-shared/Architecture-x86-shared.h
@@ -57,6 +57,8 @@ static const uint32_t ShadowStackSpace = 32;
 static const uint32_t ShadowStackSpace = 0;
 #endif
 
+static const uint32_t JumpImmediateRange = INT32_MAX;
+
 class Registers {
   public:
     typedef uint8_t Code;
diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h
index 78effa036f8..03263b7d14b 100644
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -1028,9 +1028,12 @@ class AssemblerX86Shared : public AssemblerShared
     CodeOffset thunkWithPatch() {
         return CodeOffset(masm.jmp().offset());
     }
-    void patchThunk(uint32_t jumpOffset, uint32_t targetOffset) {
+    void patchThunk(uint32_t thunkOffset, uint32_t targetOffset) {
         unsigned char* code = masm.data();
-        X86Encoding::SetRel32(code + jumpOffset, code + targetOffset);
+        X86Encoding::SetRel32(code + thunkOffset, code + targetOffset);
+    }
+    static void repatchThunk(uint8_t* code, uint32_t thunkOffset, uint32_t targetOffset) {
+        X86Encoding::SetRel32(code + thunkOffset, code + targetOffset);
     }
 
     void breakpoint() {
diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
index 4044d1b54aa..7f27c9e3c72 100644
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
@@ -551,9 +551,15 @@ MacroAssembler::thunkWithPatch()
 }
 
 void
-MacroAssembler::patchThunk(uint32_t jumpOffset, uint32_t targetOffset)
+MacroAssembler::patchThunk(uint32_t thunkOffset, uint32_t targetOffset)
 {
-    Assembler::patchThunk(jumpOffset, targetOffset);
+    Assembler::patchThunk(thunkOffset, targetOffset);
+}
+
+void
+MacroAssembler::repatchThunk(uint8_t* code, uint32_t thunkOffset, uint32_t targetOffset)
+{
+    Assembler::repatchThunk(code, thunkOffset, targetOffset);
 }
 
 void
diff --git a/js/src/jsapi-tests/testGCExactRooting.cpp b/js/src/jsapi-tests/testGCExactRooting.cpp
index 01bb1fdf08a..f50fb466b6a 100644
--- a/js/src/jsapi-tests/testGCExactRooting.cpp
+++ b/js/src/jsapi-tests/testGCExactRooting.cpp
@@ -95,7 +95,7 @@ BEGIN_TEST(testGCRootedStaticStructInternalStackStorageAugmented)
         bool same;
 
         // Automatic move from stack to heap.
-        JS::PersistentRooted heap(cx, container);
+        JS::PersistentRooted heap(rt, container);
 
         // clear prior rooting.
         container.obj() = nullptr;
@@ -125,6 +125,42 @@ BEGIN_TEST(testGCRootedStaticStructInternalStackStorageAugmented)
 }
 END_TEST(testGCRootedStaticStructInternalStackStorageAugmented)
 
+static JS::PersistentRooted sLongLived;
+BEGIN_TEST(testGCPersistentRootedOutlivesRuntime)
+{
+    JSContext* cx2 = JS_NewContext(rt, 8192);
+    CHECK(cx2);
+
+    sLongLived.init(cx2, JS_NewObject(cx, nullptr));
+    CHECK(sLongLived);
+
+    JS_DestroyContext(cx2);
+    CHECK(!sLongLived);
+
+    return true;
+}
+END_TEST(testGCPersistentRootedOutlivesRuntime)
+
+// Unlike the above, the following test is an example of an invalid usage: for
+// performance and simplicity reasons, PersistentRooted is not
+// allowed to outlive the container it belongs to. The following commented out
+// test can be used to verify that the relevant assertion fires as expected.
+static JS::PersistentRooted sContainer;
+BEGIN_TEST(testGCPersistentRootedTraceableCannotOutliveRuntime)
+{
+    JS::Rooted container(cx);
+    container.obj() = JS_NewObject(cx, nullptr);
+    container.str() = JS_NewStringCopyZ(cx, "Hello");
+    sContainer.init(rt, container);
+
+    // Commenting the following line will trigger an assertion that the
+    // PersistentRooted outlives the runtime it is attached to.
+    sContainer.reset();
+
+    return true;
+}
+END_TEST(testGCPersistentRootedTraceableCannotOutliveRuntime)
+
 using MyHashMap = js::GCHashMap;
 
 BEGIN_TEST(testGCRootedHashMap)
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index 646a7003f96..5ace21b81bb 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5193,13 +5193,36 @@ JS_Stringify(JSContext* cx, MutableHandleValue vp, HandleObject replacer,
     StringBuffer sb(cx);
     if (!sb.ensureTwoByteChars())
         return false;
-    if (!Stringify(cx, vp, replacer, space, sb))
+    if (!Stringify(cx, vp, replacer, space, sb, StringifyBehavior::Normal))
         return false;
     if (sb.empty() && !sb.append(cx->names().null))
         return false;
     return callback(sb.rawTwoByteBegin(), sb.length(), data);
 }
 
+JS_PUBLIC_API(bool)
+JS::ToJSONMaybeSafely(JSContext* cx, JS::HandleObject input,
+                      JSONWriteCallback callback, void* data)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, input);
+
+    StringBuffer sb(cx);
+    if (!sb.ensureTwoByteChars())
+        return false;
+
+    RootedValue inputValue(cx, ObjectValue(*input));
+    if (!Stringify(cx, &inputValue, nullptr, NullHandleValue, sb,
+                   StringifyBehavior::RestrictedSafe))
+        return false;
+
+    if (sb.empty() && !sb.append(cx->names().null))
+        return false;
+
+    return callback(sb.rawTwoByteBegin(), sb.length(), data);
+}
+
 JS_PUBLIC_API(bool)
 JS_ParseJSON(JSContext* cx, const char16_t* chars, uint32_t len, MutableHandleValue vp)
 {
@@ -5866,6 +5889,13 @@ JS_SetGlobalJitCompilerOption(JSRuntime* rt, JSJitCompilerOption opt, uint32_t v
             JitSpew(js::jit::JitSpew_IonScripts, "Disable signals");
         }
         break;
+      case JSJITCOMPILER_JUMP_THRESHOLD:
+        if (value == uint32_t(-1)) {
+            jit::DefaultJitOptions defaultValues;
+            value = defaultValues.jumpThreshold;
+        }
+        jit::JitOptions.jumpThreshold = value;
+        break;
       default:
         break;
     }
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
index 8d5d91bda3a..f30bc0eb94d 100644
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4754,6 +4754,31 @@ JS_PUBLIC_API(bool)
 JS_Stringify(JSContext* cx, JS::MutableHandleValue value, JS::HandleObject replacer,
              JS::HandleValue space, JSONWriteCallback callback, void* data);
 
+namespace JS {
+
+/**
+ * An API akin to JS_Stringify but with the goal of not having observable
+ * side-effects when the stringification is performed.  This means it does not
+ * allow a replacer or a custom space, and has the following constraints on its
+ * input:
+ *
+ * 1) The input must be a plain object or array, not an abitrary value.
+ * 2) Every value in the graph reached by the algorithm starting with this
+ *    object must be one of the following: null, undefined, a string (NOT a
+ *    string object!), a boolean, a finite number (i.e. no NaN or Infinity or
+ *    -Infinity), a plain object with no accessor properties, or an Array with
+ *    no holes.
+ *
+ * The actual behavior differs from JS_Stringify only in asserting the above and
+ * NOT attempting to get the "toJSON" property from things, since that could
+ * clearly have side-effects.
+ */
+JS_PUBLIC_API(bool)
+ToJSONMaybeSafely(JSContext* cx, JS::HandleObject input,
+                  JSONWriteCallback callback, void* data);
+
+} /* namespace JS */
+
 /**
  * JSON.parse as specified by ES5.
  */
@@ -5282,7 +5307,8 @@ JS_SetOffthreadIonCompilationEnabled(JSRuntime* rt, bool enabled);
     Register(ION_ENABLE, "ion.enable")                                     \
     Register(BASELINE_ENABLE, "baseline.enable")                           \
     Register(OFFTHREAD_COMPILATION_ENABLE, "offthread-compilation.enable") \
-    Register(SIGNALS_ENABLE, "signals.enable")
+    Register(SIGNALS_ENABLE, "signals.enable")                             \
+    Register(JUMP_THRESHOLD, "jump-threshold")
 
 typedef enum JSJitCompilerOption {
 #define JIT_COMPILER_DECLARE(key, str) \
diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp
index 4ffaa497d40..dd74f989ccf 100644
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -700,6 +700,26 @@ ParseDigitsN(size_t n, size_t* result, const CharT* s, size_t* i, size_t limit)
     return false;
 }
 
+/*
+ * Read and convert n or less decimal digits from s[*i]
+ * to s[min(*i+n,limit)] into *result.
+ *
+ * Succeed only if greater than zero but less than or equal to n digits are
+ * converted. Advance *i only on success.
+ */
+template 
+static bool
+ParseDigitsNOrLess(size_t n, size_t* result, const CharT* s, size_t* i, size_t limit)
+{
+    size_t init = *i;
+
+    if (ParseDigits(result, s, i, Min(limit, init + n)))
+        return ((*i - init) > 0) && ((*i - init) <= n);
+
+    *i = init;
+    return false;
+}
+
 static int
 DaysInMonth(int year, int month)
 {
@@ -713,11 +733,6 @@ DaysInMonth(int year, int month)
  * "NOTE-datetime" specification. These formats make up a restricted
  * profile of the ISO 8601 format. Quoted here:
  *
- *   The formats are as follows. Exactly the components shown here
- *   must be present, with exactly this punctuation. Note that the "T"
- *   appears literally in the string, to indicate the beginning of the
- *   time element, as specified in ISO 8601.
- *
  *   Any combination of the date formats with the time formats is
  *   allowed, and also either the date or the time can be missing.
  *
@@ -730,6 +745,12 @@ DaysInMonth(int year, int month)
  *   00:00 UTC.  If the time is present but the time zone field is
  *   missing then we use local time.
  *
+ * For the sake of cross compatibility with other implementations we
+ * make a few exceptions to the standard: months, days, hours, minutes
+ * and seconds may be either one or two digits long, and the 'T' from
+ * the time part may be replaced with a space. Given that, a date time
+ * like "1999-1-1 1:1:1" will parse successfully.
+ *
  * Date part:
  *
  *  Year:
@@ -755,17 +776,17 @@ DaysInMonth(int year, int month)
  * where:
  *
  *   YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
- *   MM   = two-digit month (01=January, etc.)
- *   DD   = two-digit day of month (01 through 31)
- *   hh   = two digits of hour (00 through 23) (am/pm NOT allowed)
- *   mm   = two digits of minute (00 through 59)
- *   ss   = two digits of second (00 through 59)
+ *   MM   = one or two-digit month (01=January, etc.)
+ *   DD   = one or two-digit day of month (01 through 31)
+ *   hh   = one or two digits of hour (00 through 23) (am/pm NOT allowed)
+ *   mm   = one or two digits of minute (00 through 59)
+ *   ss   = one or two digits of second (00 through 59)
  *   s    = one or more digits representing a decimal fraction of a second
  *   TZD  = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
  */
 template 
 static bool
-ParseISODate(const CharT* s, size_t length, ClippedTime* result)
+ParseISOStyleDate(const CharT* s, size_t length, ClippedTime* result)
 {
     size_t i = 0;
     int tzMul = 1;
@@ -795,6 +816,9 @@ ParseISODate(const CharT* s, size_t length, ClippedTime* result)
 #define NEED_NDIGITS(n, field)                                                 \
     if (!ParseDigitsN(n, &field, s, &i, length)) { return false; }
 
+#define NEED_NDIGITS_OR_LESS(n, field)                                         \
+    if (!ParseDigitsNOrLess(n, &field, s, &i, length)) { return false; }
+
     if (PEEK('+') || PEEK('-')) {
         if (PEEK('-'))
             dateMul = -1;
@@ -804,19 +828,23 @@ ParseISODate(const CharT* s, size_t length, ClippedTime* result)
         NEED_NDIGITS(4, year);
     }
     DONE_DATE_UNLESS('-');
-    NEED_NDIGITS(2, month);
+    NEED_NDIGITS_OR_LESS(2, month);
     DONE_DATE_UNLESS('-');
-    NEED_NDIGITS(2, day);
+    NEED_NDIGITS_OR_LESS(2, day);
 
  done_date:
-    DONE_UNLESS('T');
-    NEED_NDIGITS(2, hour);
+    if (PEEK('T') || PEEK(' '))
+        i++;
+    else
+        goto done;
+
+    NEED_NDIGITS_OR_LESS(2, hour);
     NEED(':');
-    NEED_NDIGITS(2, min);
+    NEED_NDIGITS_OR_LESS(2, min);
 
     if (PEEK(':')) {
         ++i;
-        NEED_NDIGITS(2, sec);
+        NEED_NDIGITS_OR_LESS(2, sec);
         if (PEEK('.')) {
             ++i;
             if (!ParseFractional(&frac, s, &i, length))
@@ -882,7 +910,7 @@ template 
 static bool
 ParseDate(const CharT* s, size_t length, ClippedTime* result)
 {
-    if (ParseISODate(s, length, result))
+    if (ParseISOStyleDate(s, length, result))
         return true;
 
     if (length == 0)
diff --git a/js/src/jsdtoa.cpp b/js/src/jsdtoa.cpp
index a7b7503449c..14a111f68e3 100644
--- a/js/src/jsdtoa.cpp
+++ b/js/src/jsdtoa.cpp
@@ -49,6 +49,7 @@ static inline void dtoa_free(void* p) { return js_free(p); }
 
 #define NO_GLOBAL_STATE
 #define NO_ERRNO
+#define Omit_Private_Memory // This saves memory for the workloads we see.
 #define MALLOC dtoa_malloc
 #define FREE dtoa_free
 #include "dtoa.c"
diff --git a/js/src/json.cpp b/js/src/json.cpp
index d8b98926eb2..e6f493dd676 100644
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -132,14 +132,19 @@ class StringifyContext
 {
   public:
     StringifyContext(JSContext* cx, StringBuffer& sb, const StringBuffer& gap,
-                     HandleObject replacer, const AutoIdVector& propertyList)
+                     HandleObject replacer, const AutoIdVector& propertyList,
+                     bool maybeSafely)
       : sb(sb),
         gap(gap),
         replacer(cx, replacer),
         stack(cx, GCHashSet>(cx)),
         propertyList(propertyList),
-        depth(0)
-    {}
+        depth(0),
+        maybeSafely(maybeSafely)
+    {
+        MOZ_ASSERT_IF(maybeSafely, !replacer);
+        MOZ_ASSERT_IF(maybeSafely, gap.empty());
+    }
 
     bool init() {
         return stack.init(8);
@@ -151,6 +156,7 @@ class StringifyContext
     Rooted>> stack;
     const AutoIdVector& propertyList;
     uint32_t depth;
+    bool maybeSafely;
 };
 
 } /* anonymous namespace */
@@ -212,6 +218,11 @@ template
 static bool
 PreprocessValue(JSContext* cx, HandleObject holder, KeyType key, MutableHandleValue vp, StringifyContext* scx)
 {
+    // We don't want to do any preprocessing here if scx->maybeSafely,
+    // since the stuff we do here can have side-effects.
+    if (scx->maybeSafely)
+        return true;
+
     RootedString keyStr(cx);
 
     /* Step 2. */
@@ -342,6 +353,8 @@ JO(JSContext* cx, HandleObject obj, StringifyContext* scx)
      *     (and in JA as well).
      */
 
+    MOZ_ASSERT_IF(scx->maybeSafely, obj->is());
+
     /* Steps 1-2, 11. */
     CycleDetector detect(scx, obj);
     if (!detect.foundCycle(cx))
@@ -384,6 +397,14 @@ JO(JSContext* cx, HandleObject obj, StringifyContext* scx)
          */
         id = propertyList[i];
         RootedValue outputValue(cx);
+#ifdef DEBUG
+        if (scx->maybeSafely) {
+            RootedNativeObject nativeObj(cx, &obj->as());
+            RootedShape prop(cx);
+            NativeLookupOwnPropertyNoResolve(cx, nativeObj, id, &prop);
+            MOZ_ASSERT(prop && prop->isDataDescriptor());
+        }
+#endif // DEBUG
         if (!GetProperty(cx, obj, obj, id, &outputValue))
             return false;
         if (!PreprocessValue(cx, obj, HandleId(id), &outputValue, scx))
@@ -460,6 +481,27 @@ JA(JSContext* cx, HandleObject obj, StringifyContext* scx)
              * and the replacer and maybe unboxing, and interpreting some
              * values as |null| in separate steps.
              */
+#ifdef DEBUG
+            if (scx->maybeSafely) {
+                /*
+                 * Trying to do a JS_AlreadyHasOwnElement runs the risk of
+                 * hitting OOM on jsid creation.  Let's just assert sanity for
+                 * small enough indices.
+                 */
+                MOZ_ASSERT(obj->is());
+                MOZ_ASSERT(obj->is());
+                RootedNativeObject nativeObj(cx, &obj->as());
+                if (i <= JSID_INT_MAX) {
+                    MOZ_ASSERT(nativeObj->containsDenseElement(i) != nativeObj->isIndexed(),
+                               "the array must either be small enough to remain "
+                               "fully dense (and otherwise un-indexed), *or* "
+                               "all its initially-dense elements were sparsified "
+                               "and the object is indexed");
+                } else {
+                    MOZ_ASSERT(obj->isIndexed());
+                }
+            }
+#endif
             if (!GetElement(cx, obj, obj, i, &outputValue))
                 return false;
             if (!PreprocessValue(cx, obj, i, &outputValue, scx))
@@ -525,8 +567,12 @@ Str(JSContext* cx, const Value& v, StringifyContext* scx)
     /* Step 9. */
     if (v.isNumber()) {
         if (v.isDouble()) {
-            if (!IsFinite(v.toDouble()))
+            if (!IsFinite(v.toDouble())) {
+                MOZ_ASSERT(!scx->maybeSafely,
+                           "input JS::ToJSONMaybeSafely must not include "
+                           "reachable non-finite numbers");
                 return scx->sb.append("null");
+            }
         }
 
         return NumberValueToStringBuffer(cx, v, scx->sb);
@@ -536,6 +582,10 @@ Str(JSContext* cx, const Value& v, StringifyContext* scx)
     MOZ_ASSERT(v.isObject());
     RootedObject obj(cx, &v.toObject());
 
+    MOZ_ASSERT(!scx->maybeSafely || obj->is() || obj->is(),
+               "input to JS::ToJSONMaybeSafely must not include reachable "
+               "objects that are neither arrays nor plain objects");
+
     scx->depth++;
     auto dec = mozilla::MakeScopeExit([&] { scx->depth--; });
 
@@ -549,11 +599,21 @@ Str(JSContext* cx, const Value& v, StringifyContext* scx)
 /* ES6 24.3.2. */
 bool
 js::Stringify(JSContext* cx, MutableHandleValue vp, JSObject* replacer_, Value space_,
-              StringBuffer& sb)
+              StringBuffer& sb, StringifyBehavior stringifyBehavior)
 {
     RootedObject replacer(cx, replacer_);
     RootedValue space(cx, space_);
 
+    MOZ_ASSERT_IF(stringifyBehavior == StringifyBehavior::RestrictedSafe, space.isNull());
+    MOZ_ASSERT_IF(stringifyBehavior == StringifyBehavior::RestrictedSafe, vp.isObject());
+    /**
+     * This uses MOZ_ASSERT, since it's actually asserting something jsapi
+     * consumers could get wrong, so needs a better error message.
+     */
+    MOZ_ASSERT(stringifyBehavior == StringifyBehavior::Normal ||
+               vp.toObject().is() || vp.toObject().is(),
+               "input to JS::ToJSONMaybeSafely must be a plain object or array");
+
     /* Step 4. */
     AutoIdVector propertyList(cx);
     if (replacer) {
@@ -691,7 +751,8 @@ js::Stringify(JSContext* cx, MutableHandleValue vp, JSObject* replacer_, Value s
         return false;
 
     /* Step 12. */
-    StringifyContext scx(cx, sb, gap, replacer, propertyList);
+    StringifyContext scx(cx, sb, gap, replacer, propertyList,
+                         stringifyBehavior == StringifyBehavior::RestrictedSafe);
     if (!scx.init())
         return false;
     if (!PreprocessValue(cx, wrapper, HandleId(emptyId), vp, &scx))
@@ -890,7 +951,7 @@ json_stringify(JSContext* cx, unsigned argc, Value* vp)
     RootedValue space(cx, args.get(2));
 
     StringBuffer sb(cx);
-    if (!Stringify(cx, &value, replacer, space, sb))
+    if (!Stringify(cx, &value, replacer, space, sb, StringifyBehavior::Normal))
         return false;
 
     // XXX This can never happen to nsJSON.cpp, but the JSON object
diff --git a/js/src/json.h b/js/src/json.h
index a6ae4f7fe08..36f4643bc33 100644
--- a/js/src/json.h
+++ b/js/src/json.h
@@ -19,9 +19,19 @@ class StringBuffer;
 extern JSObject*
 InitJSONClass(JSContext* cx, HandleObject obj);
 
+enum class StringifyBehavior {
+    Normal,
+    RestrictedSafe
+};
+
+/**
+ * If maybeSafely is true, Stringify will attempt to assert the API requirements
+ * of JS::ToJSONMaybeSafely as it traverses the graph, and will not try to
+ * invoke .toJSON on things as it goes.
+ */
 extern bool
 Stringify(JSContext* cx, js::MutableHandleValue vp, JSObject* replacer,
-          Value space, StringBuffer& sb);
+          Value space, StringBuffer& sb, StringifyBehavior stringifyBehavior);
 
 template 
 extern bool
diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h
index 5410252a101..b7ded95473b 100644
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -125,9 +125,6 @@ typedef void
 (* JSTraceDataOp)(JSTracer* trc, void* data);
 
 namespace js {
-
-void FinishGC(JSRuntime* rt);
-
 namespace gc {
 class AutoTraceSession;
 class StoreBuffer;
@@ -292,6 +289,29 @@ class RootLists
         }
     }
 
+    ~RootLists() {
+        // The semantics of PersistentRooted containing pointers and tagged
+        // pointers are somewhat different from those of PersistentRooted
+        // containing a structure with a trace method. PersistentRooted
+        // containing pointers are allowed to outlive the owning RootLists,
+        // whereas those containing a traceable structure are not.
+        //
+        // The purpose of this feature is to support lazy initialization of
+        // global references for the several places in Gecko that do not have
+        // access to a tighter context, but that still need to refer to GC
+        // pointers. For such pointers, FinishPersistentRootedChains ensures
+        // that the contained references are nulled out when the owning
+        // RootLists dies to prevent UAF errors.
+        //
+        // However, for RootKind::Traceable, we do not know the concrete type
+        // of the held thing, so we simply cannot do this without accruing
+        // extra overhead and complexity for all users for a case that is
+        // unlikely to ever be used in practice. For this reason, the following
+        // assertion disallows usage of PersistentRooted that
+        // outlives the RootLists.
+        MOZ_ASSERT(heapRoots_[JS::RootKind::Traceable].isEmpty());
+    }
+
     void traceStackRoots(JSTracer* trc);
     void checkNoGCRooters();
 
diff --git a/js/src/tests/ecma_6/Array/iterator_edge_cases.js b/js/src/tests/ecma_6/Array/iterator_edge_cases.js
index a52d1d40e19..079245ebfb7 100644
--- a/js/src/tests/ecma_6/Array/iterator_edge_cases.js
+++ b/js/src/tests/ecma_6/Array/iterator_edge_cases.js
@@ -7,7 +7,7 @@ function TestArrayIteratorPrototypeConfusion() {
         throw new Error("Call did not throw");
     } catch (e) {
         assertEq(e instanceof TypeError, true);
-        assertEq(e.message, "CallArrayIteratorMethodIfWrapped method called on incompatible Array Iterator");
+        assertEq(e.message, "next method called on incompatible Array Iterator");
     }
 }
 TestArrayIteratorPrototypeConfusion();
diff --git a/js/src/tests/ecma_6/Date/non-iso.js b/js/src/tests/ecma_6/Date/non-iso.js
new file mode 100644
index 00000000000..9c352ee740b
--- /dev/null
+++ b/js/src/tests/ecma_6/Date/non-iso.js
@@ -0,0 +1,63 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommonn.org/licenses/publicdomain/
+ */
+
+/*
+ * For the sake of cross compatibility with other implementations we
+ * follow the W3C "NOTE-datetime" specification when parsing dates of
+ * the form YYYY-MM-DDTHH:MM:SS save for a few exceptions: months, days, hours
+ * minutes, and seconds may be either one _or_ two digits long, and the 'T'
+ * preceding the time part may be replaced with a space. So, a string like
+ * "1997-3-8 1:1:1" will parse successfully. See bug: 1203298
+ */
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+assertEq(new Date("1997-03-08 1:1:1.01").getTime(),
+         new Date("1997-03-08T01:01:01.01").getTime());
+assertEq(new Date("1997-03-08 11:19:20").getTime(),
+         new Date("1997-03-08T11:19:20").getTime());
+assertEq(new Date("1997-3-08 11:19:20").getTime(),
+         new Date("1997-03-08T11:19:20").getTime());
+assertEq(new Date("1997-3-8 11:19:20").getTime(),
+         new Date("1997-03-08T11:19:20").getTime());
+assertEq(new Date("1997-3-8T11:19:20").getTime(),
+         new Date("1997-03-08T11:19:20").getTime());
+assertEq(new Date("1997-03-8T11:19:20").getTime(),
+         new Date("1997-03-08T11:19:20").getTime());
+assertEq(new Date("1997-03-08 11:19").getTime(),
+         new Date("1997-03-08T11:19").getTime());
+assertEq(new Date("1997-03-08 1:19").getTime(),
+         new Date("1997-03-08T1:19").getTime());
+assertEq(new Date("1997-03-08 1:1").getTime(),
+         new Date("1997-03-08T1:1").getTime());
+assertEq(new Date("1997-03-08 1:1:01").getTime(),
+         new Date("1997-03-08T1:1:01").getTime());
+assertEq(new Date("1997-03-08 1:1:1").getTime(),
+         new Date("1997-03-08T1:1:1").getTime());
+assertEq(new Date("1997-03-08 11").getTime(),
+         new Date("1997-03-08T11").getTime());
+assertEq(new Date("1997-03-08").getTime(),
+         new Date("1997-03-08").getTime());
+assertEq(new Date("1997-03-8").getTime(),
+         new Date("1997-03-08").getTime());
+assertEq(new Date("1997-3-8").getTime(),
+         new Date("1997-03-08").getTime());
+assertEq(new Date("1997-3-8 ").getTime(),
+         new Date("1997-03-08T").getTime()); // Date(NaN)
+assertEq(new Date("1997-3-8 :00:01").getTime(),
+         new Date(NaN).getTime());
+assertEq(new Date("1997-3-8 :00:01").getTime(),
+         new Date(NaN).getTime());
+assertEq(new Date("1997-3-8 01::01").getTime(),
+         new Date(NaN).getTime());
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
diff --git a/js/src/tests/ecma_6/String/iterator_edge_cases.js b/js/src/tests/ecma_6/String/iterator_edge_cases.js
index 2cba856909e..15fe6d0fb11 100644
--- a/js/src/tests/ecma_6/String/iterator_edge_cases.js
+++ b/js/src/tests/ecma_6/String/iterator_edge_cases.js
@@ -7,7 +7,7 @@ function TestStringIteratorPrototypeConfusion() {
         throw new Error("Call did not throw");
     } catch (e) {
         assertEq(e instanceof TypeError, true);
-        assertEq(e.message, "CallStringIteratorMethodIfWrapped method called on incompatible String Iterator");
+        assertEq(e.message, "next method called on incompatible String Iterator");
     }
 }
 TestStringIteratorPrototypeConfusion();
diff --git a/js/src/vm/CallNonGenericMethod.cpp b/js/src/vm/CallNonGenericMethod.cpp
index 157f348e218..131c447421a 100644
--- a/js/src/vm/CallNonGenericMethod.cpp
+++ b/js/src/vm/CallNonGenericMethod.cpp
@@ -11,6 +11,7 @@
 
 #include "proxy/Proxy.h"
 #include "vm/ProxyObject.h"
+#include "vm/SelfHosting.h"
 
 using namespace js;
 
@@ -27,6 +28,9 @@ JS::detail::CallMethodIfWrapped(JSContext* cx, IsAcceptableThis test, NativeImpl
             return Proxy::nativeCall(cx, test, impl, args);
     }
 
+    if (IsCallSelfHostedNonGenericMethod(impl))
+        return ReportIncompatibleSelfHostedMethod(cx, args);
+
     ReportIncompatible(cx, args);
     return false;
 }
diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
index c3a3614aa3f..3392fcc7bb7 100644
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -530,8 +530,6 @@ JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Runtim
     for (ContextIter acx(this); !acx.done(); acx.next())
         rtSizes->contexts += acx->sizeOfIncludingThis(mallocSizeOf);
 
-    rtSizes->dtoa += mallocSizeOf(mainThread.dtoaState);
-
     rtSizes->temporary += tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
 
     rtSizes->interpreterStack += interpreterStack_.sizeOfExcludingThis(mallocSizeOf);
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
index a9c6d19b4eb..3a18c69e484 100644
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1382,6 +1382,34 @@ CallNonGenericSelfhostedMethod(JSContext* cx, unsigned argc, Value* vp)
     return CallNonGenericMethod(cx, args);
 }
 
+bool
+js::IsCallSelfHostedNonGenericMethod(NativeImpl impl)
+{
+    return impl == CallSelfHostedNonGenericMethod;
+}
+
+bool
+js::ReportIncompatibleSelfHostedMethod(JSContext* cx, const CallArgs& args)
+{
+    // The contract for this function is the same as CallSelfHostedNonGenericMethod.
+    // The normal ReportIncompatible function doesn't work for selfhosted functions,
+    // because they always call the different CallXXXMethodIfWrapped methods,
+    // which would be reported as the called function instead.
+
+    // Lookup the selfhosted method that was invoked.
+    ScriptFrameIter iter(cx);
+    MOZ_ASSERT(iter.isFunctionFrame());
+
+    JSAutoByteString funNameBytes;
+    if (const char* funName = GetFunctionNameBytes(cx, iter.callee(cx), &funNameBytes)) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_METHOD,
+                             funName, "method", InformalValueTypeName(args.thisv()));
+    }
+
+    return false;
+}
+
+
 /**
  * Returns the default locale as a well-formed, but not necessarily canonicalized,
  * BCP-47 language tag.
diff --git a/js/src/vm/SelfHosting.h b/js/src/vm/SelfHosting.h
index dcd18708a57..7e2055469d9 100644
--- a/js/src/vm/SelfHosting.h
+++ b/js/src/vm/SelfHosting.h
@@ -8,6 +8,7 @@
 #define vm_SelfHosting_h_
 
 #include "jsapi.h"
+#include "NamespaceImports.h"
 
 class JSAtom;
 
@@ -17,7 +18,14 @@ namespace js {
  * Check whether the given JSFunction is a self-hosted function whose
  * self-hosted name is the given name.
  */
-bool IsSelfHostedFunctionWithName(JSFunction* fun, JSAtom* name);
+bool
+IsSelfHostedFunctionWithName(JSFunction* fun, JSAtom* name);
+
+bool
+IsCallSelfHostedNonGenericMethod(NativeImpl impl);
+
+bool
+ReportIncompatibleSelfHostedMethod(JSContext* cx, const CallArgs& args);
 
 /* Get the compile options used when compiling self hosted code. */
 void
diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp
index cf9fec36030..6180087add2 100644
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -4308,6 +4308,12 @@ JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM* oom)
     for (unsigned i = 0; i < num; i++)
         typeArray[i].sweep(zone(), *oom);
 
+    if (oom->hadOOM()) {
+        // It's possible we OOM'd while copying freeze constraints, so they
+        // need to be regenerated.
+        hasFreezeConstraints_ = false;
+    }
+
     // Update the recompile indexes in any IonScripts still on the script.
     if (hasIonScript())
         ionScript()->recompileInfoRef().shouldSweep(types);
diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h
index aa39e754101..f4032d5b5df 100644
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -598,6 +598,9 @@ class AutoClearTypeInferenceStateOnOOM
     void setOOM() {
         oom = true;
     }
+    bool hadOOM() const {
+        return oom;
+    }
 };
 
 /* Superclass common to stack and heap type sets. */
diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp
index 84859e5e7ad..985ed3af2a7 100644
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2485,11 +2485,6 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats,
         KIND_HEAP, rtStats.runtime.contexts,
         "JSContext objects and structures that belong to them.");
 
-    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/dtoa"),
-        KIND_HEAP, rtStats.runtime.dtoa,
-        "The DtoaState object, which is used for converting strings to "
-        "numbers and vice versa.");
-
     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/temporary"),
         KIND_HEAP, rtStats.runtime.temporary,
         "Transient data (mostly parse nodes) held by the JSRuntime during "
diff --git a/layout/base/FramePropertyTable.cpp b/layout/base/FramePropertyTable.cpp
index 12a1dcc3ded..0fd9b1c377a 100644
--- a/layout/base/FramePropertyTable.cpp
+++ b/layout/base/FramePropertyTable.cpp
@@ -123,7 +123,10 @@ FramePropertyTable::RemoveInternal(
   if (entry->mProp.mProperty == aProperty) {
     // There's only one entry and it's the one we want
     void* value = entry->mProp.mValue;
-    mEntries.RawRemoveEntry(entry);
+
+    // Here it's ok to use RemoveEntry() -- which may resize mEntries --
+    // because we null mLastEntry at the same time.
+    mEntries.RemoveEntry(entry);
     mLastEntry = nullptr;
     if (aFoundResult) {
       *aFoundResult = true;
@@ -209,6 +212,9 @@ FramePropertyTable::DeleteAllFor(const nsIFrame* aFrame)
   }
 
   DeleteAllForEntry(entry);
+
+  // mLastEntry points into mEntries, so we use RawRemoveEntry() which will not
+  // resize mEntries.
   mEntries.RawRemoveEntry(entry);
 }
 
diff --git a/layout/base/FramePropertyTable.h b/layout/base/FramePropertyTable.h
index 9b4f04fa822..97c23272e5e 100644
--- a/layout/base/FramePropertyTable.h
+++ b/layout/base/FramePropertyTable.h
@@ -336,6 +336,9 @@ protected:
 
   static void DeleteAllForEntry(Entry* aEntry);
 
+  // Note that mLastEntry points into mEntries, so we need to be careful about
+  // not triggering a resize of mEntries, e.g. use RawRemoveEntry() instead of
+  // RemoveEntry() in some places.
   nsTHashtable mEntries;
   const nsIFrame* mLastFrame;
   Entry* mLastEntry;
diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp
index 9e0847800d2..17778401ead 100644
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -5641,7 +5641,7 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState
   // When constructing a child of a non-open 
, create only the frame // for the main element, and skip other elements. auto* details = HTMLDetailsElement::FromContentOrNull(parent); - if (details && !details->Open()) { + if (details && details->IsDetailsEnabled() && !details->Open()) { auto* summary = HTMLSummaryElement::FromContentOrNull(aContent); if (!summary || !summary->IsMainSummary()) { SetAsUndisplayedContent(aState, aItems, aContent, styleContext, diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index d083dbbe66d..50e41a036a4 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -349,7 +349,7 @@ static void AddTransformFunctions(nsCSSValueList* aList, } static TimingFunction -ToTimingFunction(Maybe aCTF) +ToTimingFunction(const Maybe& aCTF) { if (aCTF.isNothing()) { return TimingFunction(null_t()); diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 6aa3e7ddba9..b9ec1e52fd6 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -8581,6 +8581,14 @@ nsLayoutUtils::SetScrollPositionClampingScrollPortSize(nsIPresShell* aPresShell, MaybeReflowForInflationScreenSizeChange(presContext); } +/* static */ bool +nsLayoutUtils::CanScrollOriginClobberApz(nsIAtom* aScrollOrigin) +{ + return aScrollOrigin != nullptr + && aScrollOrigin != nsGkAtoms::apz + && aScrollOrigin != nsGkAtoms::restore; +} + /* static */ FrameMetrics nsLayoutUtils::ComputeFrameMetrics(nsIFrame* aForFrame, nsIFrame* aScrollFrame, @@ -8637,11 +8645,10 @@ nsLayoutUtils::ComputeFrameMetrics(nsIFrame* aForFrame, nsPoint smoothScrollPosition = scrollableFrame->LastScrollDestination(); metrics.SetSmoothScrollOffset(CSSPoint::FromAppUnits(smoothScrollPosition)); - // If the frame was scrolled since the last layers update, and by - // something other than the APZ code, we want to tell the APZ to update + // If the frame was scrolled since the last layers update, and by something + // that is higher priority than APZ, we want to tell the APZ to update // its scroll offset. - nsIAtom* lastScrollOrigin = scrollableFrame->LastScrollOrigin(); - if (lastScrollOrigin && lastScrollOrigin != nsGkAtoms::apz) { + if (CanScrollOriginClobberApz(scrollableFrame->LastScrollOrigin())) { metrics.SetScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration()); } nsIAtom* lastSmoothScrollOrigin = scrollableFrame->LastSmoothScrollOrigin(); diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 822da7e4404..58ddf54d2f6 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -2722,6 +2722,15 @@ public: static void SetScrollPositionClampingScrollPortSize(nsIPresShell* aPresShell, CSSSize aSize); + /** + * Returns true if the given scroll origin is "higher priority" than APZ. + * In general any content programmatic scrolls (e.g. scrollTo calls) are + * higher priority, and take precedence over APZ scrolling. This function + * returns true for those, and returns false for other origins like APZ + * itself, or scroll position updates from the history restore code. + */ + static bool CanScrollOriginClobberApz(nsIAtom* aScrollOrigin); + static FrameMetrics ComputeFrameMetrics(nsIFrame* aForFrame, nsIFrame* aScrollFrame, nsIContent* aContent, diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index 2517d3c1f0e..16045404690 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -1479,35 +1479,10 @@ nsRefreshDriver::DispatchPendingEvents() } static bool -DispatchAnimationEventsOnSubDocuments(nsIDocument* aDocument, - void* aRefreshDriver) +CollectDocuments(nsIDocument* aDocument, void* aDocArray) { - nsIPresShell* shell = aDocument->GetShell(); - if (!shell) { - return true; - } - - RefPtr context = shell->GetPresContext(); - if (!context || context->RefreshDriver() != aRefreshDriver) { - return true; - } - - nsCOMPtr kungFuDeathGrip(aDocument); - - context->TransitionManager()->SortEvents(); - context->AnimationManager()->SortEvents(); - - // Dispatch transition events first since transitions conceptually sit - // below animations in terms of compositing order. - context->TransitionManager()->DispatchEvents(); - // Check that the presshell has not been destroyed - if (context->GetPresShell()) { - context->AnimationManager()->DispatchEvents(); - } - - aDocument->EnumerateSubDocuments(DispatchAnimationEventsOnSubDocuments, - aRefreshDriver); - + static_cast*>(aDocArray)->AppendObject(aDocument); + aDocument->EnumerateSubDocuments(CollectDocuments, aDocArray); return true; } @@ -1518,7 +1493,32 @@ nsRefreshDriver::DispatchAnimationEvents() return; } - DispatchAnimationEventsOnSubDocuments(mPresContext->Document(), this); + nsCOMArray documents; + CollectDocuments(mPresContext->Document(), &documents); + + for (int32_t i = 0; i < documents.Count(); ++i) { + nsIDocument* doc = documents[i]; + nsIPresShell* shell = doc->GetShell(); + if (!shell) { + continue; + } + + RefPtr context = shell->GetPresContext(); + if (!context || context->RefreshDriver() != this) { + continue; + } + + context->TransitionManager()->SortEvents(); + context->AnimationManager()->SortEvents(); + + // Dispatch transition events first since transitions conceptually sit + // below animations in terms of compositing order. + context->TransitionManager()->DispatchEvents(); + // Check that the presshell has not been destroyed + if (context->GetPresShell()) { + context->AnimationManager()->DispatchEvents(); + } + } } void diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 43c829c80e8..098c770e53c 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -322,7 +322,8 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(QuotaManagerService, QuotaManagerService::FactoryCreate) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ServiceWorkerManager, ServiceWorkerManager::GetInstance) -NS_GENERIC_FACTORY_CONSTRUCTOR(WorkerDebuggerManager) +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WorkerDebuggerManager, + WorkerDebuggerManager::GetInstance) #ifdef MOZ_WIDGET_GONK NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(SystemWorkerManager, diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index e0a88d01060..7853623a12a 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1725,9 +1725,15 @@ private: void ScrollFrameHelper::AsyncScroll::InitPreferences(TimeStamp aTime, nsIAtom *aOrigin) { - if (!aOrigin){ + if (!aOrigin || aOrigin == nsGkAtoms::restore) { + // We don't have special prefs for "restore", just treat it as "other". + // "restore" scrolls are (for now) always instant anyway so unless something + // changes we should never have aOrigin == nsGkAtoms::restore here. aOrigin = nsGkAtoms::other; } + // Likewise we should never get APZ-triggered scrolls here, and if that changes + // something is likely broken somewhere. + MOZ_ASSERT(aOrigin != nsGkAtoms::apz); // Read preferences only on first iteration or for a different event origin. if (!mIsFirstIteration && aOrigin == mOrigin) { @@ -2347,7 +2353,7 @@ RemoveDisplayPortCallback(nsITimer* aTimer, void* aClosure) MOZ_ASSERT(helper->mDisplayPortExpiryTimer); helper->mDisplayPortExpiryTimer = nullptr; - if (helper->IsAlwaysActive() || helper->mIsScrollParent) { + if (!helper->AllowDisplayPortExpiration() || helper->mIsScrollParent) { // If this is a scroll parent for some other scrollable frame, don't // expire the displayport because it would break scroll handoff. Once the // descendant scrollframes have their displayports expired, they will @@ -2411,9 +2417,20 @@ void ScrollFrameHelper::ResetDisplayPortExpiryTimer() } } -void ScrollFrameHelper::TriggerDisplayPortExpiration() +bool ScrollFrameHelper::AllowDisplayPortExpiration() { if (IsAlwaysActive()) { + return false; + } + if (mIsRoot && mOuter->PresContext()->IsRoot()) { + return false; + } + return true; +} + +void ScrollFrameHelper::TriggerDisplayPortExpiration() +{ + if (!AllowDisplayPortExpiration()) { return; } @@ -3938,7 +3955,8 @@ ScrollFrameHelper::ScrollToRestoredPosition() scrollToPos.x = mScrollPort.x - (mScrollPort.XMost() - scrollToPos.x - mScrolledFrame->GetRect().width); nsWeakFrame weakFrame(mOuter); - ScrollTo(scrollToPos, nsIScrollableFrame::INSTANT); + ScrollToWithOrigin(scrollToPos, nsIScrollableFrame::INSTANT, + nsGkAtoms::restore, nullptr); if (!weakFrame.IsAlive()) { return; } diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index 98b8fb3f740..6426233e16a 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -370,6 +370,7 @@ public: void NotifyImageVisibilityUpdate(); bool GetDisplayPortAtLastImageVisibilityUpdate(nsRect* aDisplayPort); + bool AllowDisplayPortExpiration(); void TriggerDisplayPortExpiration(); void ResetDisplayPortExpiryTimer(); diff --git a/layout/reftests/details-summary/disabled-no-summary-ref.html b/layout/reftests/details-summary/disabled-no-summary-ref.html new file mode 100644 index 00000000000..6ecdbcdc0ae --- /dev/null +++ b/layout/reftests/details-summary/disabled-no-summary-ref.html @@ -0,0 +1,11 @@ + + + + + +
+

This is the details.

+
+ + diff --git a/layout/reftests/details-summary/disabled-single-summary-ref.html b/layout/reftests/details-summary/disabled-single-summary-ref.html new file mode 100644 index 00000000000..f643af6dcd1 --- /dev/null +++ b/layout/reftests/details-summary/disabled-single-summary-ref.html @@ -0,0 +1,12 @@ + + + + + +
+
Summary
+

This is the details.

+
+ + diff --git a/layout/reftests/details-summary/reftest.list b/layout/reftests/details-summary/reftest.list index eb985035404..54cf691025e 100644 --- a/layout/reftests/details-summary/reftest.list +++ b/layout/reftests/details-summary/reftest.list @@ -1,3 +1,8 @@ +# Disable
and +pref(dom.details_element.enabled,false) == single-summary.html disabled-single-summary-ref.html +pref(dom.details_element.enabled,false) == open-single-summary.html disabled-single-summary-ref.html +pref(dom.details_element.enabled,false) == no-summary.html disabled-no-summary-ref.html + # Basic handling pref(dom.details_element.enabled,true) == multiple-summary.html single-summary.html pref(dom.details_element.enabled,true) == open-multiple-summary.html open-multiple-summary-ref.html diff --git a/layout/style/nsAnimationManager.h b/layout/style/nsAnimationManager.h index 7a70683725d..18769f26ce8 100644 --- a/layout/style/nsAnimationManager.h +++ b/layout/style/nsAnimationManager.h @@ -313,7 +313,11 @@ public: * accumulate animationstart events at other points when style * contexts are created. */ - void DispatchEvents() { mEventDispatcher.DispatchEvents(mPresContext); } + void DispatchEvents() + { + RefPtr kungFuDeathGrip(this); + mEventDispatcher.DispatchEvents(mPresContext); + } void SortEvents() { mEventDispatcher.SortEvents(); } void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); } diff --git a/layout/style/nsTransitionManager.h b/layout/style/nsTransitionManager.h index 65a68771c26..35c627af82b 100644 --- a/layout/style/nsTransitionManager.h +++ b/layout/style/nsTransitionManager.h @@ -306,7 +306,11 @@ public: mozilla::Forward(aEventInfo)); } - void DispatchEvents() { mEventDispatcher.DispatchEvents(mPresContext); } + void DispatchEvents() + { + RefPtr kungFuDeathGrip(this); + mEventDispatcher.DispatchEvents(mPresContext); + } void SortEvents() { mEventDispatcher.SortEvents(); } void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); } diff --git a/layout/tools/reftest/Makefile.in b/layout/tools/reftest/Makefile.in index 9b52b80e107..3171d7f64fe 100644 --- a/layout/tools/reftest/Makefile.in +++ b/layout/tools/reftest/Makefile.in @@ -14,13 +14,6 @@ _HARNESS_FILES = \ $(srcdir)/gaia_lock_screen.js \ $(srcdir)/output.py \ automation.py \ - $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/devicemanager.py \ - $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py \ - $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py \ - $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/droid.py \ - $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/version_codes.py \ - $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/Zeroconf.py \ - $(topsrcdir)/testing/mozbase/moznetwork/moznetwork/moznetwork.py \ $(topsrcdir)/build/mobile/b2gautomation.py \ $(topsrcdir)/build/mobile/remoteautomation.py \ $(topsrcdir)/testing/mochitest/server.js \ diff --git a/layout/tools/reftest/mach_commands.py b/layout/tools/reftest/mach_commands.py index b3b2bc5ebd4..8ea0222910a 100644 --- a/layout/tools/reftest/mach_commands.py +++ b/layout/tools/reftest/mach_commands.py @@ -281,8 +281,6 @@ class ReftestRunner(MozbuildObject): imp.load_module('reftest', fh, path, ('.py', 'r', imp.PY_SOURCE)) import reftest - # Remove the stdout handler from the internal logger and let mach deal with it - runreftest.log.removeHandler(runreftest.log.handlers[0]) self.log_manager.enable_unstructured() try: rv = reftest.run(**kwargs) diff --git a/layout/tools/reftest/remotereftest.py b/layout/tools/reftest/remotereftest.py index 9f2feff87b1..9616259b031 100644 --- a/layout/tools/reftest/remotereftest.py +++ b/layout/tools/reftest/remotereftest.py @@ -7,11 +7,10 @@ import os import time import tempfile import traceback +import urllib2 -import devicemanager -import droid +import mozdevice import mozinfo -import moznetwork from automation import Automation from remoteautomation import RemoteAutomation, fennecLogcatFilters @@ -282,7 +281,7 @@ class RemoteReftest(RefTest): try: self._devicemanager.pushDir(profileDir, options.remoteProfile) self._devicemanager.chmodDir(options.remoteProfile) - except devicemanager.DMError: + except mozdevice.DMError: print "Automation Error: Failed to copy profiledir to device" raise @@ -294,7 +293,7 @@ class RemoteReftest(RefTest): try: self._devicemanager.pushDir(profileDir, options.remoteProfile) self._devicemanager.chmodDir(options.remoteProfile) - except devicemanager.DMError: + except mozdevice.DMError: print "Automation Error: Failed to copy extra files to device" raise @@ -313,7 +312,7 @@ class RemoteReftest(RefTest): else: print " %s: %s" % (category, devinfo[category]) print "Test root: %s" % self._devicemanager.deviceRoot - except devicemanager.DMError: + except mozdevice.DMError: print "WARNING: Error getting device information" def environment(self, **kwargs): @@ -367,14 +366,14 @@ def runTests(options, parser): try: if (options.dm_trans == "adb"): if (options.deviceIP): - dm = droid.DroidADB(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot) + dm = mozdevice.DroidADB(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot) elif (options.deviceSerial): - dm = droid.DroidADB(None, None, deviceSerial=options.deviceSerial, deviceRoot=options.remoteTestRoot) + dm = mozdevice.DroidADB(None, None, deviceSerial=options.deviceSerial, deviceRoot=options.remoteTestRoot) else: - dm = droid.DroidADB(None, None, deviceRoot=options.remoteTestRoot) + dm = mozdevice.DroidADB(None, None, deviceRoot=options.remoteTestRoot) else: - dm = droid.DroidSUT(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot) - except devicemanager.DMError: + dm = mozdevice.DroidSUT(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot) + except mozdevice.DMError: print "Automation Error: exception while initializing devicemanager. Most likely the device is not in a testable state." return 1 diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_component.c b/media/mtransport/third_party/nICEr/src/ice/ice_component.c index 115d61172cc..d99ef4cf3e7 100644 --- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c +++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c @@ -252,8 +252,15 @@ static int nr_ice_component_initialize_udp(struct nr_ice_ctx_ *ctx,nr_ice_compon cand=0; } } + else{ + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): relay only option results in no host candidate for %s",ctx->label,addrs[i].addr.as_string); + } #ifdef USE_TURN + if ((ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) && + (ctx->turn_server_ct == 0)) { + r_log(LOG_ICE,LOG_ERR,"ICE(%s): relay only option is set without any TURN server configured",ctx->label); + } /* And both a srvrflx and relayed candidate for each TURN server (unless we're in relay-only mode, in which case just the relayed one) */ for(j=0;jturn_server_ct;j++){ @@ -418,6 +425,7 @@ static int nr_ice_component_initialize_tcp(struct nr_ice_ctx_ *ctx,nr_ice_compon ABORT(r); } if (ctx->flags & NR_ICE_CTX_FLAGS_RELAY_ONLY) { + r_log(LOG_ICE,LOG_WARNING,"ICE(%s): relay only option results in ICE TCP being disabled",ctx->label); ice_tcp_disabled = 1; } diff --git a/media/webrtc/signaling/src/common/browser_logging/WebRtcLog.cpp b/media/webrtc/signaling/src/common/browser_logging/WebRtcLog.cpp index a387864e17d..469d0c240a3 100644 --- a/media/webrtc/signaling/src/common/browser_logging/WebRtcLog.cpp +++ b/media/webrtc/signaling/src/common/browser_logging/WebRtcLog.cpp @@ -210,3 +210,8 @@ void EnableWebRtcLog() ConfigWebRtcLog(trace_mask, log_file, aec_log_dir, multi_log); return; } + +void StopWebRtcLog() +{ + webrtc::Trace::SetTraceFile(nullptr); +} diff --git a/media/webrtc/signaling/src/common/browser_logging/WebRtcLog.h b/media/webrtc/signaling/src/common/browser_logging/WebRtcLog.h index b6b7fc0a7f2..dc304081716 100644 --- a/media/webrtc/signaling/src/common/browser_logging/WebRtcLog.h +++ b/media/webrtc/signaling/src/common/browser_logging/WebRtcLog.h @@ -9,5 +9,6 @@ void StartWebRtcLog(uint32_t log_level = webrtc::kTraceDefault); void EnableWebRtcLog(); +void StopWebRtcLog(); #endif diff --git a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp index ab8805d9fed..bcf8dc9e759 100755 --- a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp +++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp @@ -27,7 +27,6 @@ #include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/voice_engine/include/voe_errors.h" #include "webrtc/system_wrappers/interface/clock.h" -#include "browser_logging/WebRtcLog.h" #ifdef MOZ_WIDGET_ANDROID #include "AndroidJNIWrapper.h" @@ -257,8 +256,6 @@ MediaConduitErrorCode WebrtcAudioConduit::Init() return kMediaConduitSessionNotInited; } - EnableWebRtcLog(); - if(!(mPtrVoEBase = VoEBase::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoEBase", __FUNCTION__); diff --git a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp index 383562f9af4..54426111dc6 100755 --- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp +++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp @@ -21,7 +21,6 @@ #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" #include "webrtc/video_engine/include/vie_errors.h" #include "webrtc/video_engine/vie_defines.h" -#include "browser_logging/WebRtcLog.h" #ifdef MOZ_WIDGET_ANDROID #include "AndroidJNIWrapper.h" @@ -308,7 +307,6 @@ WebrtcVideoConduit::InitMain() } } - EnableWebRtcLog(); #ifdef MOZ_WIDGET_ANDROID // get the JVM JavaVM *jvm = jsjni_GetVM(); diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp index 3a24d821377..814e0be5d10 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp @@ -11,6 +11,7 @@ #include "prcvar.h" #include "mozilla/Telemetry.h" +#include "browser_logging/WebRtcLog.h" #if !defined(MOZILLA_EXTERNAL_LINKAGE) #include "mozilla/dom/RTCPeerConnectionBinding.h" @@ -135,6 +136,7 @@ nsresult PeerConnectionCtx::InitializeGlobal(nsIThread *mainThread, } } + EnableWebRtcLog(); return NS_OK; } @@ -155,6 +157,8 @@ void PeerConnectionCtx::Destroy() { delete gInstance; gInstance = nullptr; } + + StopWebRtcLog(); } #if !defined(MOZILLA_EXTERNAL_LINKAGE) diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index 94af0c340d3..212512b0623 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -238,6 +238,12 @@ pref("extensions.blocklist.interval", 86400); 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.com/%LOCALE%/blocklist/"); +// 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); + /* Don't let XPIProvider install distribution add-ons; we do our own thing on mobile. */ pref("extensions.installDistroAddons", false); @@ -440,7 +446,7 @@ pref("ui.touch.radius.rightmm", 3); pref("ui.touch.radius.bottommm", 2); pref("ui.touch.radius.visitedWeight", 120); -pref("ui.mouse.radius.enabled", true); +pref("ui.mouse.radius.enabled", false); pref("ui.mouse.radius.leftmm", 3); pref("ui.mouse.radius.topmm", 5); pref("ui.mouse.radius.rightmm", 3); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index d25586b0bea..1082106d72d 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4438,7 +4438,7 @@ pref("gfx.apitrace.enabled",false); #ifdef MOZ_X11 pref("gfx.content.use-native-pushlayer", true); #ifdef MOZ_WIDGET_GTK -pref("gfx.xrender.enabled",true); +pref("gfx.xrender.enabled",false); #endif #endif diff --git a/netwerk/base/nsURLHelper.cpp b/netwerk/base/nsURLHelper.cpp index 8a30c3719fb..27833e19e1d 100644 --- a/netwerk/base/nsURLHelper.cpp +++ b/netwerk/base/nsURLHelper.cpp @@ -248,7 +248,7 @@ net_CoalesceDirs(netCoalesceFlags flags, char* path) */ char *fwdPtr = path; char *urlPtr = path; - char *lastslash = path; + char *endPath = path; uint32_t traversal = 0; uint32_t special_ftp_len = 0; @@ -265,34 +265,18 @@ net_CoalesceDirs(netCoalesceFlags flags, char* path) special_ftp_len = 2; } - /* find the last slash before # or ? */ - for(; (*fwdPtr != '\0') && - (*fwdPtr != '?') && + /* find the end of the path - places the cursor on \0, ? or # */ + for(; (*fwdPtr != '\0') && + (*fwdPtr != '?') && (*fwdPtr != '#'); ++fwdPtr) { } - /* found nothing, but go back one only */ - /* if there is something to go back to */ - if (fwdPtr != path && *fwdPtr == '\0') - { - --fwdPtr; - } - - /* search the slash */ - for(; (fwdPtr != path) && - (*fwdPtr != '/'); --fwdPtr) - { - } - lastslash = fwdPtr; + endPath = fwdPtr; fwdPtr = path; /* replace all %2E or %2e with . in the path */ - /* but stop at lastchar if non null */ - for(; (*fwdPtr != '\0') && - (*fwdPtr != '?') && - (*fwdPtr != '#') && - (*lastslash == '\0' || fwdPtr != lastslash); ++fwdPtr) + for(; fwdPtr != endPath; ++fwdPtr) { if (*fwdPtr == '%' && *(fwdPtr+1) == '2' && (*(fwdPtr+2) == 'E' || *(fwdPtr+2) == 'e')) diff --git a/netwerk/test/unit/test_bug464591.js b/netwerk/test/unit/test_bug464591.js new file mode 100644 index 00000000000..34d7c46b527 --- /dev/null +++ b/netwerk/test/unit/test_bug464591.js @@ -0,0 +1,81 @@ + +const StandardURL = Components.Constructor("@mozilla.org/network/standard-url;1", + "nsIStandardURL", + "init"); + + // 1.percent-encoded IDN that contains blacklisted character should be converted + // to punycode, not UTF-8 string + // 2.only hostname-valid percent encoded ASCII characters should be decoded + // 3.IDN convertion must not bypassed by %00 +let reference = [ + ["www.example.com%e2%88%95www.mozill%d0%b0.com%e2%81%84www.mozilla.org", + "www.example.xn--comwww-re3c.xn--mozill-8nf.xn--comwww-rq0c.mozilla.org"], + ["www.mozill%61%2f.org", "www.mozilla%2f.org"], // a slash is not valid in the hostname + ["www.e%00xample.com%e2%88%95www.mozill%d0%b0.com%e2%81%84www.mozill%61.org", + "www.e%00xample.xn--comwww-re3c.xn--mozill-8nf.xn--comwww-rq0c.mozilla.org"], +]; + +let prefData = + [ + { + name: "network.enableIDN", + newVal: true + }, + { + name: "network.IDN_show_punycode", + newVal: false + }, + { + name: "network.IDN.whitelist.org", + newVal: true + } + ]; + + let prefIdnBlackList = { + name: "network.IDN.blacklist_chars", + minimumList: "\u2215\u0430\u2044", + }; + +function stringToURL(str) { + return (new StandardURL(Ci.nsIStandardURL.URLTYPE_AUTHORITY, 80, + str, "UTF-8", null)) + .QueryInterface(Ci.nsIURL); +} + +function run_test() { + // Make sure our prefs are set such that this test actually means something + let prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefBranch); + for (let pref of prefData) { + prefs.setBoolPref(pref.name, pref.newVal); + } + + prefIdnBlackList.set = false; + try { + prefIdnBlackList.oldVal = prefs.getComplexValue(prefIdnBlackList.name, + Ci.nsIPrefLocalizedString).data; + prefs.getComplexValue(prefIdnBlackList.name, + Ci.nsIPrefLocalizedString).data=prefIdnBlackList.minimumList; + prefIdnBlackList.set = true; + } catch (e) { + } + + do_register_cleanup(function() { + for (let pref of prefData) { + prefs.clearUserPref(pref.name); + } + if (prefIdnBlackList.set) { + prefs.getComplexValue(prefIdnBlackList.name, + Ci.nsIPrefLocalizedString).data = prefIdnBlackList.oldVal; + } + }); + + for (let i = 0; i < reference.length; ++i) { + try { + let result = stringToURL("http://" + reference[i][0]).host; + equal(result, reference[i][1]); + } catch (e) { + ok(false, "Error testing "+reference[i][0]); + } + } +} diff --git a/netwerk/test/unit/test_standardurl.js b/netwerk/test/unit/test_standardurl.js index 5db8ad432ce..ee1ee0251fe 100644 --- a/netwerk/test/unit/test_standardurl.js +++ b/netwerk/test/unit/test_standardurl.js @@ -301,6 +301,32 @@ add_test(function test_hugeStringThrows() run_next_test(); }); +add_test(function test_pathPercentEncodedDot() +{ + var url = stringToURL("http://example.com/%2eX/X%2e/%2eX"); + do_check_eq(url.spec, "http://example.com/.X/X./.X"); + + url = stringToURL("http://example.com/hello/%2e%2E/%2e"); + do_check_eq(url.spec, "http://example.com/"); + + url = stringToURL("http://example.com/hello/%2e%2E/%"); + do_check_eq(url.spec, "http://example.com/%"); + + url = stringToURL("http://example.com/hello/%2e%2E/%2"); + do_check_eq(url.spec, "http://example.com/%2"); + + url = stringToURL("http://example.com/hello/%2e%2E/%#"); + do_check_eq(url.spec, "http://example.com/%#"); + + url = stringToURL("http://example.com/hello/%2e%2E/%2?"); + do_check_eq(url.spec, "http://example.com/%2?"); + + url = stringToURL("http://example.com/hello/%2e/"); + do_check_eq(url.spec, "http://example.com/hello/"); + + run_next_test(); +}); + add_test(function test_filterWhitespace() { var url = stringToURL(" \r\n\th\nt\rt\tp://ex\r\n\tample.com/path\r\n\t/\r\n\tto the/fil\r\n\te.e\r\n\txt?que\r\n\try#ha\r\n\tsh \r\n\t "); diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 28e5cf4dd10..56c9179c988 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -346,4 +346,5 @@ firefox-appdir = browser [test_getHost.js] [test_packaged_app_bug1214079.js] [test_bug412457.js] +[test_bug464591.js] diff --git a/parser/html/javasrc/TreeBuilder.java b/parser/html/javasrc/TreeBuilder.java index c69c4ccf997..3f421334c80 100644 --- a/parser/html/javasrc/TreeBuilder.java +++ b/parser/html/javasrc/TreeBuilder.java @@ -4448,7 +4448,7 @@ public abstract class TreeBuilder implements TokenHandler, while (currentPtr > eltPos) { // > not >= intentional if (stack[currentPtr].ns == "http://www.w3.org/1999/xhtml" && stack[currentPtr].getGroup() == TEMPLATE - && (eltGroup == TABLE || eltGroup == TBODY_OR_THEAD_OR_TFOOT|| eltGroup == TR || eltGroup == HTML)) { + && (eltGroup == TABLE || eltGroup == TBODY_OR_THEAD_OR_TFOOT|| eltGroup == TR || eltPos == 0)) { return; } pop(); diff --git a/parser/html/nsHtml5TreeBuilder.cpp b/parser/html/nsHtml5TreeBuilder.cpp index d96d098320e..502575a604c 100644 --- a/parser/html/nsHtml5TreeBuilder.cpp +++ b/parser/html/nsHtml5TreeBuilder.cpp @@ -3306,7 +3306,7 @@ nsHtml5TreeBuilder::clearStackBackTo(int32_t eltPos) { int32_t eltGroup = stack[eltPos]->getGroup(); while (currentPtr > eltPos) { - if (stack[currentPtr]->ns == kNameSpaceID_XHTML && stack[currentPtr]->getGroup() == NS_HTML5TREE_BUILDER_TEMPLATE && (eltGroup == NS_HTML5TREE_BUILDER_TABLE || eltGroup == NS_HTML5TREE_BUILDER_TBODY_OR_THEAD_OR_TFOOT || eltGroup == NS_HTML5TREE_BUILDER_TR || eltGroup == NS_HTML5TREE_BUILDER_HTML)) { + if (stack[currentPtr]->ns == kNameSpaceID_XHTML && stack[currentPtr]->getGroup() == NS_HTML5TREE_BUILDER_TEMPLATE && (eltGroup == NS_HTML5TREE_BUILDER_TABLE || eltGroup == NS_HTML5TREE_BUILDER_TBODY_OR_THEAD_OR_TFOOT || eltGroup == NS_HTML5TREE_BUILDER_TR || !eltPos)) { return; } pop(); diff --git a/python/mach/mach/registrar.py b/python/mach/mach/registrar.py index 522f761dcee..579a1c2ffe5 100644 --- a/python/mach/mach/registrar.py +++ b/python/mach/mach/registrar.py @@ -91,6 +91,12 @@ class MachRegistrar(object): result = result or 0 assert isinstance(result, (int, long)) + + if context: + postrun = getattr(context, 'post_dispatch_handler', None) + if postrun: + postrun(context, handler, args=kwargs) + return result def dispatch(self, name, context=None, argv=None, **kwargs): diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index 7caae0dd37e..8916aa97267 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -493,6 +493,11 @@ class Build(MachCommandBase): print('To view resource usage of the build, run |mach ' 'resource-usage|.') + telemetry_handler = getattr(self._mach_context, + 'telemetry_handler', None) + usage = monitor.record_resource_usage() + telemetry_handler(self._mach_context, usage) + # Only for full builds because incremental builders likely don't # need to be burdened with this. if not what: diff --git a/services/common/KintoCertificateBlocklist.js b/services/common/KintoCertificateBlocklist.js new file mode 100644 index 00000000000..c7f74869f3c --- /dev/null +++ b/services/common/KintoCertificateBlocklist.js @@ -0,0 +1,115 @@ +/* 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 strict"; + +this.EXPORTED_SYMBOLS = ["OneCRLClient"]; + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://services-common/moz-kinto-client.js"); + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, "uuidgen", + "@mozilla.org/uuid-generator;1", + "nsIUUIDGenerator"); + +const PREF_KINTO_BASE = "services.kinto.base"; +const PREF_KINTO_BUCKET = "services.kinto.bucket"; +const PREF_KINTO_ONECRL_COLLECTION = "services.kinto.onecrl.collection"; +const PREF_KINTO_ONECRL_CHECKED_SECONDS = "services.kinto.onecrl.checked"; + +const RE_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + +// Kinto.js assumes version 4 UUIDs but allows you to specify custom +// validators and generators. The tooling that generates records in the +// certificates collection currently uses a version 1 UUID so we must +// specify a validator that's less strict. We must also supply a generator +// since Kinto.js does not allow one without the other. +function makeIDSchema() { + return { + validate: RE_UUID.test.bind(RE_UUID), + generate: function() { + return uuidgen.generateUUID().toString(); + } + }; +} + +// A Kinto based client to keep the OneCRL certificate blocklist up to date. +function CertBlocklistClient() { + // maybe sync the collection of certificates with remote data. + // lastModified - the lastModified date (on the server, milliseconds since + // epoch) of data in the remote collection + // serverTime - the time on the server (milliseconds since epoch) + // returns a promise which rejects on sync failure + this.maybeSync = function(lastModified, serverTime) { + let base = Services.prefs.getCharPref(PREF_KINTO_BASE); + let bucket = Services.prefs.getCharPref(PREF_KINTO_BUCKET); + + let Kinto = loadKinto(); + + let FirefoxAdapter = Kinto.adapters.FirefoxAdapter; + + + let certList = Cc["@mozilla.org/security/certblocklist;1"] + .getService(Ci.nsICertBlocklist); + + // Future blocklist clients can extract the sync-if-stale logic. For + // now, since this is currently the only client, we'll do this here. + let config = { + remote: base, + bucket: bucket, + adapter: FirefoxAdapter, + }; + + let db = new Kinto(config); + let collectionName = Services.prefs.getCharPref(PREF_KINTO_ONECRL_COLLECTION, + "certificates"); + let blocklist = db.collection(collectionName, + { idSchema: makeIDSchema() }); + + let updateLastCheck = function() { + let checkedServerTimeInSeconds = Math.round(serverTime / 1000); + Services.prefs.setIntPref(PREF_KINTO_ONECRL_CHECKED_SECONDS, + checkedServerTimeInSeconds); + } + + return Task.spawn(function* () { + try { + yield blocklist.db.open(); + let collectionLastModified = yield blocklist.db.getLastModified(); + // if the data is up to date, there's no need to sync. We still need + // to record the fact that a check happened. + if (lastModified <= collectionLastModified) { + updateLastCheck(); + return; + } + yield blocklist.sync(); + let list = yield blocklist.list(); + for (let item of list.data) { + if (item.issuerName && item.serialNumber) { + certList.revokeCertByIssuerAndSerial(item.issuerName, + item.serialNumber); + } else if (item.subject && item.pubKeyHash) { + certList.revokeCertBySubjectAndPubKey(item.subject, + item.pubKeyHash); + } else { + throw new Error("Cert blocklist record has incomplete data"); + } + } + // We explicitly do not want to save entries or update the + // last-checked time if sync fails + certList.saveEntries(); + updateLastCheck(); + } finally { + blocklist.db.close() + } + }); + } +} + +this.OneCRLClient = new CertBlocklistClient(); diff --git a/services/common/moz.build b/services/common/moz.build index aac754a292f..f33b75ee03d 100644 --- a/services/common/moz.build +++ b/services/common/moz.build @@ -15,6 +15,7 @@ EXTRA_COMPONENTS += [ EXTRA_JS_MODULES['services-common'] += [ 'async.js', + 'KintoCertificateBlocklist.js', 'logmanager.js', 'moz-kinto-client.js', 'observers.js', diff --git a/services/common/tests/unit/test_kintoCertBlocklist.js b/services/common/tests/unit/test_kintoCertBlocklist.js new file mode 100644 index 00000000000..ead270bbf88 --- /dev/null +++ b/services/common/tests/unit/test_kintoCertBlocklist.js @@ -0,0 +1,189 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { Constructor: CC } = Components; + +Cu.import("resource://services-common/KintoCertificateBlocklist.js"); +Cu.import("resource://services-common/moz-kinto-client.js") +Cu.import("resource://testing-common/httpd.js"); + +const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", "setInputStream"); + +var server; + +// set up what we need to make storage adapters +const Kinto = loadKinto(); +const FirefoxAdapter = Kinto.adapters.FirefoxAdapter; +const kintoFilename = "kinto.sqlite"; + +let kintoClient; + +function do_get_kinto_collection(collectionName) { + if (!kintoClient) { + let config = { + // Set the remote to be some server that will cause test failure when + // hit since we should never hit the server directly, only via maybeSync() + remote: "https://firefox.settings.services.mozilla.com/v1/", + // Set up the adapter and bucket as normal + adapter: FirefoxAdapter, + bucket: "blocklists" + }; + kintoClient = new Kinto(config); + } + return kintoClient.collection(collectionName); +} + +// Some simple tests to demonstrate that the logic inside maybeSync works +// correctly and that simple kinto operations are working as expected. There +// are more tests for core Kinto.js (and its storage adapter) in the +// xpcshell tests under /services/common +add_task(function* test_something(){ + const configPath = "/v1/"; + const recordsPath = "/v1/buckets/blocklists/collections/certificates/records"; + + Services.prefs.setCharPref("services.kinto.base", + `http://localhost:${server.identity.primaryPort}/v1`); + + // register a handler + function handleResponse (request, response) { + try { + const sampled = getSampleResponse(request, server.identity.primaryPort); + if (!sampled) { + do_throw(`unexpected ${request.method} request for ${request.path}?${request.queryString}`); + } + + response.setStatusLine(null, sampled.status.status, + sampled.status.statusText); + // send the headers + for (let headerLine of sampled.sampleHeaders) { + let headerElements = headerLine.split(':'); + response.setHeader(headerElements[0], headerElements[1].trimLeft()); + } + response.setHeader("Date", (new Date()).toUTCString()); + + response.write(sampled.responseBody); + } catch (e) { + dump(`${e}\n`); + } + } + server.registerPathHandler(configPath, handleResponse); + server.registerPathHandler(recordsPath, handleResponse); + + // Test an empty db populates + let result = yield OneCRLClient.maybeSync(2000, Date.now()); + + // Open the collection, verify it's been populated: + // Our test data has a single record; it should be in the local collection + let collection = do_get_kinto_collection("certificates"); + yield collection.db.open(); + let list = yield collection.list(); + do_check_eq(list.data.length, 1); + yield collection.db.close(); + + // Test the db is updated when we call again with a later lastModified value + result = yield OneCRLClient.maybeSync(4000, Date.now()); + + // Open the collection, verify it's been updated: + // Our test data now has two records; both should be in the local collection + collection = do_get_kinto_collection("certificates"); + yield collection.db.open(); + list = yield collection.list(); + do_check_eq(list.data.length, 3); + yield collection.db.close(); + + // Try to maybeSync with the current lastModified value - no connection + // should be attempted. + // Clear the kinto base pref so any connections will cause a test failure + Services.prefs.clearUserPref("services.kinto.base"); + yield OneCRLClient.maybeSync(4000, Date.now()); + + // Try again with a lastModified value at some point in the past + yield OneCRLClient.maybeSync(3000, Date.now()); + + // Check the OneCRL check time pref is modified, even if the collection + // hasn't changed + Services.prefs.setIntPref("services.kinto.onecrl.checked", 0); + yield OneCRLClient.maybeSync(3000, Date.now()); + let newValue = Services.prefs.getIntPref("services.kinto.onecrl.checked"); + do_check_neq(newValue, 0); +}); + +function run_test() { + // Set up an HTTP Server + server = new HttpServer(); + server.start(-1); + + run_next_test(); + + do_register_cleanup(function() { + server.stop(function() { }); + }); +} + +// get a response for a given request from sample data +function getSampleResponse(req, port) { + const responses = { + "OPTIONS": { + "sampleHeaders": [ + "Access-Control-Allow-Headers: Content-Length,Expires,Backoff,Retry-After,Last-Modified,Total-Records,ETag,Pragma,Cache-Control,authorization,content-type,if-none-match,Alert,Next-Page", + "Access-Control-Allow-Methods: GET,HEAD,OPTIONS,POST,DELETE,OPTIONS", + "Access-Control-Allow-Origin: *", + "Content-Type: application/json; charset=UTF-8", + "Server: waitress" + ], + "status": {status: 200, statusText: "OK"}, + "responseBody": "null" + }, + "GET:/v1/?": { + "sampleHeaders": [ + "Access-Control-Allow-Origin: *", + "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", + "Content-Type: application/json; charset=UTF-8", + "Server: waitress" + ], + "status": {status: 200, statusText: "OK"}, + "responseBody": JSON.stringify({"settings":{"cliquet.batch_max_requests":25}, "url":`http://localhost:${port}/v1/`, "documentation":"https://kinto.readthedocs.org/", "version":"1.5.1", "commit":"cbc6f58", "hello":"kinto"}) + }, + "GET:/v1/buckets/blocklists/collections/certificates/records?": { + "sampleHeaders": [ + "Access-Control-Allow-Origin: *", + "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", + "Content-Type: application/json; charset=UTF-8", + "Server: waitress", + "Etag: \"3000\"" + ], + "status": {status: 200, statusText: "OK"}, + "responseBody": JSON.stringify({"data":[{ + "issuerName": "MEQxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHjAcBgNVBAMTFXRoYXd0ZSBFViBTU0wgQ0EgLSBHMw==", + "serialNumber":"CrTHPEE6AZSfI3jysin2bA==", + "id":"78cf8900-fdea-4ce5-f8fb-b78710617718", + "last_modified":3000 + }]}) + }, + "GET:/v1/buckets/blocklists/collections/certificates/records?_since=3000": { + "sampleHeaders": [ + "Access-Control-Allow-Origin: *", + "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", + "Content-Type: application/json; charset=UTF-8", + "Server: waitress", + "Etag: \"4000\"" + ], + "status": {status: 200, statusText: "OK"}, + "responseBody": JSON.stringify({"data":[{ + "issuerName":"MFkxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKjAoBgNVBAMTIVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPdmVyaGVpZCBDQQ", + "serialNumber":"ATFpsA==", + "id":"dabafde9-df4a-ddba-2548-748da04cc02c", + "last_modified":4000 + },{ + "subject":"MCIxIDAeBgNVBAMMF0Fub3RoZXIgVGVzdCBFbmQtZW50aXR5", + "pubKeyHash":"VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=", + "id":"dabafde9-df4a-ddba-2548-748da04cc02d", + "last_modified":4000 + }]}) + } + }; + return responses[`${req.method}:${req.path}?${req.queryString}`] || + responses[req.method]; + +} diff --git a/services/common/tests/unit/xpcshell.ini b/services/common/tests/unit/xpcshell.ini index 3288abc7651..215a8006226 100644 --- a/services/common/tests/unit/xpcshell.ini +++ b/services/common/tests/unit/xpcshell.ini @@ -10,6 +10,7 @@ support-files = [test_load_modules.js] [test_kinto.js] +[test_kintoCertBlocklist.js] [test_storage_adapter.js] [test_utils_atob.js] diff --git a/testing/crashtest/crashtests.list b/testing/crashtest/crashtests.list index b75debbad51..863c10b9f61 100644 --- a/testing/crashtest/crashtests.list +++ b/testing/crashtest/crashtests.list @@ -20,6 +20,7 @@ include ../../dom/mathml/crashtests/crashtests.list include ../../dom/media/mediasource/test/crashtests/crashtests.list include ../../dom/media/test/crashtests/crashtests.list skip-if(!webrtc) include ../../dom/media/tests/crashtests/crashtests.list +include ../../dom/media/webspeech/synth/crashtests/crashtests.list include ../../dom/offline/crashtests/crashtests.list include ../../dom/plugins/test/crashtests/crashtests.list include ../../dom/smil/crashtests/crashtests.list diff --git a/testing/marionette/driver.js b/testing/marionette/driver.js index 4e920324971..febe3a07970 100644 --- a/testing/marionette/driver.js +++ b/testing/marionette/driver.js @@ -515,7 +515,7 @@ GeckoDriver.prototype.listeningPromise = function() { }; /** Create a new session. */ -GeckoDriver.prototype.newSession = function(cmd, resp) { +GeckoDriver.prototype.newSession = function*(cmd, resp) { this.sessionId = cmd.parameters.sessionId || cmd.parameters.session_id || elements.generateUUID(); @@ -640,10 +640,10 @@ GeckoDriver.prototype.setSessionCapabilities = function(newCaps) { to = copy(from[key], to); break; case "requiredCapabilities": - if (from[key]["proxy"]) { - this.setUpProxy(from[key]["proxy"]); - to["proxy"] = from[key]["proxy"]; - delete from[key]["proxy"]; + if (from[key].proxy) { + this.setUpProxy(from[key].proxy); + to.proxy = from[key].proxy; + delete from[key].proxy; } for (let caps in from[key]) { if (from[key][caps] !== this.sessionCapabilities[caps]) { @@ -716,6 +716,7 @@ GeckoDriver.prototype.setUpProxy = function(proxy) { case "NOPROXY": default: Preferences.set("network.proxy.type", 0); + break; } } else { throw new InvalidArgumentError("Value of 'proxy' should be an object"); @@ -874,7 +875,7 @@ GeckoDriver.prototype.executeScriptInSandbox = function( * If directInject is ture, it will run directly and not as a function * body. */ -GeckoDriver.prototype.execute = function(cmd, resp, directInject) { +GeckoDriver.prototype.execute = function*(cmd, resp, directInject) { let {inactivityTimeout, scriptTimeout, script, @@ -985,7 +986,7 @@ GeckoDriver.prototype.setScriptTimeout = function(cmd, resp) { * Execute pure JavaScript. Used to execute mochitest-like Marionette * tests. */ -GeckoDriver.prototype.executeJSScript = function(cmd, resp) { +GeckoDriver.prototype.executeJSScript = function*(cmd, resp) { // TODO(ato): cmd.newSandbox doesn't ever exist? // All pure JS scripts will need to call // Marionette.finish() to complete the test @@ -1036,7 +1037,7 @@ GeckoDriver.prototype.executeJSScript = function(cmd, resp) { * If directInject is true, it will be run directly and not as a * function body. */ -GeckoDriver.prototype.executeWithCallback = function(cmd, resp, directInject) { +GeckoDriver.prototype.executeWithCallback = function*(cmd, resp, directInject) { let {script, args, newSandbox, @@ -1215,7 +1216,7 @@ GeckoDriver.prototype.executeWithCallback = function(cmd, resp, directInject) { * @param {string} url * URL to navigate to. */ -GeckoDriver.prototype.get = function(cmd, resp) { +GeckoDriver.prototype.get = function*(cmd, resp) { let url = cmd.parameters.url; switch (this.context) { @@ -1292,17 +1293,15 @@ GeckoDriver.prototype.getCurrentUrl = function(cmd) { switch (this.context) { case Context.CHROME: return this.getCurrentWindow().location.href; - break; case Context.CONTENT: let isB2G = this.appName == "B2G"; return this.listener.getCurrentUrl(isB2G); - break; } }; /** Gets the current title of the window. */ -GeckoDriver.prototype.getTitle = function(cmd, resp) { +GeckoDriver.prototype.getTitle = function*(cmd, resp) { switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); @@ -1322,7 +1321,7 @@ GeckoDriver.prototype.getWindowType = function(cmd, resp) { }; /** Gets the page source of the content document. */ -GeckoDriver.prototype.getPageSource = function(cmd, resp) { +GeckoDriver.prototype.getPageSource = function*(cmd, resp) { switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); @@ -1337,17 +1336,17 @@ GeckoDriver.prototype.getPageSource = function(cmd, resp) { }; /** Go back in history. */ -GeckoDriver.prototype.goBack = function(cmd, resp) { +GeckoDriver.prototype.goBack = function*(cmd, resp) { yield this.listener.goBack(); }; /** Go forward in history. */ -GeckoDriver.prototype.goForward = function(cmd, resp) { +GeckoDriver.prototype.goForward = function*(cmd, resp) { yield this.listener.goForward(); }; /** Refresh the page. */ -GeckoDriver.prototype.refresh = function(cmd, resp) { +GeckoDriver.prototype.refresh = function*(cmd, resp) { yield this.listener.refresh(); }; @@ -1527,7 +1526,7 @@ GeckoDriver.prototype.setWindowPosition = function(cmd, resp) { * @param {string} name * Target name or ID of the window to switch to. */ -GeckoDriver.prototype.switchToWindow = function(cmd, resp) { +GeckoDriver.prototype.switchToWindow = function*(cmd, resp) { let switchTo = cmd.parameters.name; let isB2G = this.appName == "B2G"; let found; @@ -1620,10 +1619,7 @@ GeckoDriver.prototype.getActiveFrame = function(cmd, resp) { } }; -/** - * - */ -GeckoDriver.prototype.switchToParentFrame = function (cmd, resp) { +GeckoDriver.prototype.switchToParentFrame = function*(cmd, resp) { let res = yield this.listener.switchToParentFrame(); }; @@ -1636,7 +1632,7 @@ GeckoDriver.prototype.switchToParentFrame = function (cmd, resp) { * If element is not defined, then this holds either the id, name, * or index of the frame to switch to. */ -GeckoDriver.prototype.switchToFrame = function(cmd, resp) { +GeckoDriver.prototype.switchToFrame = function*(cmd, resp) { let {id, element, focus} = cmd.parameters; let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); @@ -1711,7 +1707,7 @@ GeckoDriver.prototype.switchToFrame = function(cmd, resp) { let frames = curWindow.document.getElementsByTagName("iframe"); let numFrames = frames.length; for (let i = 0; i < numFrames; i++) { - if (XPCNativeWrapper(frames[i]) == XPCNativeWrapper(wantedFrame)) { + if (new XPCNativeWrapper(frames[i]) == new XPCNativeWrapper(wantedFrame)) { curWindow = frames[i].contentWindow; this.curFrame = curWindow; if (focus) { @@ -1836,7 +1832,7 @@ GeckoDriver.prototype.timeouts = function(cmd, resp) { }; /** Single tap. */ -GeckoDriver.prototype.singleTap = function(cmd, resp) { +GeckoDriver.prototype.singleTap = function*(cmd, resp) { let {id, x, y} = cmd.parameters; switch (this.context) { @@ -1860,7 +1856,7 @@ GeckoDriver.prototype.singleTap = function(cmd, resp) { * @return {number} * Last touch ID. */ -GeckoDriver.prototype.actionChain = function(cmd, resp) { +GeckoDriver.prototype.actionChain = function*(cmd, resp) { let {chain, nextId} = cmd.parameters; switch (this.context) { @@ -1896,7 +1892,7 @@ GeckoDriver.prototype.actionChain = function(cmd, resp) { * the middle array represents a collection of events for each * finger, and the outer array represents all fingers. */ -GeckoDriver.prototype.multiAction = function(cmd, resp) { +GeckoDriver.prototype.multiAction = function*(cmd, resp) { switch (this.context) { case Context.CHROME: throw new WebDriverError("Command 'multiAction' is not available in chrome context"); @@ -1916,7 +1912,7 @@ GeckoDriver.prototype.multiAction = function(cmd, resp) { * @param {string} value * Value the client is looking for. */ -GeckoDriver.prototype.findElement = function(cmd, resp) { +GeckoDriver.prototype.findElement = function*(cmd, resp) { switch (this.context) { case Context.CHROME: resp.body.value = yield new Promise((resolve, reject) => { @@ -1949,7 +1945,7 @@ GeckoDriver.prototype.findElement = function(cmd, resp) { * @param {string} value * Value the client is looking for. */ -GeckoDriver.prototype.findElements = function(cmd, resp) { +GeckoDriver.prototype.findElements = function*(cmd, resp) { switch (this.context) { case Context.CHROME: resp.body = yield new Promise((resolve, reject) => { @@ -1975,7 +1971,7 @@ GeckoDriver.prototype.findElements = function(cmd, resp) { }; /** Return the active element on the page. */ -GeckoDriver.prototype.getActiveElement = function(cmd, resp) { +GeckoDriver.prototype.getActiveElement = function*(cmd, resp) { resp.body.value = yield this.listener.getActiveElement(); }; @@ -1985,14 +1981,14 @@ GeckoDriver.prototype.getActiveElement = function(cmd, resp) { * @param {string} id * Reference ID to the element that will be clicked. */ -GeckoDriver.prototype.clickElement = function(cmd, resp) { +GeckoDriver.prototype.clickElement = function*(cmd, resp) { let id = cmd.parameters.id; switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); yield this.interactions.clickElement({ frame: win }, - this.curBrowser.elementManager, id) + this.curBrowser.elementManager, id); break; case Context.CONTENT: @@ -2014,7 +2010,7 @@ GeckoDriver.prototype.clickElement = function(cmd, resp) { * @param {string} name * Name of the attribute to retrieve. */ -GeckoDriver.prototype.getElementAttribute = function(cmd, resp) { +GeckoDriver.prototype.getElementAttribute = function*(cmd, resp) { let {id, name} = cmd.parameters; switch (this.context) { @@ -2037,7 +2033,7 @@ GeckoDriver.prototype.getElementAttribute = function(cmd, resp) { * @param {string} id * Reference ID to the element that will be inspected. */ -GeckoDriver.prototype.getElementText = function(cmd, resp) { +GeckoDriver.prototype.getElementText = function*(cmd, resp) { let id = cmd.parameters.id; switch (this.context) { @@ -2062,7 +2058,7 @@ GeckoDriver.prototype.getElementText = function(cmd, resp) { * @param {string} id * Reference ID to the element that will be inspected. */ -GeckoDriver.prototype.getElementTagName = function(cmd, resp) { +GeckoDriver.prototype.getElementTagName = function*(cmd, resp) { let id = cmd.parameters.id; switch (this.context) { @@ -2084,7 +2080,7 @@ GeckoDriver.prototype.getElementTagName = function(cmd, resp) { * @param {string} id * Reference ID to the element that will be inspected. */ -GeckoDriver.prototype.isElementDisplayed = function(cmd, resp) { +GeckoDriver.prototype.isElementDisplayed = function*(cmd, resp) { let id = cmd.parameters.id; switch (this.context) { @@ -2108,7 +2104,7 @@ GeckoDriver.prototype.isElementDisplayed = function(cmd, resp) { * @param {string} propertyName * CSS rule that is being requested. */ -GeckoDriver.prototype.getElementValueOfCssProperty = function(cmd, resp) { +GeckoDriver.prototype.getElementValueOfCssProperty = function*(cmd, resp) { let {id, propertyName: prop} = cmd.parameters; switch (this.context) { @@ -2131,7 +2127,7 @@ GeckoDriver.prototype.getElementValueOfCssProperty = function(cmd, resp) { * @param {string} id * Reference ID to the element that will be checked. */ -GeckoDriver.prototype.isElementEnabled = function(cmd, resp) { +GeckoDriver.prototype.isElementEnabled = function*(cmd, resp) { let id = cmd.parameters.id; switch (this.context) { @@ -2154,7 +2150,7 @@ GeckoDriver.prototype.isElementEnabled = function(cmd, resp) { * @param {string} id * Reference ID to the element that will be checked. */ -GeckoDriver.prototype.isElementSelected = function(cmd, resp) { +GeckoDriver.prototype.isElementSelected = function*(cmd, resp) { let id = cmd.parameters.id; switch (this.context) { @@ -2171,7 +2167,7 @@ GeckoDriver.prototype.isElementSelected = function(cmd, resp) { } }; -GeckoDriver.prototype.getElementRect = function(cmd, resp) { +GeckoDriver.prototype.getElementRect = function*(cmd, resp) { let id = cmd.parameters.id; switch (this.context) { @@ -2201,7 +2197,7 @@ GeckoDriver.prototype.getElementRect = function(cmd, resp) { * @param {string} value * Value to send to the element. */ -GeckoDriver.prototype.sendKeysToElement = function(cmd, resp) { +GeckoDriver.prototype.sendKeysToElement = function*(cmd, resp) { let {id, value} = cmd.parameters; if (!value) { @@ -2251,7 +2247,7 @@ GeckoDriver.prototype.sendKeysToElement = function(cmd, resp) { }; /** Sets the test name. The test name is used for logging purposes. */ -GeckoDriver.prototype.setTestName = function(cmd, resp) { +GeckoDriver.prototype.setTestName = function*(cmd, resp) { let val = cmd.parameters.value; this.testName = val; yield this.listener.setTestName({value: val}); @@ -2263,7 +2259,7 @@ GeckoDriver.prototype.setTestName = function(cmd, resp) { * @param {string} id * Reference ID to the element that will be cleared. */ -GeckoDriver.prototype.clearElement = function(cmd, resp) { +GeckoDriver.prototype.clearElement = function*(cmd, resp) { let id = cmd.parameters.id; switch (this.context) { @@ -2289,14 +2285,14 @@ GeckoDriver.prototype.clearElement = function(cmd, resp) { * * @param {string} id element id. */ -GeckoDriver.prototype.switchToShadowRoot = function(cmd, resp) { +GeckoDriver.prototype.switchToShadowRoot = function*(cmd, resp) { let id; if (cmd.parameters) { id = cmd.parameters.id; } yield this.listener.switchToShadowRoot(id); }; /** Add a cookie to the document. */ -GeckoDriver.prototype.addCookie = function(cmd, resp) { +GeckoDriver.prototype.addCookie = function*(cmd, resp) { let cb = msg => { this.mm.removeMessageListener("Marionette:addCookie", cb); let cookie = msg.json; @@ -2321,12 +2317,12 @@ GeckoDriver.prototype.addCookie = function(cmd, resp) { * This is the equivalent of calling {@code document.cookie} and parsing * the result. */ -GeckoDriver.prototype.getCookies = function(cmd, resp) { +GeckoDriver.prototype.getCookies = function*(cmd, resp) { resp.body = yield this.listener.getCookies(); }; /** Delete all cookies that are visible to a document. */ -GeckoDriver.prototype.deleteAllCookies = function(cmd, resp) { +GeckoDriver.prototype.deleteAllCookies = function*(cmd, resp) { let cb = msg => { let cookie = msg.json; cookieManager.remove( @@ -2342,7 +2338,7 @@ GeckoDriver.prototype.deleteAllCookies = function(cmd, resp) { }; /** Delete a cookie by name. */ -GeckoDriver.prototype.deleteCookie = function(cmd, resp) { +GeckoDriver.prototype.deleteCookie = function*(cmd, resp) { let cb = msg => { this.mm.removeMessageListener("Marionette:deleteCookie", cb); let cookie = msg.json; @@ -2507,11 +2503,11 @@ GeckoDriver.prototype.deleteSession = function(cmd, resp) { }; /** Returns the current status of the Application Cache. */ -GeckoDriver.prototype.getAppCacheStatus = function(cmd, resp) { +GeckoDriver.prototype.getAppCacheStatus = function*(cmd, resp) { resp.body.value = yield this.listener.getAppCacheStatus(); }; -GeckoDriver.prototype.importScript = function(cmd, resp) { +GeckoDriver.prototype.importScript = function*(cmd, resp) { let script = cmd.parameters.script; let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] @@ -2634,7 +2630,6 @@ GeckoDriver.prototype.takeScreenshot = function(cmd, resp) { case Context.CONTENT: return this.listener.takeScreenshot(id, full, highlights); - break; } }; diff --git a/testing/marionette/driver/marionette_driver/geckoinstance.py b/testing/marionette/driver/marionette_driver/geckoinstance.py index 2246785b4be..32e976061ec 100644 --- a/testing/marionette/driver/marionette_driver/geckoinstance.py +++ b/testing/marionette/driver/marionette_driver/geckoinstance.py @@ -35,6 +35,11 @@ class GeckoInstance(object): "datareporting.policy.dataSubmissionEnabled": False, "datareporting.policy.dataSubmissionPolicyAccepted": False, "dom.ipc.reportProcessHangs": False, + # Only install add-ons from the profile and the application scope + # Also ensure that those are not getting disabled. + # see: https://developer.mozilla.org/en/Installing_extensions + "extensions.enabledScopes": 5, + "extensions.autoDisableScopes": 10, "focusmanager.testmode": True, "marionette.defaultPrefs.enabled": True, "startup.homepage_welcome_url": "about:blank", @@ -204,8 +209,6 @@ class DesktopInstance(GeckoInstance): 'browser.tabs.warnOnOpen': False, 'browser.uitour.enabled': False, 'dom.report_all_js_exceptions': True, - 'extensions.enabledScopes': 5, - 'extensions.autoDisableScopes': 10, 'extensions.getAddons.cache.enabled': False, 'extensions.installDistroAddons': False, 'extensions.logging.enabled': True, diff --git a/testing/marionette/unit.ini b/testing/marionette/unit.ini index c874bc0ec7b..e36a99281e8 100644 --- a/testing/marionette/unit.ini +++ b/testing/marionette/unit.ini @@ -5,9 +5,7 @@ # xpcshell unit tests for Marionette [DEFAULT] -head = -tail = -skip-if = appname == "thunderbird" # Thunderbird does not use marionette +skip-if = appname == "thunderbird" [test_error.js] [test_message.js] diff --git a/testing/mochitest/mochitest_options.py b/testing/mochitest/mochitest_options.py index 4fd12d6bed7..43bcc43929d 100644 --- a/testing/mochitest/mochitest_options.py +++ b/testing/mochitest/mochitest_options.py @@ -795,11 +795,6 @@ class MochitestArguments(ArgumentContainer): "geckomediaplugin": 20000, } - # Bug 1091917 - We exit early in tab processes on Windows, so we don't - # get leak logs yet. - if mozinfo.isWin: - options.ignoreMissingLeaks.append("tab") - # XXX We can't normalize test_paths in the non build_obj case here, # because testRoot depends on the flavor, which is determined by the # mach command and therefore not finalized yet. Conversely, test paths diff --git a/testing/mozharness/configs/android/android_panda_releng.py b/testing/mozharness/configs/android/android_panda_releng.py index 999832f4abb..23a23604cb6 100644 --- a/testing/mozharness/configs/android/android_panda_releng.py +++ b/testing/mozharness/configs/android/android_panda_releng.py @@ -171,6 +171,8 @@ config = { "--symbols-path=%(symbols_path)s", "--extra-profile-file=reftest/fonts", "--suite=reftest", + "--log-raw=%(raw_log_file)s", + "--log-errorsummary=%(error_summary_file)s", ], "tests": ["reftest/tests/layout/reftests/reftest.list"], "run_filename": "remotereftest.py", diff --git a/testing/mozharness/configs/android/androidarm.py b/testing/mozharness/configs/android/androidarm.py index 2facdad4ba4..a1d9b18b7da 100644 --- a/testing/mozharness/configs/android/androidarm.py +++ b/testing/mozharness/configs/android/androidarm.py @@ -143,6 +143,8 @@ config = { "--total-chunks=16", "--extra-profile-file=fonts", "--suite=reftest", + "--log-raw=%(raw_log_file)s", + "--log-errorsummary=%(error_summary_file)s", ], "tests": ["tests/layout/reftests/reftest.list"], }, diff --git a/testing/mozharness/configs/android/androidarm_4_3.py b/testing/mozharness/configs/android/androidarm_4_3.py index 0f17cb88622..9ba8d7e6c09 100644 --- a/testing/mozharness/configs/android/androidarm_4_3.py +++ b/testing/mozharness/configs/android/androidarm_4_3.py @@ -141,6 +141,8 @@ config = { "--total-chunks=16", "--extra-profile-file=fonts", "--suite=reftest", + "--log-raw=%(raw_log_file)s", + "--log-errorsummary=%(error_summary_file)s", ], "tests": ["tests/layout/reftests/reftest.list",], }, diff --git a/testing/mozharness/configs/builds/releng_base_android_64_builds.py b/testing/mozharness/configs/builds/releng_base_android_64_builds.py index 06e14540e4f..5c0a291105a 100644 --- a/testing/mozharness/configs/builds/releng_base_android_64_builds.py +++ b/testing/mozharness/configs/builds/releng_base_android_64_builds.py @@ -32,6 +32,8 @@ config = { ('/home/cltbld/.ssh', '/home/mock_mozilla/.ssh'), ('/home/cltbld/.hgrc', '/builds/.hgrc'), ('/home/cltbld/.boto', '/builds/.boto'), + ('/builds/relengapi.tok', '/builds/relengapi.tok'), + ('/tools/tooltool.py', '/builds/tooltool.py'), ('/builds/mozilla-api.key', '/builds/mozilla-api.key'), ('/builds/mozilla-fennec-geoloc-api.key', '/builds/mozilla-fennec-geoloc-api.key'), ('/builds/crash-stats-api.token', '/builds/crash-stats-api.token'), diff --git a/testing/mozharness/configs/builds/releng_base_linux_32_builds.py b/testing/mozharness/configs/builds/releng_base_linux_32_builds.py index 8641eba7c36..6ef7dc96282 100644 --- a/testing/mozharness/configs/builds/releng_base_linux_32_builds.py +++ b/testing/mozharness/configs/builds/releng_base_linux_32_builds.py @@ -43,6 +43,7 @@ config = { ('/builds/mozilla-desktop-geoloc-api.key', '/builds/mozilla-desktop-geoloc-api.key'), ('/builds/crash-stats-api.token', '/builds/crash-stats-api.token'), ('/builds/adjust-sdk.token', '/builds/adjust-sdk.token'), + ('/builds/adjust-sdk-beta.token', '/builds/adjust-sdk-beta.token'), ('/usr/local/lib/hgext', '/usr/local/lib/hgext'), ], 'enable_ccache': True, diff --git a/testing/mozharness/configs/builds/releng_base_linux_64_builds.py b/testing/mozharness/configs/builds/releng_base_linux_64_builds.py index 84c70168491..e8dc78e6629 100644 --- a/testing/mozharness/configs/builds/releng_base_linux_64_builds.py +++ b/testing/mozharness/configs/builds/releng_base_linux_64_builds.py @@ -43,6 +43,7 @@ config = { ('/builds/mozilla-desktop-geoloc-api.key', '/builds/mozilla-desktop-geoloc-api.key'), ('/builds/crash-stats-api.token', '/builds/crash-stats-api.token'), ('/builds/adjust-sdk.token', '/builds/adjust-sdk.token'), + ('/builds/adjust-sdk-beta.token', '/builds/adjust-sdk-beta.token'), ('/usr/local/lib/hgext', '/usr/local/lib/hgext'), ], 'enable_ccache': True, diff --git a/testing/mozharness/configs/hazards/common.py b/testing/mozharness/configs/hazards/common.py index 0b62c0acc7c..be6d2ca2b5f 100644 --- a/testing/mozharness/configs/hazards/common.py +++ b/testing/mozharness/configs/hazards/common.py @@ -82,6 +82,8 @@ config = { ], "mock_files": [ ("/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"), + ('/home/cltbld/.hgrc', '/builds/.hgrc'), + ('/builds/relengapi.tok', '/builds/relengapi.tok'), ("/tools/tooltool.py", "/tools/tooltool.py"), ('/usr/local/lib/hgext', '/usr/local/lib/hgext'), ], diff --git a/testing/mozharness/configs/unittests/linux_unittest.py b/testing/mozharness/configs/unittests/linux_unittest.py index 136cff910c3..3431029a3f2 100644 --- a/testing/mozharness/configs/unittests/linux_unittest.py +++ b/testing/mozharness/configs/unittests/linux_unittest.py @@ -157,7 +157,9 @@ config = { "--appname=%(binary_path)s", "--utility-path=tests/bin", "--extra-profile-file=tests/bin/plugins", - "--symbols-path=%(symbols_path)s" + "--symbols-path=%(symbols_path)s", + "--log-raw=%(raw_log_file)s", + "--log-errorsummary=%(error_summary_file)s", ], "run_filename": "runreftest.py", "testsdir": "reftest" diff --git a/testing/mozharness/configs/unittests/win_unittest.py b/testing/mozharness/configs/unittests/win_unittest.py index bb8c3c8dfd2..9abc4a76941 100644 --- a/testing/mozharness/configs/unittests/win_unittest.py +++ b/testing/mozharness/configs/unittests/win_unittest.py @@ -120,7 +120,9 @@ config = { "--appname=%(binary_path)s", "--utility-path=tests/bin", "--extra-profile-file=tests/bin/plugins", - "--symbols-path=%(symbols_path)s" + "--symbols-path=%(symbols_path)s", + "--log-raw=%(raw_log_file)s", + "--log-errorsummary=%(error_summary_file)s", ], "run_filename": "runreftest.py", "testsdir": "reftest" diff --git a/testing/mozharness/mozharness/base/vcs/mercurial.py b/testing/mozharness/mozharness/base/vcs/mercurial.py index e41d6d84d89..228f9634c37 100755 --- a/testing/mozharness/mozharness/base/vcs/mercurial.py +++ b/testing/mozharness/mozharness/base/vcs/mercurial.py @@ -420,6 +420,7 @@ class MercurialVCS(ScriptMixin, LogMixin, object): with "source" using Mercurial's share extension """ self.info("Sharing %s to %s." % (source, dest)) + self.mkdir_p(dest) if self.run_command(self.hg + ['share', '-U', source, dest], error_list=HgErrorList): raise VCSException("Unable to share %s to %s!" % (source, dest)) diff --git a/testing/mozharness/mozharness/mozilla/building/buildbase.py b/testing/mozharness/mozharness/mozilla/building/buildbase.py index 1088007a49b..bee671ccbcd 100755 --- a/testing/mozharness/mozharness/mozilla/building/buildbase.py +++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py @@ -1151,7 +1151,7 @@ or run without that action (ie: --no-{action})" if auth_file: cmd.extend(['--authentication-file', auth_file]) self.info(str(cmd)) - self.run_command(cmd, cwd=dirs['abs_src_dir'], halt_on_failure=True) + self.run_command_m(cmd, cwd=dirs['abs_src_dir'], halt_on_failure=True) def query_revision(self, source_path=None): """ returns the revision of the build diff --git a/testing/mozharness/mozharness/mozilla/tooltool.py b/testing/mozharness/mozharness/mozilla/tooltool.py index bcdb7eb4f78..ebf0c292666 100644 --- a/testing/mozharness/mozharness/mozilla/tooltool.py +++ b/testing/mozharness/mozharness/mozilla/tooltool.py @@ -78,8 +78,16 @@ class TooltoolMixin(object): if cache: cmd.extend(['-c', cache]) + # when mock is enabled run tooltool in mock. We can't use + # run_command_m in all cases because it won't exist unless + # MockMixin is used on the parent class + if self.config.get('mock_target'): + cmd_runner = self.run_command_m + else: + cmd_runner = self.run_command + self.retry( - self.run_command, + cmd_runner, args=(cmd, ), kwargs={'cwd': output_dir, 'error_list': TooltoolErrorList, diff --git a/testing/mozharness/scripts/merge_day/b2g_tag.py b/testing/mozharness/scripts/merge_day/b2g_tag.py deleted file mode 100755 index e528a9fc36e..00000000000 --- a/testing/mozharness/scripts/merge_day/b2g_tag.py +++ /dev/null @@ -1,379 +0,0 @@ -#!/usr/bin/env python -# ***** BEGIN LICENSE BLOCK ***** -# 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/. -# ***** END LICENSE BLOCK ***** -"""b2g_tag.py - -Tag the b2g repos for merge day. -""" - -import os -import pprint -import sys -import time - -try: - import simplejson as json - assert json -except ImportError: - import json - -sys.path.insert(1, os.path.dirname(os.path.dirname(sys.path[0]))) - -from mozharness.base.errors import GitErrorList, HgErrorList, VCSException -from mozharness.base.log import INFO -from mozharness.base.transfer import TransferMixin -from mozharness.base.vcs.vcsbase import MercurialScript - - -# B2GTag {{{1 -class B2GTag(TransferMixin, MercurialScript): - config_options = [ - [['--gecko-repo', ], { - "action": "extend", - "dest": "gecko_repos", - "type": "string", - "help": "Specify which gecko repo(s) to tag, along with gaia." - }], - [['--date-string', ], { - "action": "store", - "dest": "date_string", - "type": "string", - "default": time.strftime('%Y%m%d'), - "help": "Specify the date string to use in the tag name." - }], - ] - gecko_repos = None - - def __init__(self, require_config_file=True): - super(B2GTag, self).__init__( - config_options=self.config_options, - all_actions=[ - 'clobber', - 'clean-repos', - 'pull', - 'push-loop', - 'summary', - ], - default_actions=[ - 'clean-repos', - 'pull', - 'push-loop', - 'summary', - ], - require_config_file=require_config_file - ) - self.run_sanity_check() - -# Helper methods {{{1 - def run_sanity_check(self): - """ Make sure we're set up correctly before we start cloning or tagging - anything. - - First, verify any specified gecko_repos exist in the b2g_branches - dict. - - Next, make sure we can reach mapper and it's working. - - If there are any errors, compile the list and fatal(). - """ - sanity_message = "" - if self.config.get("gecko_repos"): - bad_branch_names = [] - for repo_name in self.query_gecko_repos(): - if repo_name not in self.config["b2g_branches"].keys(): - bad_branch_names.append(repo_name) - if bad_branch_names: - sanity_message += "The following branch name(s) aren't in b2g_branches: %s\n" % ",".join(bad_branch_names) - if 'push_loop' in self.actions: - try: - json_contents = self.load_json_from_url( - "%s/1" % self.config["gaia_mapper_base_url"], - log_level=INFO, - ) - assert json_contents["git_rev"] - except: - sanity_message += "Can't load %s/1 for hg->git mapping!\n" % self.config["gaia_mapper_base_url"] - if sanity_message: - self.fatal(sanity_message) - - def query_gecko_repos(self): - """ Which gecko repos + gaia are we converting? - By default everything in self.config['b2g_branches'], - but we can override that list with --gecko-repos. - """ - if self.gecko_repos: - return self.gecko_repos - self.gecko_repos = list(self.config.get("gecko_repos", - self.config['b2g_branches'].keys())) - self.gecko_repos.sort() - return self.gecko_repos - - def query_repo_pull_config(self, repo_name, b2g_branch_config): - """ Build the repo pull url for a repo. This is for convenience. - """ - return { - "repo": '%s/%s' % (self.config['hg_base_pull_url'], repo_name), - "dest": repo_name, - "tag": b2g_branch_config.get("tag", "default") - } - - def query_repo_push_url(self, repo_name): - """ Build the repo push url for a repo. This is for convenience. - """ - return '%s/%s' % (self.config['hg_base_push_url'], repo_name) - - def query_gaia_git_revision(self, repo_name, b2g_branch_config): - """ For most repos, read b2g/config/gaia.json, - read the hg gaia revision, convert that in mapper, and return it. - In the case of not being able to determine the git revision, - throw a VCSException. - - Otherwise return None, and we'll tag the tip of the branch. - """ - dirs = self.query_abs_dirs() - if not b2g_branch_config.get("use_gaia_json", True): - return None - json_path = os.path.join(dirs["abs_work_dir"], repo_name, "b2g", "config", "gaia.json") - if not os.path.exists(json_path): - raise VCSException("%s doesn't exist!" % json_path) - contents = self.read_from_file(json_path) - try: - json_contents = json.loads(contents) - hg_revision = json_contents['revision'] - except ValueError: - raise VCSException("%s is invalid json!" % json_path) - except KeyError: - raise VCSException("%s has no 'revision' set!" % json_path) - try: - url = "%s/%s" % (self.config["gaia_mapper_base_url"], hg_revision) - json_contents = self.load_json_from_url(url) - git_rev = json_contents["git_rev"] - assert git_rev - return git_rev - except: - raise VCSException("Unable to get git_rev from %s !" % url) - - def query_abs_dirs(self): - """ Override the built-in query_abs_dirs to provide an absolute path - for the gaia clone. - """ - if self.abs_dirs: - return self.abs_dirs - dirs = super(B2GTag, self).query_abs_dirs() - dirs['abs_gaia_dir'] = os.path.join(dirs['abs_work_dir'], 'gaia') - self.abs_dirs = dirs - return self.abs_dirs - - def query_tag_name(self, b2g_branch_config): - """ Return the tag name for a specific branch config. - e.g. B2G_1_3_20140428_MERGEDAY - """ - return b2g_branch_config["tag_name"] % {'DATE': self.config['date_string']} - - def query_short_tag_name(self, b2g_branch_config): - """ Return the shortened tag name for a specific branch config. - This assumes there's going to be a _%(DATE)s in the tag name. - - e.g. B2G_1_3 - """ - return b2g_branch_config["tag_name"].split("_%")[0] - - def hg_tag(self, repo_name, b2g_branch_config): - """ Attempt to tag and push gecko. This assumes the trees are open. - - On failure, throw a VCSException. - """ - hg = self.query_exe("hg", return_type="list") - dirs = self.query_abs_dirs() - hg_dir = os.path.join(dirs["abs_work_dir"], repo_name) - tag_name = self.query_tag_name(b2g_branch_config) - short_tag_name = self.query_short_tag_name(b2g_branch_config) - push_url = self.query_repo_push_url(repo_name) - cmd = hg + ["tag", tag_name, "-m", - "tagging %s for mergeday. r=a=mergeday CLOSED TREE DONTBUILD" % short_tag_name, - ] - if self.run_command(cmd, cwd=hg_dir, error_list=HgErrorList): - raise VCSException("Can't tag %s with %s" % (repo_name, tag_name)) - # Debugging! Echo only for now. - # cmd = hg + ["push", push_url] - cmd = hg + ["push", push_url] - if self.run_command(cmd, cwd=hg_dir, error_list=HgErrorList): - self.run_command(hg + ["--config", "extensions.mq=", - "strip", "--no-backup", "outgoing()"], - cwd=hg_dir) - self.run_command(hg + ["up", "-C"], - cwd=hg_dir) - self.run_command(hg + ["--config", "extensions.purge=", - "purge", "--all"], - cwd=hg_dir) - raise VCSException("Can't push to %s!" % push_url) - - def git_tag(self, hg_repo_name, gaia_git_revision, b2g_branch_config): - """ Attempt to tag and push gaia. - - On failure, throw a VCSException. - """ - git = self.query_exe("git", return_type="list") - dirs = self.query_abs_dirs() - tag_name = self.query_tag_name(b2g_branch_config) - cmd = git + ['tag', tag_name] - if gaia_git_revision is None: - if self.run_command( - git + ["checkout", b2g_branch_config["gaia_branch"]], - cwd=dirs["abs_gaia_dir"], - error_list=GitErrorList, - ): - raise VCSException("Can't checkout %s branch!" % b2g_branch_config["gaia_branch"]) - else: - cmd.append(gaia_git_revision) - if self.run_command( - cmd, - cwd=dirs["abs_gaia_dir"], - error_list=GitErrorList, - ): - raise VCSException("Can't tag gaia for %s!" % hg_repo_name) - if self.run_command( - # Debugging! Echo only for now. - git + ["push", "--tags"], - # comment out for testing - # git + ["push", "--tags"], - cwd=dirs["abs_gaia_dir"], - error_list=GitErrorList, - ): - raise VCSException("Can't push gaia tag for %s!" % hg_repo_name) - -# Actions {{{1 - def clean_repos(self): - """ We may end up with contaminated local repos at some point, but - we don't want to have to clobber and reclone from scratch every - time. - - This is an attempt to clean up the local repos without needing a - clobber. - """ - dirs = self.query_abs_dirs() - hg = self.query_exe("hg", return_type="list") - git = self.query_exe("git", return_type="list") - hg_repos = self.query_gecko_repos() - hg_strip_error_list = [{ - 'substr': r'''abort: empty revision set''', 'level': INFO, - 'explanation': "Nothing to clean up; we're good!", - }] + HgErrorList - for repo_name in hg_repos: - repo_path = os.path.join(dirs['abs_work_dir'], repo_name) - if os.path.exists(repo_path): - self.retry( - self.run_command, - args=(hg + ["--config", "extensions.mq=", "strip", - "--no-backup", "outgoing()"], ), - kwargs={ - 'cwd': repo_path, - 'error_list': hg_strip_error_list, - 'return_type': 'num_errors', - 'success_codes': (0, 255), - }, - ) - if os.path.exists(dirs['abs_gaia_dir']): - remote_tags = [] - output = self.get_output_from_command( - git + ["ls-remote", "--tags"], - cwd=dirs['abs_gaia_dir'], - ) - for line in output.splitlines(): - if 'refs/tags' not in line: - continue - remote_tags.append(line.split('/')[-1]) - output = self.get_output_from_command( - git + ["tag", "-l"], - cwd=dirs['abs_gaia_dir'], - ) - local_tags = output.splitlines() - for tag in local_tags: - if tag not in remote_tags: - self.info("Found local tag %s that doesn't exist on remote!" % tag) - self.run_command( - git + ['tag', '-d', tag], - cwd=dirs["abs_gaia_dir"], - ) - # Verify - output = self.get_output_from_command( - git + ["tag", "-l"], - cwd=dirs['abs_gaia_dir'], - ) - local_tags = output.splitlines() - if not set(local_tags).issubset(set(remote_tags)): - self.fatal("Gaia still has tags that don't exist on remote!") - else: - self.info("Looks like we're good, local_tags is a subset of remote_tags.") - - def pull(self): - """ Pull action. - - Builds an hg repo list-of-dicts and sends them to - MercurialScript.pull(). Also pulls the gaia_url. - - We'll potentially run another pull in the push-loop action, but - this action makes sure we have an up-to-date clone on disk to - operate on. - """ - dirs = self.query_abs_dirs() - git = self.query_exe("git", return_type="list") - hg_repos = [] - for repo_name in self.query_gecko_repos(): - b2g_branch_config = self.config['b2g_branches'][repo_name] - hg_repos.append(self.query_repo_pull_config(repo_name, b2g_branch_config)) - self.debug("HG repos: %s" % pprint.pformat(hg_repos)) - super(B2GTag, self).pull(repos=hg_repos) - if not os.path.exists(dirs['abs_gaia_dir']): - self.run_command( - git + ["clone", self.config["gaia_url"], "gaia"], - cwd=dirs['abs_work_dir'], - error_list=GitErrorList, - halt_on_failure=True, - ) - else: - self.run_command( - git + ["pull"], - cwd=dirs['abs_gaia_dir'], - error_list=GitErrorList, - halt_on_failure=True, - ) - - def push_loop(self): - """ Create the tag and push for each gecko+gaia pair. - This sometimes requires a pull+rebase, hence the loop. - """ - for repo_name in self.query_gecko_repos(): - b2g_branch_config = self.config['b2g_branches'][repo_name] - repo_config = self.query_repo_pull_config(repo_name, b2g_branch_config) - super(B2GTag, self).pull(repos=[repo_config]) - try: - # We'll get None (tag tip-of-branch) or a git revision or a - # VCSException here. - gaia_git_revision = self.query_gaia_git_revision(repo_name, b2g_branch_config) - except VCSException, e: - self.add_failure(repo_name, message=str(e)) - continue - if self.retry( - self.hg_tag, - retry_exceptions=(VCSException, ), - args=(repo_name, b2g_branch_config), - ): - self.add_failure(repo_name, message=str(e)) - continue - if self.retry( - self.git_tag, - retry_exceptions=(VCSException, ), - args=(repo_name, gaia_git_revision, b2g_branch_config), - ): - self.add_failure(repo_name, message=str(e)) - continue - - -# __main__ {{{1 -if __name__ == '__main__': - b2g_tag = B2GTag() - b2g_tag.run_and_exit() diff --git a/testing/taskcluster/tasks/tests/fx_test_base.yml b/testing/taskcluster/tasks/tests/fx_test_base.yml index bbc547ecc9f..c2d51ec241c 100644 --- a/testing/taskcluster/tasks/tests/fx_test_base.yml +++ b/testing/taskcluster/tasks/tests/fx_test_base.yml @@ -3,6 +3,8 @@ $inherits: from: 'tasks/test.yml' task: workerType: desktop-test + scopes: + - docker-worker:cache:level-{{level}}-{{project}}-test-workspace payload: image: type: 'task-image' @@ -13,6 +15,12 @@ task: NEED_PULSEAUDIO: true GECKO_HEAD_REPOSITORY: '{{{head_repository}}}' GECKO_HEAD_REV: '{{{head_rev}}}' + + cache: + # put the workspace and /tmp on a cache, less for inter-task caching than + # to get this directory on fast, non-aufs storage + level-{{level}}-{{project}}-test-workspace: '/home/worker/workspace' + artifacts: 'public/test': type: directory diff --git a/testing/web-platform/meta/url/a-element.html.ini b/testing/web-platform/meta/url/a-element.html.ini index 94ee09f4a0a..8d296eb1132 100644 --- a/testing/web-platform/meta/url/a-element.html.ini +++ b/testing/web-platform/meta/url/a-element.html.ini @@ -147,9 +147,6 @@ [Parsing: against ] expected: FAIL - [Parsing: against ] - expected: FAIL - [Parsing: against ] expected: FAIL @@ -500,10 +497,3 @@ [Parsing: against ] expected: FAIL - - [Parsing: against ] - expected: FAIL - - [Parsing: against ] - expected: FAIL - diff --git a/testing/web-platform/meta/url/url-constructor.html.ini b/testing/web-platform/meta/url/url-constructor.html.ini index c04845f940c..e3087a04bf3 100644 --- a/testing/web-platform/meta/url/url-constructor.html.ini +++ b/testing/web-platform/meta/url/url-constructor.html.ini @@ -96,9 +96,6 @@ [Parsing: against ] expected: FAIL - [Parsing: against ] - expected: FAIL - [Parsing: against ] expected: FAIL @@ -341,10 +338,3 @@ [Parsing: against ] expected: FAIL - - [Parsing: against ] - expected: FAIL - - [Parsing: against ] - expected: FAIL - diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-nocookie-worker.py b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-nocookie-worker.py index 5776c0019db..0f09b7e32c0 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-nocookie-worker.py +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-nocookie-worker.py @@ -9,6 +9,7 @@ def main(request, response): content_type = 'application/javascript' headers.append(('Content-Type', content_type)) - # Return a different script for each access. - return headers, '// %s' % (time.time()) + # Return a different script for each access. Use .time() and .clock() for + # best time resolution across different platforms. + return headers, '// %s %s' % (time.time(), time.clock()) diff --git a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-worker.py b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-worker.py index dbb61516977..bc9b32ad3e6 100644 --- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-worker.py +++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/update-worker.py @@ -40,6 +40,7 @@ def main(request, response): extra_body = "addEventListener('install', function(e) { throw new Error('boom'); });" headers.append(('Content-Type', content_type)) - # Return a different script for each access. - return headers, '/* %s */ %s' % (time.time(), extra_body) + # Return a different script for each access. Use .time() and .clock() for + # best time resolution across different platforms. + return headers, '/* %s %s */ %s' % (time.time(), time.clock(), extra_body) diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 0cd1a15050d..4c67864fbfa 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -3231,6 +3231,22 @@ "kind": "boolean", "description": "Did UrlClassifier fail to construct the PrefixSet?" }, + "URLCLASSIFIER_UPDATE_REMOTE_STATUS": { + "alert_emails": ["gcp@mozilla.com", "francois@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "n_values": 16, + "bug_numbers": [1150921], + "description": "Server HTTP status code from SafeBrowsing database updates. (0=1xx, 1=200, 2=2xx, 3=204, 4=3xx, 5=400, 6=4xx, 7=403, 8=404, 9=408, 10=413, 11=5xx, 12=502|504|511, 13=503, 14=505, 15=Other)" + }, + "URLCLASSIFIER_COMPLETE_REMOTE_STATUS": { + "alert_emails": ["gcp@mozilla.com", "francois@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "n_values": 16, + "bug_numbers": [1150921], + "description": "Server HTTP status code from remote SafeBrowsing gethash lookups. (0=1xx, 1=200, 2=2xx, 3=204, 4=3xx, 5=400, 6=4xx, 7=403, 8=404, 9=408, 10=413, 11=5xx, 12=502|504|511, 13=503, 14=505, 15=Other)" + }, "PLACES_PAGES_COUNT": { "expires_in_version": "never", "kind": "exponential", diff --git a/toolkit/components/url-classifier/nsUrlClassifierHashCompleter.js b/toolkit/components/url-classifier/nsUrlClassifierHashCompleter.js index a7900b84cf0..93be582d9d3 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierHashCompleter.js +++ b/toolkit/components/url-classifier/nsUrlClassifierHashCompleter.js @@ -47,6 +47,115 @@ function log(...stuff) { dump(msg + "\n"); } +// Map the HTTP response code to a Telemetry bucket +// https://developers.google.com/safe-browsing/developers_guide_v2?hl=en +function httpStatusToBucket(httpStatus) { + var statusBucket; + switch (httpStatus) { + case 100: + case 101: + // Unexpected 1xx return code + statusBucket = 0; + break; + case 200: + // OK - Data is available in the HTTP response body. + statusBucket = 1; + break; + case 201: + case 202: + case 203: + case 205: + case 206: + // Unexpected 2xx return code + statusBucket = 2; + break; + case 204: + // No Content - There are no full-length hashes with the requested prefix. + statusBucket = 3; + break; + case 300: + case 301: + case 302: + case 303: + case 304: + case 305: + case 307: + case 308: + // Unexpected 3xx return code + statusBucket = 4; + break; + case 400: + // Bad Request - The HTTP request was not correctly formed. + // The client did not provide all required CGI parameters. + statusBucket = 5; + break; + case 401: + case 402: + case 405: + case 406: + case 407: + case 409: + case 410: + case 411: + case 412: + case 414: + case 415: + case 416: + case 417: + case 421: + case 426: + case 428: + case 429: + case 431: + case 451: + // Unexpected 4xx return code + statusBucket = 6; + break; + case 403: + // Forbidden - The client id is invalid. + statusBucket = 7; + break; + case 404: + // Not Found + statusBucket = 8; + break; + case 408: + // Request Timeout + statusBucket = 9; + break; + case 413: + // Request Entity Too Large - Bug 1150334 + statusBucket = 10; + break; + case 500: + case 501: + case 510: + // Unexpected 5xx return code + statusBucket = 11; + break; + case 502: + case 504: + case 511: + // Local network errors, we'll ignore these. + statusBucket = 12; + break; + case 503: + // Service Unavailable - The server cannot handle the request. + // Clients MUST follow the backoff behavior specified in the + // Request Frequency section. + statusBucket = 13; + break; + case 505: + // HTTP Version Not Supported - The server CANNOT handle the requested + // protocol major version. + statusBucket = 14; + break; + default: + statusBucket = 15; + }; + return statusBucket; +} + function HashCompleter() { // The current HashCompleterRequest in flight. Once it is started, it is set // to null. It may be used by multiple calls to |complete| in succession to @@ -435,6 +544,10 @@ HashCompleterRequest.prototype = { } log('Received a ' + httpStatus + ' status code from the gethash server.'); + let histogram = + Services.telemetry.getHistogramById("URLCLASSIFIER_COMPLETE_REMOTE_STATUS"); + histogram.add(httpStatusToBucket(httpStatus)); + let success = Components.isSuccessCode(aStatusCode); // Notify the RequestBackoff once a response is received. this._completer.finishRequest(this.gethashUrl, httpStatus); diff --git a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp index a8ad3a3445f..431c2a3513f 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp @@ -20,6 +20,7 @@ #include "mozilla/Logging.h" #include "nsIInterfaceRequestor.h" #include "mozilla/LoadContext.h" +#include "mozilla/Telemetry.h" #include "nsContentUtils.h" static const char* gQuitApplicationMessage = "quit-application"; @@ -444,6 +445,114 @@ nsUrlClassifierStreamUpdater::AddRequestBody(const nsACString &aRequestBody) return NS_OK; } +// Map the HTTP response code to a Telemetry bucket +static uint32_t HTTPStatusToBucket(uint32_t status) +{ + uint32_t statusBucket; + switch (status) { + case 100: + case 101: + // Unexpected 1xx return code + statusBucket = 0; + break; + case 200: + // OK - Data is available in the HTTP response body. + statusBucket = 1; + break; + case 201: + case 202: + case 203: + case 205: + case 206: + // Unexpected 2xx return code + statusBucket = 2; + break; + case 204: + // No Content + statusBucket = 3; + break; + case 300: + case 301: + case 302: + case 303: + case 304: + case 305: + case 307: + case 308: + // Unexpected 3xx return code + statusBucket = 4; + break; + case 400: + // Bad Request - The HTTP request was not correctly formed. + // The client did not provide all required CGI parameters. + statusBucket = 5; + break; + case 401: + case 402: + case 405: + case 406: + case 407: + case 409: + case 410: + case 411: + case 412: + case 414: + case 415: + case 416: + case 417: + case 421: + case 426: + case 428: + case 429: + case 431: + case 451: + // Unexpected 4xx return code + statusBucket = 6; + break; + case 403: + // Forbidden - The client id is invalid. + statusBucket = 7; + break; + case 404: + // Not Found + statusBucket = 8; + break; + case 408: + // Request Timeout + statusBucket = 9; + break; + case 413: + // Request Entity Too Large - Bug 1150334 + statusBucket = 10; + break; + case 500: + case 501: + case 510: + // Unexpected 5xx return code + statusBucket = 11; + break; + case 502: + case 504: + case 511: + // Local network errors, we'll ignore these. + statusBucket = 12; + break; + case 503: + // Service Unavailable - The server cannot handle the request. + // Clients MUST follow the backoff behavior specified in the + // Request Frequency section. + statusBucket = 13; + break; + case 505: + // HTTP Version Not Supported - The server CANNOT handle the requested + // protocol major version. + statusBucket = 14; + break; + default: + statusBucket = 15; + }; + return statusBucket; +} /////////////////////////////////////////////////////////////////////////////// // nsIStreamListenerObserver implementation @@ -479,6 +588,9 @@ nsUrlClassifierStreamUpdater::OnStartRequest(nsIRequest *request, if (NS_FAILED(status)) { // Assume we're overloading the server and trigger backoff. downloadError = true; + mozilla::Telemetry::Accumulate(mozilla::Telemetry::URLCLASSIFIER_UPDATE_REMOTE_STATUS, + 15 /* unknown response code */); + } else { bool succeeded = false; rv = httpChannel->GetRequestSucceeded(&succeeded); @@ -487,6 +599,8 @@ nsUrlClassifierStreamUpdater::OnStartRequest(nsIRequest *request, uint32_t requestStatus; rv = httpChannel->GetResponseStatus(&requestStatus); NS_ENSURE_SUCCESS(rv, rv); + mozilla::Telemetry::Accumulate(mozilla::Telemetry::URLCLASSIFIER_UPDATE_REMOTE_STATUS, + HTTPStatusToBucket(requestStatus)); LOG(("nsUrlClassifierStreamUpdater::OnStartRequest %s (%d)", succeeded ? "succeeded" : "failed", requestStatus)); if (!succeeded) { diff --git a/toolkit/content/TopLevelVideoDocument.js b/toolkit/content/TopLevelVideoDocument.js index 33552fda2dc..5a2b8a857c4 100644 --- a/toolkit/content/TopLevelVideoDocument.js +++ b/toolkit/content/TopLevelVideoDocument.js @@ -14,9 +14,9 @@ document.addEventListener("keypress", ev => { if (ev.synthetic) // prevent recursion return; - // Maximize the video when pressing F11, - // because this is the standanlone video document. - if (ev.key == "F11") { + // Maximize the standalone video when pressing F11, + // but ignore audio elements + if (ev.key == "F11" && videoElement.videoWidth != 0 && videoElement.videoHeight != 0) { // If we're in browser fullscreen mode, it means the user pressed F11 // while browser chrome or another tab had focus. // Don't break leaving that mode, so do nothing here. diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index 27c158519c9..4ea3767e160 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -404,10 +404,18 @@ SafeInstallOperation.prototype = { let oldFile = aCopy ? null : aFile.clone(); let newFile = aFile.clone(); try { - if (aCopy) + if (aCopy) { newFile.copyTo(aTargetDirectory, null); - else + // copyTo does not update the nsIFile with the new. + newFile = aTargetDirectory.clone(); + newFile.append(aFile.leafName); + // Windows roaming profiles won't properly sync directories if a new file + // has an older lastModifiedTime than a previous file, so update. + newFile.lastModifiedTime = Date.now(); + } + else { newFile.moveTo(aTargetDirectory, null); + } } catch (e) { logger.error("Failed to " + (aCopy ? "copy" : "move") + " file " + aFile.path + @@ -5443,8 +5451,13 @@ AddonInstall.prototype = { } // No valid add-on was found, delete all the temporary files - for (let { file } of files) - file.remove(true); + for (let { file } of files) { + try { + file.remove(true); + } catch (e) { + this.logger.warn("Could not remove temp file " + file.path); + } + } return Promise.reject([AddonManager.ERROR_CORRUPT_FILE, "Multi-package XPI does not contain any valid packages to install"]); diff --git a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js index ba8bfb83351..3466e0c1304 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js +++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js @@ -21,6 +21,15 @@ const PREF_XPI_SIGNATURES_REQUIRED = "xpinstall.signatures.required"; // Forcibly end the test if it runs longer than 15 minutes const TIMEOUT_MS = 900000; +// Maximum error in file modification times. Some file systems don't store +// modification times exactly. As long as we are closer than this then it +// still passes. +const MAX_TIME_DIFFERENCE = 3000; + +// Time to reset file modified time relative to Date.now() so we can test that +// times are modified (10 hours old). +const MAKE_FILE_OLD_DIFFERENCE = 10 * 3600 * 1000; + Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm"); Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/FileUtils.jsm"); @@ -1233,6 +1242,7 @@ function writeInstallRDFToXPIFile(aData, aFile, aExtraFile) { var zipW = AM_Cc["@mozilla.org/zipwriter;1"]. createInstance(AM_Ci.nsIZipWriter); zipW.open(aFile, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE); + // Note these files are being created in the XPI archive with date "0" which is 1970-01-01. zipW.addEntryStream("install.rdf", 0, AM_Ci.nsIZipWriter.COMPRESSION_NONE, stream, false); if (aExtraFile) diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_distribution.js b/toolkit/mozapps/extensions/test/xpcshell/test_distribution.js index 9f5bfacca88..ab7cbc47793 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_distribution.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_distribution.js @@ -68,7 +68,7 @@ function setOldModificationTime() { extension.append("addon1@tests.mozilla.org"); else extension.append("addon1@tests.mozilla.org.xpi"); - setExtensionModifiedTime(extension, Date.now - 10000); + setExtensionModifiedTime(extension, Date.now() - MAKE_FILE_OLD_DIFFERENCE); startupManager(false); } @@ -78,9 +78,11 @@ function run_test() { run_test_1(); } -// Tests that on the first startup the add-on gets installed +// Tests that on the first startup the add-on gets installed, with now as the +// profile modifiedTime. function run_test_1() { - writeInstallRDFForExtension(addon1_1, distroDir); + let extension = writeInstallRDFForExtension(addon1_1, distroDir); + setExtensionModifiedTime(extension, Date.now() - MAKE_FILE_OLD_DIFFERENCE); startupManager(); @@ -91,6 +93,15 @@ function run_test_1() { do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE); do_check_false(a1.foreignInstall); + // Modification time should be updated when the addon is copied to the + // profile. + let testURI = a1.getResourceURI(TEST_UNPACKED ? "install.rdf" : ""); + let testFile = testURI.QueryInterface(Components.interfaces.nsIFileURL).file; + + do_check_true(testFile.exists()); + let difference = testFile.lastModifiedTime - Date.now(); + do_check_true(Math.abs(difference) < MAX_TIME_DIFFERENCE); + do_execute_soon(run_test_2); }); } diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_install.js b/toolkit/mozapps/extensions/test/xpcshell/test_install.js index e724b8e027e..8c064b0c88a 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_install.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_install.js @@ -7,11 +7,6 @@ var Cc = Components.classes; var Ci = Components.interfaces; var Cu = Components.utils; -// Maximum error in file modification times. Some file systems don't store -// modification times exactly. As long as we are closer than this then it -// still passes. -const MAX_TIME_DIFFERENCE = 3000; - // install.rdf size, icon.png, icon64.png size const ADDON1_SIZE = 705 + 16 + 16; @@ -145,6 +140,8 @@ function check_test_1(installSyncGUID) { else { let iconFile = uri.QueryInterface(AM_Ci.nsIFileURL).file; do_check_true(iconFile.exists()); + // Make the iconFile predictably old. + iconFile.lastModifiedTime = Date.now() - MAKE_FILE_OLD_DIFFERENCE; } // Make the pending install have a sensible date @@ -194,6 +191,14 @@ function check_test_1(installSyncGUID) { do_check_eq(a1.iconURL, uri + "icon.png"); do_check_eq(a1.icon64URL, uri + "icon64.png"); + // Ensure that extension bundle (or icon if unpacked) has updated + // lastModifiedDate. + let testURI = a1.getResourceURI(TEST_UNPACKED ? "icon.png" : ""); + let testFile = testURI.QueryInterface(Components.interfaces.nsIFileURL).file; + do_check_true(testFile.exists()); + difference = testFile.lastModifiedTime - Date.now(); + do_check_true(Math.abs(difference) < MAX_TIME_DIFFERENCE); + a1.uninstall(); let { id, version } = a1; restartManager(); diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_install_strictcompat.js b/toolkit/mozapps/extensions/test/xpcshell/test_install_strictcompat.js index 2b6a95ab435..5c23bb6547b 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_install_strictcompat.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_install_strictcompat.js @@ -7,11 +7,6 @@ var Cc = Components.classes; var Ci = Components.interfaces; var Cu = Components.utils; -// Maximum error in file modification times. Some file systems don't store -// modification times exactly. As long as we are closer than this then it -// still passes. -const MAX_TIME_DIFFERENCE = 3000; - // install.rdf size, icon.png, icon64.png size const ADDON1_SIZE = 705 + 16 + 16; diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_update.js b/toolkit/mozapps/extensions/test/xpcshell/test_update.js index 6ed135d00a5..71b6dba5298 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_update.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_update.js @@ -242,13 +242,18 @@ for (let test of testParams) { do_check_neq(a1.syncGUID, null); do_check_eq(originalSyncGUID, a1.syncGUID); + // Make sure that the extension lastModifiedTime was updated. + let testURI = a1.getResourceURI(TEST_UNPACKED ? "install.rdf" : ""); + let testFile = testURI.QueryInterface(Components.interfaces.nsIFileURL).file; + let difference = testFile.lastModifiedTime - Date.now(); + do_check_true(Math.abs(difference) < MAX_TIME_DIFFERENCE); + a1.uninstall(); run_next_test(); }); })); }; - // Check that an update check finds compatibility updates and applies them let check_test_3; add_test(function run_test_3() { diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 2cc8cda70e4..fde11038950 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -48,6 +48,8 @@ #include "SurfaceTexture.h" #include "GLContextProvider.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/UniquePtr.h" #include "mozilla/dom/ContentChild.h" using namespace mozilla; @@ -184,9 +186,10 @@ AndroidBridge::~AndroidBridge() } AndroidBridge::AndroidBridge() - : mLayerClient(nullptr), - mPresentationWindow(nullptr), - mPresentationSurface(nullptr) + : mLayerClient(nullptr) + , mUiTaskQueueLock("UiTaskQueue") + , mPresentationWindow(nullptr) + , mPresentationSurface(nullptr) { ALOG_BRIDGE("AndroidBridge::Init"); @@ -2047,24 +2050,76 @@ AndroidBridge::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, return progressiveUpdateData->Abort(); } +class AndroidBridge::DelayedTask +{ + using TimeStamp = mozilla::TimeStamp; + using TimeDuration = mozilla::TimeDuration; + +public: + DelayedTask(Task* aTask) + : mTask(aTask) + , mRunTime() // Null timestamp representing no delay. + {} + + DelayedTask(Task* aTask, int aDelayMs) + : mTask(aTask) + , mRunTime(TimeStamp::Now() + TimeDuration::FromMilliseconds(aDelayMs)) + {} + + bool IsEarlierThan(const DelayedTask& aOther) const + { + if (mRunTime) { + return aOther.mRunTime ? mRunTime < aOther.mRunTime : false; + } + // In the case of no delay, we're earlier if aOther has a delay. + // Otherwise, we're not earlier, to maintain task order. + return !!aOther.mRunTime; + } + + int64_t MillisecondsToRunTime() const + { + if (mRunTime) { + return int64_t((mRunTime - TimeStamp::Now()).ToMilliseconds()); + } + return 0; + } + + UniquePtr&& GetTask() + { + return static_cast&&>(mTask); + } + +private: + UniquePtr mTask; + const TimeStamp mRunTime; +}; + + void AndroidBridge::PostTaskToUiThread(Task* aTask, int aDelayMs) { - // add the new task into the mDelayedTaskQueue, sorted with + // add the new task into the mUiTaskQueue, sorted with // the earliest task first in the queue - DelayedTask* newTask = new DelayedTask(aTask, aDelayMs); - uint32_t i = 0; - while (i < mDelayedTaskQueue.Length()) { - if (newTask->IsEarlierThan(mDelayedTaskQueue[i])) { - mDelayedTaskQueue.InsertElementAt(i, newTask); - break; + size_t i; + DelayedTask newTask(aDelayMs ? DelayedTask(aTask, aDelayMs) + : DelayedTask(aTask)); + + { + MutexAutoLock lock(mUiTaskQueueLock); + + for (i = 0; i < mUiTaskQueue.Length(); i++) { + if (newTask.IsEarlierThan(mUiTaskQueue[i])) { + mUiTaskQueue.InsertElementAt(i, mozilla::Move(newTask)); + break; + } + } + + if (i == mUiTaskQueue.Length()) { + // We didn't insert the task, which means we should append it. + mUiTaskQueue.AppendElement(mozilla::Move(newTask)); } - i++; - } - if (i == mDelayedTaskQueue.Length()) { - // this new task will run after all the existing tasks in the queue - mDelayedTaskQueue.AppendElement(newTask); } + if (i == 0) { // if we're inserting it at the head of the queue, notify Java because // we need to get a callback at an earlier time than the last scheduled @@ -2076,9 +2131,10 @@ AndroidBridge::PostTaskToUiThread(Task* aTask, int aDelayMs) int64_t AndroidBridge::RunDelayedUiThreadTasks() { - while (mDelayedTaskQueue.Length() > 0) { - DelayedTask* nextTask = mDelayedTaskQueue[0]; - int64_t timeLeft = nextTask->MillisecondsToRunTime(); + MutexAutoLock lock(mUiTaskQueueLock); + + while (!mUiTaskQueue.IsEmpty()) { + const int64_t timeLeft = mUiTaskQueue[0].MillisecondsToRunTime(); if (timeLeft > 0) { // this task (and therefore all remaining tasks) // have not yet reached their runtime. return the @@ -2086,14 +2142,13 @@ AndroidBridge::RunDelayedUiThreadTasks() return timeLeft; } - // we have a delayed task to run. extract it from - // the wrapper and free the wrapper + // Retrieve task before unlocking/running. + const UniquePtr nextTask(mUiTaskQueue[0].GetTask()); + mUiTaskQueue.RemoveElementAt(0); - mDelayedTaskQueue.RemoveElementAt(0); - Task* task = nextTask->GetTask(); - delete nextTask; - - task->Run(); + // Unlock to allow posting new tasks reentrantly. + MutexAutoUnlock unlock(mUiTaskQueueLock); + nextTask->Run(); } return -1; } diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index fa546a514b2..675613f2c73 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -21,7 +21,6 @@ #include "nsIMIMEInfo.h" #include "nsColor.h" #include "gfxRect.h" -#include "mozilla/gfx/Point.h" #include "nsIAndroidBridge.h" #include "nsIMobileMessageCallback.h" @@ -29,9 +28,9 @@ #include "nsIDOMDOMCursor.h" #include "mozilla/Likely.h" -#include "mozilla/StaticPtr.h" -#include "mozilla/TimeStamp.h" +#include "mozilla/Mutex.h" #include "mozilla/Types.h" +#include "mozilla/gfx/Point.h" #include "mozilla/jni/Utils.h" // Some debug #defines @@ -76,31 +75,6 @@ typedef struct AndroidSystemColors { nscolor panelColorBackground; } AndroidSystemColors; -class DelayedTask { -public: - DelayedTask(Task* aTask, int aDelayMs) { - mTask = aTask; - mRunTime = mozilla::TimeStamp::Now() + mozilla::TimeDuration::FromMilliseconds(aDelayMs); - } - - bool IsEarlierThan(DelayedTask *aOther) { - return mRunTime < aOther->mRunTime; - } - - int64_t MillisecondsToRunTime() { - mozilla::TimeDuration timeLeft = mRunTime - mozilla::TimeStamp::Now(); - return (int64_t)timeLeft.ToMilliseconds(); - } - - Task* GetTask() { - return mTask; - } - -private: - Task* mTask; - mozilla::TimeStamp mRunTime; -}; - class ThreadCursorContinueCallback : public nsICursorContinueCallback { public: @@ -438,9 +412,10 @@ protected: void (* Region_set)(void* region, void* rect); private: - // This will always be accessed from one thread (the Java UI thread), - // so we don't need to do locking to touch it. - nsTArray mDelayedTaskQueue; + class DelayedTask; + nsTArray mUiTaskQueue; + mozilla::Mutex mUiTaskQueueLock; + public: void PostTaskToUiThread(Task* aTask, int aDelayMs); int64_t RunDelayedUiThreadTasks(); diff --git a/widget/gtk/mozgtk/mozgtk.c b/widget/gtk/mozgtk/mozgtk.c index 38133bc3313..137fa644471 100644 --- a/widget/gtk/mozgtk/mozgtk.c +++ b/widget/gtk/mozgtk/mozgtk.c @@ -389,6 +389,7 @@ STUB(gtk_settings_get_for_screen) STUB(gtk_socket_add_id) STUB(gtk_socket_get_id) STUB(gtk_socket_get_type) +STUB(gtk_socket_get_plug_window) STUB(gtk_socket_new) STUB(gtk_spin_button_new) STUB(gtk_statusbar_new) diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index d2e9cb3e42f..04e9c7a9f78 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -209,7 +209,7 @@ NoteWeakMapsTracer::trace(JSObject* aMap, JS::GCCellPtr aKey, mChildTracer.mKey = aKey; mChildTracer.mKeyDelegate = kdelegate; - if (aValue.is()) { + if (!aValue.is()) { JS::TraceChildren(&mChildTracer, aValue); } diff --git a/xpcom/base/nsTraceRefcnt.cpp b/xpcom/base/nsTraceRefcnt.cpp index 0e7d972febf..6d88323f4f9 100644 --- a/xpcom/base/nsTraceRefcnt.cpp +++ b/xpcom/base/nsTraceRefcnt.cpp @@ -470,14 +470,14 @@ DumpSerialNumbers(PLHashEntry* aHashEntry, int aIndex, void* aClosure) fprintf(outputFile, "%" PRIdPTR " @%p (%d references; %d from COMPtrs)\n", record->serialNumber, - NS_INT32_TO_PTR(aHashEntry->key), + aHashEntry->key, record->refCount, record->COMPtrCount); #else fprintf(outputFile, "%" PRIdPTR " @%p (%d references)\n", record->serialNumber, - NS_INT32_TO_PTR(aHashEntry->key), + aHashEntry->key, record->refCount); #endif #ifdef MOZ_STACKWALKING @@ -706,6 +706,7 @@ InitLog(const char* aEnvVar, const char* aMsg, FILE** aResult) } else { fprintf(stdout, "### %s defined -- unable to log %s to %s\n", aEnvVar, aMsg, fname.get()); + MOZ_ASSERT(false, "Tried and failed to create an XPCOM log"); } return stream != nullptr; }