merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2016-02-11 11:52:01 +01:00
commit d80c849001
226 changed files with 3209 additions and 1358 deletions

View File

@ -99,7 +99,7 @@
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Testing // Testing
//gA11yEventDumpToConsole = true; // debug gA11yEventDumpToConsole = true; // debug
var gQueue = null; var gQueue = null;
function doTests() function doTests()
@ -107,7 +107,7 @@
testRelation(browserDocument(), RELATION_EMBEDS, testRelation(browserDocument(), RELATION_EMBEDS,
getAccessible(currentTabDocument())); getAccessible(currentTabDocument()));
//enableLogging("docload"); enableLogging("docload");
gQueue = new eventQueue(); gQueue = new eventQueue();
gQueue.push(new loadURI("about:about")); gQueue.push(new loadURI("about:about"));
@ -115,7 +115,7 @@
gQueue.onFinish = function() gQueue.onFinish = function()
{ {
//disableLogging(); disableLogging();
closeBrowserWindow(); closeBrowserWindow();
} }
gQueue.invoke(); gQueue.invoke();

View File

@ -7,10 +7,11 @@
"unpack": true "unpack": true
}, },
{ {
"size": 12057960, "size": 12072532,
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh",
"unpack": true "unpack": true
} }
] ]

View File

@ -7,10 +7,11 @@
"unpack": true "unpack": true
}, },
{ {
"size": 12057960, "size": 12072532,
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh",
"unpack": true "unpack": true
} }
] ]

View File

@ -7,10 +7,11 @@
"unpack": true "unpack": true
}, },
{ {
"size": 12057960, "size": 12072532,
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh",
"unpack": true "unpack": true
} }
] ]

View File

@ -7,10 +7,11 @@
"unpack": true "unpack": true
}, },
{ {
"size": 12057960, "size": 12072532,
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh",
"unpack": true "unpack": true
} }
] ]

View File

@ -7,10 +7,11 @@
"unpack": true "unpack": true
}, },
{ {
"size": 12057960, "size": 12072532,
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh",
"unpack": true "unpack": true
} }
] ]

View File

@ -7,10 +7,11 @@
"unpack": true "unpack": true
}, },
{ {
"size": 12057960, "size": 12072532,
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh",
"unpack": true "unpack": true
} }
] ]

View File

@ -7,10 +7,11 @@
"unpack": true "unpack": true
}, },
{ {
"size": 12057960, "size": 12072532,
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh",
"unpack": true "unpack": true
} }
] ]

View File

@ -7,8 +7,8 @@
"unpack": true "unpack": true
}, },
{ {
"size": 11179576, "size": 11189216,
"digest": "91567ce8e2bb8ab0ebc60c31e90731d88a1ea889fb71bcf55c735746a60fa7610b7e040ea3d8f727b6f692ae3ee703d6f3b30cdbd76fdf5617f77d9c38aa20ed", "digest": "18bc52b0599b1308b667e282abb45f47597bfc98a5140cfcab8da71dacf89dd76d0dee22a04ce26fe7ad1f04e2d6596991f9e5b01fd2aaaab5542965f596b0e6",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh", "setup": "setup.sh",

View File

@ -7,8 +7,8 @@
"unpack": true "unpack": true
}, },
{ {
"size": 12057960, "size": 12072532,
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh", "setup": "setup.sh",

View File

@ -30,9 +30,6 @@ pref("network.predictor.enabled", true);
// No AccessibleCaret // No AccessibleCaret
pref("layout.accessiblecaret.enabled", false); 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. // To be removed once bug 942756 is fixed.
pref("devtools.debugger.unix-domain-socket", "6000"); pref("devtools.debugger.unix-domain-socket", "6000");

View File

@ -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.detailsURL", "https://www.mozilla.org/%LOCALE%/blocklist/");
pref("extensions.blocklist.itemURL", "https://blocklist.addons.mozilla.org/%LOCALE%/%APP%/blocked/%blockID%"); 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.update.autoUpdateDefault", true);
pref("extensions.hotfix.id", "firefox-hotfix@mozilla.org"); pref("extensions.hotfix.id", "firefox-hotfix@mozilla.org");

View File

@ -11,4 +11,5 @@ ac_add_options --enable-verify-mar
mk_add_options MOZ_PGO=1 mk_add_options MOZ_PGO=1
. "$topsrcdir/build/mozconfig.rust"
. "$topsrcdir/build/mozconfig.common.override" . "$topsrcdir/build/mozconfig.common.override"

View File

@ -18,4 +18,5 @@ mk_add_options MOZ_PGO=1
# defines.sh during the beta cycle # defines.sh during the beta cycle
export BUILDING_RELEASE=1 export BUILDING_RELEASE=1
. "$topsrcdir/build/mozconfig.rust"
. "$topsrcdir/build/mozconfig.common.override" . "$topsrcdir/build/mozconfig.common.override"

View File

@ -10,5 +10,6 @@ fi
ac_add_options --enable-official-branding ac_add_options --enable-official-branding
ac_add_options --enable-verify-mar ac_add_options --enable-verify-mar
. "$topsrcdir/build/mozconfig.rust"
. "$topsrcdir/build/mozconfig.common.override" . "$topsrcdir/build/mozconfig.common.override"
. "$topsrcdir/build/mozconfig.cache" . "$topsrcdir/build/mozconfig.cache"

View File

@ -16,5 +16,6 @@ ac_add_options --enable-verify-mar
# defines.sh during the beta cycle # defines.sh during the beta cycle
export BUILDING_RELEASE=1 export BUILDING_RELEASE=1
. "$topsrcdir/build/mozconfig.rust"
. "$topsrcdir/build/mozconfig.common.override" . "$topsrcdir/build/mozconfig.common.override"
. "$topsrcdir/build/mozconfig.cache" . "$topsrcdir/build/mozconfig.cache"

View File

@ -40,7 +40,6 @@ whitelist['nightly']['linux64'] += [
'STRIP_FLAGS="--strip-debug"', 'STRIP_FLAGS="--strip-debug"',
'ac_add_options --with-ccache=/usr/bin/ccache', 'ac_add_options --with-ccache=/usr/bin/ccache',
'. "$topsrcdir/build/mozconfig.cache"', '. "$topsrcdir/build/mozconfig.cache"',
'. "$topsrcdir/build/mozconfig.rust"',
] ]
whitelist['nightly']['macosx-universal'] += [ whitelist['nightly']['macosx-universal'] += [
@ -53,7 +52,6 @@ whitelist['nightly']['macosx-universal'] += [
'ac_add_options --disable-install-strip', 'ac_add_options --disable-install-strip',
'ac_add_options --enable-instruments', 'ac_add_options --enable-instruments',
'ac_add_options --enable-dtrace', 'ac_add_options --enable-dtrace',
'. "$topsrcdir/build/mozconfig.rust"',
] ]
whitelist['nightly']['win32'] += [ whitelist['nightly']['win32'] += [

View File

@ -7,10 +7,11 @@
"unpack": true "unpack": true
}, },
{ {
"size": 11179576, "size": 11189216,
"digest": "91567ce8e2bb8ab0ebc60c31e90731d88a1ea889fb71bcf55c735746a60fa7610b7e040ea3d8f727b6f692ae3ee703d6f3b30cdbd76fdf5617f77d9c38aa20ed", "digest": "18bc52b0599b1308b667e282abb45f47597bfc98a5140cfcab8da71dacf89dd76d0dee22a04ce26fe7ad1f04e2d6596991f9e5b01fd2aaaab5542965f596b0e6",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh",
"unpack": true "unpack": true
}, },
{ {

View File

@ -10,8 +10,8 @@
"unpack": true "unpack": true
}, },
{ {
"size": 12057960, "size": 12072532,
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh", "setup": "setup.sh",

View File

@ -10,10 +10,11 @@
"unpack": true "unpack": true
}, },
{ {
"size": 12057960, "size": 12072532,
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh",
"unpack": true "unpack": true
}, },
{ {

View File

@ -10,10 +10,11 @@
"unpack": true "unpack": true
}, },
{ {
"size": 12057960, "size": 12072532,
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh",
"unpack": true "unpack": true
} }
] ]

View File

@ -7,10 +7,11 @@
"unpack": true "unpack": true
}, },
{ {
"size": 12057960, "size": 12072532,
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh",
"unpack": true "unpack": true
}, },
{ {

View File

@ -10,10 +10,11 @@
"unpack": true "unpack": true
}, },
{ {
"size": 12057960, "size": 12072532,
"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e", "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
"algorithm": "sha512", "algorithm": "sha512",
"filename": "gtk3.tar.xz", "filename": "gtk3.tar.xz",
"setup": "setup.sh",
"unpack": true "unpack": true
} }
] ]

View File

@ -5,10 +5,13 @@
from __future__ import print_function, unicode_literals from __future__ import print_function, unicode_literals
import errno import errno
import json
import os import os
import platform import platform
import random
import sys import sys
import time import time
import uuid
import __builtin__ import __builtin__
from types import ModuleType from types import ModuleType
@ -67,6 +70,7 @@ SEARCH_PATHS = [
'config', 'config',
'dom/bindings', 'dom/bindings',
'dom/bindings/parser', 'dom/bindings/parser',
'dom/media/test/external',
'layout/tools/reftest', 'layout/tools/reftest',
'other-licenses/ply', 'other-licenses/ply',
'testing', 'testing',
@ -109,6 +113,7 @@ MACH_MODULES = [
'addon-sdk/mach_commands.py', 'addon-sdk/mach_commands.py',
'build/valgrind/mach_commands.py', 'build/valgrind/mach_commands.py',
'dom/bindings/mach_commands.py', 'dom/bindings/mach_commands.py',
'dom/media/test/external/mach_commands.py',
'layout/tools/reftest/mach_commands.py', 'layout/tools/reftest/mach_commands.py',
'python/mach_commands.py', 'python/mach_commands.py',
'python/mach/mach/commands/commandinfo.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(): def get_state_dir():
"""Obtain the path to a directory to hold state. """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] sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS]
import mach.main 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): def pre_dispatch_handler(context, handler, args):
"""Perform global checks before command dispatch. """Perform global checks before command dispatch.
@ -230,13 +284,7 @@ def bootstrap(topsrcdir, mozilla_dir=None):
tools are up to date. tools are up to date.
""" """
# Don't do anything when... # Don't do anything when...
if should_skip_dispatch(context, handler):
# 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:
return return
# User has disabled first run check. # User has disabled first run check.
@ -245,10 +293,6 @@ def bootstrap(topsrcdir, mozilla_dir=None):
if 'NO_MERCURIAL_SETUP_CHECK' in os.environ: if 'NO_MERCURIAL_SETUP_CHECK' in os.environ:
return 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. # Mercurial isn't managing this source checkout.
if not os.path.exists(os.path.join(topsrcdir, '.hg')): if not os.path.exists(os.path.join(topsrcdir, '.hg')):
return return
@ -269,6 +313,68 @@ def bootstrap(topsrcdir, mozilla_dir=None):
print(NO_MERCURIAL_SETUP.format(mach=sys.argv[0]), file=sys.stderr) print(NO_MERCURIAL_SETUP.format(mach=sys.argv[0]), file=sys.stderr)
sys.exit(2) 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): def populate_context(context, key=None):
if key is None: if key is None:
return return
@ -305,6 +411,12 @@ def bootstrap(topsrcdir, mozilla_dir=None):
if key == 'pre_dispatch_handler': if key == 'pre_dispatch_handler':
return 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) raise AttributeError(key)
mach = mach.main.Mach(os.getcwd()) mach = mach.main.Mach(os.getcwd())

View File

@ -13,7 +13,7 @@ import subprocess
import sys import sys
from automation import Automation from automation import Automation
from devicemanager import DMError, DeviceManager from mozdevice import DMError, DeviceManager
from mozlog import get_default_logger from mozlog import get_default_logger
import mozcrash import mozcrash

View File

@ -118,18 +118,20 @@ cat <<EOF > $root_dir/gtk3/setup.sh
#!/bin/sh #!/bin/sh
cd \$(dirname \$0) cd \$(dirname \$0)
HERE=\$(pwd)
# pango expects absolute paths in pango.modules, and TOOLTOOL_DIR may vary... # pango expects absolute paths in pango.modules, and TOOLTOOL_DIR may vary...
LD_LIBRARY_PATH=./usr/local/lib \ LD_LIBRARY_PATH=\$HERE/usr/local/lib \
PANGO_SYSCONFDIR=./usr/local/etc \ PANGO_SYSCONFDIR=\$HERE/usr/local/etc \
PANGO_LIBDIR=./usr/local/lib \ PANGO_LIBDIR=\$HERE/usr/local/lib \
./usr/local/bin/pango-querymodules > ./usr/local/etc/pango/pango.modules \$HERE/usr/local/bin/pango-querymodules > \$HERE/usr/local/etc/pango/pango.modules
# same with gdb-pixbuf and loaders.cache # same with gdb-pixbuf and loaders.cache
LD_LIBRARY_PATH=./usr/local/lib \ LD_LIBRARY_PATH=\$HERE/usr/local/lib \
GDK_PIXBUF_MODULE_FILE=./usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache \ GDK_PIXBUF_MODULE_FILE=\$HERE/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 \ GDK_PIXBUF_MODULEDIR=\$HERE/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 \$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 # The fontconfig version in the tooltool package has known uses of
# uninitialized memory when creating its cache, and while most users # 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 # will create it. Combined with valgrind, this generates irrelevant
# errors. # errors.
# So create the fontconfig cache beforehand. # 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 EOF
chmod +x $root_dir/gtk3/setup.sh chmod +x $root_dir/gtk3/setup.sh

View File

@ -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_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 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" 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

View File

@ -41,6 +41,8 @@ reAfterArg = "(?=[,)])"
reMatchArg = re.compile(reBeforeArg + reArgType + reArgName + reArgDefault + reAfterArg) reMatchArg = re.compile(reBeforeArg + reArgType + reArgName + reArgDefault + reAfterArg)
def get_normalized_signatures(signature, fileAnnot = None): def get_normalized_signatures(signature, fileAnnot = None):
# Remove static
signature = signature.replace('static', '')
# Remove semicolon. # Remove semicolon.
signature = signature.replace(';', ' ') signature = signature.replace(';', ' ')
# Normalize spaces. # Normalize spaces.

View File

@ -226,7 +226,7 @@ this.AppsUtils = {
deferred.resolve(file); deferred.resolve(file);
} }
}); });
aRequestChannel.asyncOpen(listener, null); aRequestChannel.asyncOpen2(listener);
return deferred.promise; return deferred.promise;
}, },

View File

@ -3490,21 +3490,15 @@ this.DOMApplicationRegistry = {
let requestChannel; let requestChannel;
let appURI = NetUtil.newURI(aNewApp.origin, null, null); let appURI = NetUtil.newURI(aNewApp.origin, null, null);
let principal =
Services.scriptSecurityManager.createCodebasePrincipal(appURI,
{appId: aNewApp.localId});
if (aIsLocalFileInstall) { if (aIsLocalFileInstall) {
requestChannel = NetUtil.newChannel({ requestChannel = NetUtil.newChannel({
uri: aFullPackagePath, uri: aFullPackagePath,
loadingPrincipal: principal, loadUsingSystemPrincipal: true}
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER}
).QueryInterface(Ci.nsIFileChannel); ).QueryInterface(Ci.nsIFileChannel);
} else { } else {
requestChannel = NetUtil.newChannel({ requestChannel = NetUtil.newChannel({
uri: aFullPackagePath, uri: aFullPackagePath,
loadingPrincipal: principal, loadUsingSystemPrincipal: true}
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER}
).QueryInterface(Ci.nsIHttpChannel); ).QueryInterface(Ci.nsIHttpChannel);
requestChannel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING; requestChannel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
} }

View File

@ -219,7 +219,7 @@ ShadowRoot::RemoveFromIdTable(Element* aElement, nsIAtom* aId)
if (entry) { if (entry) {
entry->RemoveIdElement(aElement); entry->RemoveIdElement(aElement);
if (entry->IsEmpty()) { if (entry->IsEmpty()) {
mIdentifierMap.RawRemoveEntry(entry); mIdentifierMap.RemoveEntry(entry);
} }
} }
} }

View File

@ -7270,13 +7270,13 @@ nsContentUtils::GetInnerWindowID(nsIRequest* aRequest)
return inner ? inner->WindowID() : 0; return inner ? inner->WindowID() : 0;
} }
void nsresult
nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI, nsCString& aHost) nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI, nsCString& aHost)
{ {
aHost.Truncate(); aHost.Truncate();
nsresult rv = aURI->GetHost(aHost); nsresult rv = aURI->GetHost(aHost);
if (NS_FAILED(rv)) { // Some URIs do not have a host if (NS_FAILED(rv)) { // Some URIs do not have a host
return; return rv;
} }
if (aHost.FindChar(':') != -1) { // Escape IPv6 address if (aHost.FindChar(':') != -1) { // Escape IPv6 address
@ -7285,14 +7285,20 @@ nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI, nsCString& aHost)
aHost.Insert('[', 0); aHost.Insert('[', 0);
aHost.Append(']'); aHost.Append(']');
} }
return NS_OK;
} }
void nsresult
nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI, nsAString& aHost) nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI, nsAString& aHost)
{ {
nsAutoCString hostname; nsAutoCString hostname;
GetHostOrIPv6WithBrackets(aURI, hostname); nsresult rv = GetHostOrIPv6WithBrackets(aURI, hostname);
if (NS_FAILED(rv)) {
return rv;
}
CopyUTF8toUTF16(hostname, aHost); CopyUTF8toUTF16(hostname, aHost);
return NS_OK;
} }
void void

View File

@ -2402,8 +2402,8 @@ public:
* If the hostname for aURI is an IPv6 it encloses it in brackets, * If the hostname for aURI is an IPv6 it encloses it in brackets,
* otherwise it just outputs the hostname in aHost. * otherwise it just outputs the hostname in aHost.
*/ */
static void GetHostOrIPv6WithBrackets(nsIURI* aURI, nsAString& aHost); static nsresult GetHostOrIPv6WithBrackets(nsIURI* aURI, nsAString& aHost);
static void GetHostOrIPv6WithBrackets(nsIURI* aURI, nsCString& aHost); static nsresult GetHostOrIPv6WithBrackets(nsIURI* aURI, nsCString& aHost);
/* /*
* Call the given callback on all remote children of the given top-level * Call the given callback on all remote children of the given top-level

View File

@ -3003,7 +3003,7 @@ nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId)
++mExpandoAndGeneration.generation; ++mExpandoAndGeneration.generation;
} }
if (entry->IsEmpty()) { if (entry->IsEmpty()) {
mIdentifierMap.RawRemoveEntry(entry); mIdentifierMap.RemoveEntry(entry);
} }
} }

View File

@ -1438,7 +1438,7 @@ nsHTMLCopyEncoder::SetSelection(nsISelection* aSelection)
mIsTextWidget = true; mIsTextWidget = true;
break; break;
} }
#ifdef MOZ_THUNDERBIRD #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
else if (selContent->IsHTMLElement(nsGkAtoms::body)) { else if (selContent->IsHTMLElement(nsGkAtoms::body)) {
// Currently, setting mIsTextWidget to 'true' will result in the selection // Currently, setting mIsTextWidget to 'true' will result in the selection
// being encoded/copied as pre-formatted plain text. // being encoded/copied as pre-formatted plain text.

View File

@ -104,7 +104,6 @@ GK_ATOM(applet, "applet")
GK_ATOM(applyImports, "apply-imports") GK_ATOM(applyImports, "apply-imports")
GK_ATOM(applyTemplates, "apply-templates") GK_ATOM(applyTemplates, "apply-templates")
GK_ATOM(mozapptype, "mozapptype") GK_ATOM(mozapptype, "mozapptype")
GK_ATOM(apz, "apz")
GK_ATOM(archive, "archive") GK_ATOM(archive, "archive")
GK_ATOM(area, "area") GK_ATOM(area, "area")
GK_ATOM(arrow, "arrow") GK_ATOM(arrow, "arrow")
@ -2270,13 +2269,20 @@ GK_ATOM(SendMail, "SendMail")
GK_ATOM(ForwardMail, "ForwardMail") GK_ATOM(ForwardMail, "ForwardMail")
GK_ATOM(ReplyToMail, "ReplyToMail") 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.<origin>.*" 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(mouseWheel, "mouseWheel") // For discrete wheel events (e.g. not OSX magic mouse)
GK_ATOM(pixels, "pixels") GK_ATOM(pixels, "pixels")
GK_ATOM(lines, "lines") GK_ATOM(lines, "lines")
GK_ATOM(pages, "pages") GK_ATOM(pages, "pages")
GK_ATOM(scrollbars, "scrollbars") GK_ATOM(scrollbars, "scrollbars")
GK_ATOM(other, "other") GK_ATOM(other, "other")
// Scroll origins without smooth-scrolling prefs
GK_ATOM(apz, "apz")
GK_ATOM(restore, "restore")
#ifdef ACCESSIBILITY #ifdef ACCESSIBILITY
GK_ATOM(alert, "alert") GK_ATOM(alert, "alert")

View File

@ -40,7 +40,7 @@ protected:
JS::MutableHandle<JS::Value> aVal); JS::MutableHandle<JS::Value> aVal);
bool StringifyToJSON(JSContext* aCx, bool StringifyToJSON(JSContext* aCx,
JS::MutableHandle<JS::Value> aValue, JS::Handle<JSObject*> aObj,
nsAString& aJSON) const; nsAString& aJSON) const;
// Struct used as a way to force a dictionary constructor to not init the // Struct used as a way to force a dictionary constructor to not init the

View File

@ -45,8 +45,9 @@
#include "mozilla/dom/HTMLAppletElementBinding.h" #include "mozilla/dom/HTMLAppletElementBinding.h"
#include "mozilla/dom/Promise.h" #include "mozilla/dom/Promise.h"
#include "mozilla/dom/ResolveSystemBinding.h" #include "mozilla/dom/ResolveSystemBinding.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "WorkerPrivate.h"
#include "nsDOMClassInfo.h" #include "nsDOMClassInfo.h"
#include "ipc/ErrorIPCUtils.h" #include "ipc/ErrorIPCUtils.h"
#include "mozilla/UseCounter.h" #include "mozilla/UseCounter.h"
@ -1867,11 +1868,10 @@ DictionaryBase::ParseJSON(JSContext* aCx,
bool bool
DictionaryBase::StringifyToJSON(JSContext* aCx, DictionaryBase::StringifyToJSON(JSContext* aCx,
JS::MutableHandle<JS::Value> aValue, JS::Handle<JSObject*> aObj,
nsAString& aJSON) const nsAString& aJSON) const
{ {
return JS_Stringify(aCx, aValue, nullptr, JS::NullHandleValue, return JS::ToJSONMaybeSafely(aCx, aObj, AppendJSONToString, &aJSON);
AppendJSONToString, &aJSON);
} }
/* static */ /* 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 dom
} // namespace mozilla } // namespace mozilla

View File

@ -3327,6 +3327,26 @@ DeprecationWarning(JSContext* aCx, JSObject* aObject,
JSString* JSString*
InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject, InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
unsigned /* indent */); 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 dom
} // namespace mozilla } // namespace mozilla

View File

@ -12257,13 +12257,21 @@ class CGDictionary(CGThing):
"ToJSON", "bool", "ToJSON", "bool",
[Argument('nsAString&', 'aJSON')], [Argument('nsAString&', 'aJSON')],
body=dedent(""" body=dedent("""
MOZ_ASSERT(NS_IsMainThread());
AutoJSAPI jsapi; AutoJSAPI jsapi;
jsapi.Init(); jsapi.Init();
JSContext *cx = jsapi.cx(); JSContext *cx = jsapi.cx();
JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope()); // Usage approved by bholley // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here
JS::Rooted<JS::Value> obj(cx); // because we'll only be creating objects, in ways that have no
return ToObjectInternal(cx, &obj) && StringifyToJSON(cx, &obj, aJSON); // 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<JS::Value> val(cx);
if (!ToObjectInternal(cx, &val)) {
return false;
}
JS::Rooted<JSObject*> obj(cx, &val.toObject());
return StringifyToJSON(cx, obj, aJSON);
"""), const=True) """), const=True)
def toObjectInternalMethod(self): def toObjectInternalMethod(self):
@ -12403,7 +12411,8 @@ class CGDictionary(CGThing):
methods.append(self.initFromJSONMethod()) methods.append(self.initFromJSONMethod())
try: try:
methods.append(self.toObjectInternalMethod()) methods.append(self.toObjectInternalMethod())
methods.append(self.toJSONMethod()) if self.dictionarySafeToJSONify(self.dictionary):
methods.append(self.toJSONMethod())
except MethodNotNewObjectError: except MethodNotNewObjectError:
# If we can't have a ToObjectInternal() because one of our members # If we can't have a ToObjectInternal() because one of our members
# can only be returned from [NewObject] methods, then just skip # can only be returned from [NewObject] methods, then just skip
@ -12710,6 +12719,52 @@ class CGDictionary(CGThing):
return False return False
return all(isTypeCopyConstructible(m.type) for m in dictionary.members) 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): class CGRegisterWorkerBindings(CGAbstractMethod):
def __init__(self, config): def __init__(self, config):
@ -13191,7 +13246,6 @@ class CGBindingRoot(CGThing):
bindingHeaders["WrapperFactory.h"] = descriptors bindingHeaders["WrapperFactory.h"] = descriptors
bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors
bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries # AutoJSAPI 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 # Ensure we see our enums in the generated .cpp file, for the ToJSValue
# method body. Also ensure that we see jsapi.h. # method body. Also ensure that we see jsapi.h.
if enums: if enums:
@ -15637,7 +15691,10 @@ class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember):
jsapi.Init(); jsapi.Init();
jsapi.TakeOwnershipOfErrorReporting(); jsapi.TakeOwnershipOfErrorReporting();
JSContext* cx = jsapi.cx(); 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<JS::Value> v(cx); JS::Rooted<JS::Value> v(cx);
if(!ToJSValue(cx, self, &v)) { if(!ToJSValue(cx, self, &v)) {
aRv.Throw(NS_ERROR_UNEXPECTED); aRv.Throw(NS_ERROR_UNEXPECTED);

View File

@ -15,6 +15,7 @@ var Cr = Components.results;
*/ */
Cu.import("resource://gre/modules/Services.jsm"); 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/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/BrowserElementPromptService.jsm"); Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
@ -1001,36 +1002,17 @@ BrowserElementParent.prototype = {
Ci.nsIRequestObserver]) Ci.nsIRequestObserver])
}; };
// If we have a URI we'll use it to get the triggering principal to use, let referrer = Services.io.newURI(_options.referrer, null, null);
// if not available a null principal is acceptable. let principal =
let referrer = null; Services.scriptSecurityManager.createCodebasePrincipal(
let principal = null; referrer, this._frameLoader.loadContext.originAttributes);
if (_options.referrer) {
// newURI can throw on malformed URIs.
try {
referrer = Services.io.newURI(_options.referrer, null, null);
}
catch(e) {
debug('Malformed referrer -- ' + e);
}
// This simply returns null if there is no principal available let channel = NetUtil.newChannel({
// for the requested uri. This is an acceptable fallback when uri: url,
// calling newChannelFromURI2. loadingPrincipal: principal,
principal = securityFlags: SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS,
Services.scriptSecurityManager.createCodebasePrincipal( contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
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);
// XXX We would set private browsing information prior to calling this. // XXX We would set private browsing information prior to calling this.
channel.notificationCallbacks = interfaceRequestor; channel.notificationCallbacks = interfaceRequestor;
@ -1055,7 +1037,7 @@ BrowserElementParent.prototype = {
} }
// Set-up complete, let's get things started. // Set-up complete, let's get things started.
channel.asyncOpen(new DownloadListener(), null); channel.asyncOpen2(new DownloadListener());
return req; return req;
}, },

View File

@ -48,6 +48,7 @@
#include "nsPIDOMWindow.h" #include "nsPIDOMWindow.h"
#include "nsSandboxFlags.h" #include "nsSandboxFlags.h"
#include "xpcpublic.h" #include "xpcpublic.h"
#include "nsIFrame.h"
namespace mozilla { namespace mozilla {
@ -416,10 +417,7 @@ EventListenerManager::AddEventListenerInternal(
} }
if (IsApzAwareEvent(aTypeAtom)) { if (IsApzAwareEvent(aTypeAtom)) {
nsCOMPtr<nsINode> node = do_QueryInterface(mTarget); ProcessApzAwareEventListenerAdd();
if (node) {
node->SetMayHaveApzAwareListeners();
}
} }
if (aTypeAtom && mTarget) { if (aTypeAtom && mTarget) {
@ -432,6 +430,44 @@ EventListenerManager::AddEventListenerInternal(
} }
} }
void
EventListenerManager::ProcessApzAwareEventListenerAdd()
{
// Mark the node as having apz aware listeners
nsCOMPtr<nsINode> 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<nsPIDOMWindowInner> window = GetTargetAsInnerWindow()) {
doc = window->GetExtantDoc();
}
}
if (!doc) {
if (nsCOMPtr<DOMEventTargetHelper> 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 bool
EventListenerManager::IsDeviceType(EventMessage aEventMessage) EventListenerManager::IsDeviceType(EventMessage aEventMessage)
{ {

View File

@ -467,6 +467,8 @@ protected:
nsIDocShell* GetDocShellForTarget(); nsIDocShell* GetDocShellForTarget();
void ProcessApzAwareEventListenerAdd();
/** /**
* Compile the "inline" event listener for aListener. The * Compile the "inline" event listener for aListener. The
* body of the listener can be provided in aBody; if this is null we * body of the listener can be provided in aBody; if this is null we

View File

@ -187,7 +187,7 @@ function mpTestModernBeatsLegacy(eventInfo) {
// Test that an event which bubbles may fire listeners of different flavors // Test that an event which bubbles may fire listeners of different flavors
// (modern vs. legacy) at each bubbling level, depending on what's registered // (modern vs. legacy) at each bubbling level, depending on what's registered
// at that level. // at that level.
function mpTestAncestorsWithDiffListeners(eventInfo) { function mpTestDiffListenersEventBubbling(eventInfo) {
return new Promise( return new Promise(
function(resolve, reject) { function(resolve, reject) {
var grandparent = createChildDiv(); var grandparent = createChildDiv();
@ -198,35 +198,81 @@ function mpTestAncestorsWithDiffListeners(eventInfo) {
var eventSentToTarget; var eventSentToTarget;
target.addEventListener(eventInfo.modern_name, target.addEventListener(eventInfo.modern_name,
createHandlerWithTypeCheck(eventInfo.modern_name, createHandlerWithTypeCheck(eventInfo.modern_name, function(e) {
function(e) { ok(e.bubbles, "Expecting event to bubble");
ok(e.bubbles, "Expecting event to bubble"); eventSentToTarget = e;
eventSentToTarget = e; didEventFireOnTarget = true;
didEventFireOnTarget = true; }));
}));
parent.addEventListener(eventInfo.legacy_name, parent.addEventListener(eventInfo.legacy_name,
createHandlerWithTypeCheck(eventInfo.legacy_name, createHandlerWithTypeCheck(eventInfo.legacy_name, function(e) {
function(e) { is(e, eventSentToTarget,
is(e, eventSentToTarget, "Same event object should bubble, despite difference in type");
"Same event object should bubble, " + didEventFireOnParent = true;
"despite difference in type"); }));
didEventFireOnParent = true;
}));
grandparent.addEventListener(eventInfo.modern_name, grandparent.addEventListener(eventInfo.modern_name,
createHandlerWithTypeCheck(eventInfo.modern_name, createHandlerWithTypeCheck(eventInfo.modern_name, function(e) {
function(e) { ok(didEventFireOnTarget,
ok(didEventFireOnTarget, "Event should have fired on child");
"Event should have fired on child"); ok(didEventFireOnParent,
ok(didEventFireOnParent, "Event should have fired on parent");
"Event should have fired on parent"); is(e, eventSentToTarget,
is(e, eventSentToTarget, "Same event object should bubble, despite difference in type");
"Same event object should bubble, " + // Clean up.
"despite difference in type"); grandparent.parentNode.removeChild(grandparent);
parent.removeChild(target); resolve();
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); eventInfo.trigger_event(target);
} }
); );
@ -239,7 +285,9 @@ function main() {
}).then(function() { }).then(function() {
return Promise.all(gLegacyEventInfo.map(mpTestModernBeatsLegacy)); return Promise.all(gLegacyEventInfo.map(mpTestModernBeatsLegacy));
}).then(function() { }).then(function() {
return Promise.all(gLegacyEventInfo.map(mpTestAncestorsWithDiffListeners)); return Promise.all(gLegacyEventInfo.map(mpTestDiffListenersEventCapturing));
}).then(function() {
return Promise.all(gLegacyEventInfo.map(mpTestDiffListenersEventBubbling));
}).then(function() { }).then(function() {
SimpleTest.finish(); SimpleTest.finish();
}).catch(function(reason) { }).catch(function(reason) {

View File

@ -4,6 +4,7 @@
<title>Test for MozMousePixelScroll events</title> <title>Test for MozMousePixelScroll events</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style> <style>
.scrollable { .scrollable {
@ -63,6 +64,8 @@ function* prepareScrollUnits()
} }
window.addEventListener("MozMousePixelScroll", handler, true); window.addEventListener("MozMousePixelScroll", handler, true);
yield waitForAllPaints(function () { setTimeout(runTest, 0); });
yield synthesizeWheel(gScrollable128, 10, 10, yield synthesizeWheel(gScrollable128, 10, 10,
{ deltaMode: WheelEvent.DOM_DELTA_LINE, { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaY: 1.0, lineOrPageDeltaY: 1 }); deltaY: 1.0, lineOrPageDeltaY: 1 });

View File

@ -24,7 +24,7 @@ NS_NewHTMLDetailsElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
bool /* static */ bool
HTMLDetailsElement::IsDetailsEnabled() HTMLDetailsElement::IsDetailsEnabled()
{ {
static bool isDetailsEnabled = false; static bool isDetailsEnabled = false;

View File

@ -891,8 +891,8 @@ nsHTMLDocument::GetDomain(nsAString& aDomain)
} }
nsAutoCString hostName; nsAutoCString hostName;
nsresult rv = nsContentUtils::GetHostOrIPv6WithBrackets(uri, hostName);
if (NS_SUCCEEDED(uri->GetHost(hostName))) { if (NS_SUCCEEDED(rv)) {
CopyUTF8toUTF16(hostName, aDomain); CopyUTF8toUTF16(hostName, aDomain);
} else { } else {
// If we can't get the host from the URI (e.g. about:, javascript:, // If we can't get the host from the URI (e.g. about:, javascript:,

View File

@ -8566,6 +8566,16 @@ private:
NS_DECL_NSIRUNNABLE NS_DECL_NSIRUNNABLE
}; };
class FlushPendingFileDeletionsRunnable final
: public nsRunnable
{
private:
~FlushPendingFileDeletionsRunnable()
{ }
NS_DECL_NSIRUNNABLE
};
class PermissionRequestHelper final class PermissionRequestHelper final
: public PermissionRequestBase : public PermissionRequestBase
, public PIndexedDBPermissionRequestParent , public PIndexedDBPermissionRequestParent
@ -9568,6 +9578,19 @@ DeallocPBackgroundIndexedDBUtilsParent(PBackgroundIndexedDBUtilsParent* aActor)
return true; return true;
} }
bool
RecvFlushPendingFileDeletions()
{
AssertIsOnBackgroundThread();
RefPtr<FlushPendingFileDeletionsRunnable> runnable =
new FlushPendingFileDeletionsRunnable();
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
return true;
}
PIndexedDBPermissionRequestParent* PIndexedDBPermissionRequestParent*
AllocPIndexedDBPermissionRequestParent(Element* aOwnerElement, AllocPIndexedDBPermissionRequestParent(Element* aOwnerElement,
nsIPrincipal* aPrincipal) nsIPrincipal* aPrincipal)
@ -27062,6 +27085,24 @@ GetFileReferencesHelper::Run()
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
FlushPendingFileDeletionsRunnable::Run()
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::Get();
if (NS_WARN_IF(!mgr)) {
return NS_ERROR_FAILURE;
}
nsresult rv = mgr->FlushPendingFileDeletions();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
void void
PermissionRequestHelper::OnPromptComplete(PermissionValue aPermissionValue) PermissionRequestHelper::OnPromptComplete(PermissionValue aPermissionValue)
{ {

View File

@ -45,6 +45,9 @@ AllocPBackgroundIndexedDBUtilsParent();
bool bool
DeallocPBackgroundIndexedDBUtilsParent(PBackgroundIndexedDBUtilsParent* aActor); DeallocPBackgroundIndexedDBUtilsParent(PBackgroundIndexedDBUtilsParent* aActor);
bool
RecvFlushPendingFileDeletions();
PIndexedDBPermissionRequestParent* PIndexedDBPermissionRequestParent*
AllocPIndexedDBPermissionRequestParent(Element* aOwnerElement, AllocPIndexedDBPermissionRequestParent(Element* aOwnerElement,
nsIPrincipal* aPrincipal); nsIPrincipal* aPrincipal);

View File

@ -22,7 +22,6 @@
#include "mozilla/EventDispatcher.h" #include "mozilla/EventDispatcher.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/Services.h" #include "mozilla/Services.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/DOMError.h" #include "mozilla/dom/DOMError.h"
#include "mozilla/dom/ErrorEvent.h" #include "mozilla/dom/ErrorEvent.h"
#include "mozilla/dom/ErrorEventBinding.h" #include "mozilla/dom/ErrorEventBinding.h"
@ -934,12 +933,12 @@ IndexedDatabaseManager::FlushPendingFileDeletions()
return rv; return rv;
} }
} else { } else {
ContentChild* contentChild = ContentChild::GetSingleton(); PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread();
if (NS_WARN_IF(!contentChild)) { if (NS_WARN_IF(!bgActor)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
if (!contentChild->SendFlushPendingFileDeletions()) { if (!bgActor->SendFlushPendingFileDeletions()) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
} }

View File

@ -182,7 +182,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_file_delete.html] [test_file_delete.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_file_os_delete.html] [test_file_os_delete.html]
skip-if = e10s || (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116, Bug 1183959 for e10s skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_file_put_get_object.html] [test_file_put_get_object.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_file_put_get_values.html] [test_file_put_get_values.html]

View File

@ -64,7 +64,6 @@
#include "mozilla/dom/cellbroadcast/CellBroadcastParent.h" #include "mozilla/dom/cellbroadcast/CellBroadcastParent.h"
#include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h" #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
#include "mozilla/dom/icc/IccParent.h" #include "mozilla/dom/icc/IccParent.h"
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
#include "mozilla/dom/mobileconnection/MobileConnectionParent.h" #include "mozilla/dom/mobileconnection/MobileConnectionParent.h"
#include "mozilla/dom/mobilemessage/SmsParent.h" #include "mozilla/dom/mobilemessage/SmsParent.h"
#include "mozilla/dom/power/PowerManagerService.h" #include "mozilla/dom/power/PowerManagerService.h"
@ -291,7 +290,6 @@ using namespace mozilla::dom::bluetooth;
using namespace mozilla::dom::cellbroadcast; using namespace mozilla::dom::cellbroadcast;
using namespace mozilla::dom::devicestorage; using namespace mozilla::dom::devicestorage;
using namespace mozilla::dom::icc; using namespace mozilla::dom::icc;
using namespace mozilla::dom::indexedDB;
using namespace mozilla::dom::power; using namespace mozilla::dom::power;
using namespace mozilla::dom::mobileconnection; using namespace mozilla::dom::mobileconnection;
using namespace mozilla::dom::mobilemessage; using namespace mozilla::dom::mobilemessage;
@ -5109,26 +5107,6 @@ ContentParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
return true; return true;
} }
bool
ContentParent::RecvFlushPendingFileDeletions()
{
RefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::Get();
if (NS_WARN_IF(!mgr)) {
return false;
}
if (NS_WARN_IF(!mgr->IsMainProcess())) {
return false;
}
nsresult rv = mgr->FlushPendingFileDeletions();
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
return true;
}
bool bool
ContentParent::IgnoreIPCPrincipal() ContentParent::IgnoreIPCPrincipal()
{ {

View File

@ -1045,9 +1045,6 @@ private:
virtual bool virtual bool
DeallocPFileDescriptorSetParent(PFileDescriptorSetParent*) override; DeallocPFileDescriptorSetParent(PFileDescriptorSetParent*) override;
virtual bool
RecvFlushPendingFileDeletions() override;
virtual PWebrtcGlobalParent* AllocPWebrtcGlobalParent() override; virtual PWebrtcGlobalParent* AllocPWebrtcGlobalParent() override;
virtual bool DeallocPWebrtcGlobalParent(PWebrtcGlobalParent *aActor) override; virtual bool DeallocPWebrtcGlobalParent(PWebrtcGlobalParent *aActor) override;

View File

@ -1055,9 +1055,6 @@ parent:
sync KeygenProvideContent() sync KeygenProvideContent()
returns (nsString aAttribute, nsString[] aContent); returns (nsString aAttribute, nsString[] aContent);
// Use only for testing!
async FlushPendingFileDeletions();
/** /**
* Tell the chrome process there is an creation of PBrowser. * Tell the chrome process there is an creation of PBrowser.
* return a system-wise unique Id. * return a system-wise unique Id.

View File

@ -2631,6 +2631,9 @@ MediaManager::Shutdown()
GetActiveWindows()->Clear(); GetActiveWindows()->Clear();
mActiveCallbacks.Clear(); mActiveCallbacks.Clear();
mCallIds.Clear(); mCallIds.Clear();
#ifdef MOZ_WEBRTC
StopWebRtcLog();
#endif
// Because mMediaThread is not an nsThread, we must dispatch to it so it can // Because mMediaThread is not an nsThread, we must dispatch to it so it can
// clean up BackgroundChild. Continue stopping thread once this is done. // clean up BackgroundChild. Continue stopping thread once this is done.

View File

@ -53,8 +53,8 @@ function range(start, end) {
function once(target, name, cb) { function once(target, name, cb) {
var p = new Promise(function(resolve, reject) { var p = new Promise(function(resolve, reject) {
target.addEventListener(name, function() { target.addEventListener(name, function onceEvent() {
target.removeEventListener(name, arguments.callee); target.removeEventListener(name, onceEvent);
resolve(); resolve();
}); });
}); });

View File

@ -740,7 +740,7 @@ MediaCodecDataDecoder::Shutdown()
{ {
MonitorAutoLock lock(mMonitor); MonitorAutoLock lock(mMonitor);
if (!mThread || State() == kStopping) { if (State() == kStopping) {
// Already shutdown or in the process of doing so // Already shutdown or in the process of doing so
return NS_OK; return NS_OK;
} }
@ -748,15 +748,20 @@ MediaCodecDataDecoder::Shutdown()
State(kStopping); State(kStopping);
lock.Notify(); lock.Notify();
while (State() == kStopping) { while (mThread && State() == kStopping) {
lock.Wait(); lock.Wait();
} }
mThread->Shutdown(); if (mThread) {
mThread = nullptr; mThread->Shutdown();
mThread = nullptr;
}
mDecoder->Stop(); if (mDecoder) {
mDecoder->Release(); mDecoder->Stop();
mDecoder->Release();
mDecoder = nullptr;
}
return NS_OK; return NS_OK;
} }

View File

@ -40,8 +40,8 @@ FFmpegLibWrapper::Link()
uint32_t version = avcodec_version(); uint32_t version = avcodec_version();
mVersion = (version >> 16) & 0xff; mVersion = (version >> 16) & 0xff;
uint32_t micro = version & 0xff; uint32_t micro = version & 0xff;
if (mVersion == 57 && micro != 100) { if (mVersion == 57 && micro < 100) {
// a micro version of 100 indicates that it's FFmpeg (as opposed to LibAV). // a micro version >= 100 indicates that it's FFmpeg (as opposed to LibAV).
// Due to current AVCodecContext binary incompatibility we can only // Due to current AVCodecContext binary incompatibility we can only
// support FFmpeg 57 at this stage. // support FFmpeg 57 at this stage.
Unlink(); Unlink();

View File

@ -0,0 +1,73 @@
# 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/.
from __future__ import absolute_import, unicode_literals
import os
import sys
from mozbuild.base import (
MachCommandBase,
MachCommandConditions as conditions,
)
from mach.decorators import (
CommandProvider,
Command,
)
def setup_argument_parser():
from external_media_harness.runtests import MediaTestArguments
return MediaTestArguments()
def run_external_media_test(tests, testtype=None, topsrcdir=None, **kwargs):
from external_media_harness.runtests import (
FirefoxMediaHarness,
MediaTestArguments,
MediaTestRunner,
mn_cli,
)
from mozlog.structured import commandline
parser = MediaTestArguments()
commandline.add_logging_group(parser)
args = parser.parse_args()
if not tests:
tests = [os.path.join(topsrcdir,
'dom/media/test/external/external_media_tests/manifest.ini')]
args.tests = tests
if not args.binary:
args.binary = kwargs['binary']
for k, v in kwargs.iteritems():
setattr(args, k, v)
parser.verify_usage(args)
args.logger = commandline.setup_logging("Firefox External Media Tests",
args,
{"mach": sys.stdout})
failed = mn_cli(MediaTestRunner, MediaTestArguments, FirefoxMediaHarness,
args=args)
if failed > 0:
return 1
else:
return 0
@CommandProvider
class MachCommands(MachCommandBase):
@Command('external-media-tests', category='testing',
description='Run Firefox external media tests.',
conditions=[conditions.is_firefox],
parser=setup_argument_parser,
)
def run_external_media_test(self, tests, **kwargs):
kwargs['binary'] = self.get_binary_path('app')
return run_external_media_test(tests, topsrcdir=self.topsrcdir, **kwargs)

View File

@ -15,9 +15,9 @@ mozrunner==6.11
moztest==0.7 moztest==0.7
mozversion==1.4 mozversion==1.4
wptserve==1.3.0 wptserve==1.3.0
marionette-client==2.1.0 marionette-client == 2.2.0
marionette-driver==1.2.0 marionette-driver == 1.3.0
firefox-puppeteer==3.1.0 firefox-puppeteer==3.2.0
# Install the firefox media tests package # Install the firefox media tests package
./ ./

View File

@ -7,11 +7,11 @@ from setuptools import setup, find_packages
PACKAGE_VERSION = '1.0' PACKAGE_VERSION = '1.0'
deps = [ deps = [
'marionette-client == 2.1.0', 'marionette-client == 2.2.0',
'marionette-driver == 1.2.0', 'marionette-driver == 1.3.0',
'mozlog == 3.1', 'mozlog == 3.1',
'manifestparser == 1.1', 'manifestparser == 1.1',
'firefox-puppeteer >= 3.1.0, <4.0.0', 'firefox-puppeteer >= 3.2.0, <4.0.0',
] ]
setup(name='external-media-tests', setup(name='external-media-tests',

View File

@ -1357,8 +1357,8 @@ function removeNodeAndSource(n) {
function once(target, name, cb) { function once(target, name, cb) {
var p = new Promise(function(resolve, reject) { var p = new Promise(function(resolve, reject) {
target.addEventListener(name, function() { target.addEventListener(name, function onceEvent() {
target.removeEventListener(name, cb); target.removeEventListener(name, onceEvent);
resolve(); resolve();
}); });
}); });

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<meta charset="utf-8">
<script type="application/javascript">
function f()
{
if (speechSynthesis.getVoices().length == 0) {
// No synthesis backend to test this
document.documentElement.removeAttribute('class');
return;
}
var s = new SpeechSynthesisUtterance("hello world");
s.onerror = () => {
// No synthesis backend to test this
document.documentElement.removeAttribute('class');
return;
}
s.onend = () => {
document.documentElement.removeAttribute('class');
};
speechSynthesis.speak(s);
speechSynthesis.cancel();
speechSynthesis.pause();
speechSynthesis.resume();
}
</script>
</head>
<body onload="f();">
</body>
</html>

View File

@ -0,0 +1 @@
skip-if(!cocoaWidget) pref(media.webspeech.synth.enabled,true) load 1230428.html # bug 1230428

View File

@ -58,9 +58,16 @@ public:
switch (event) { switch (event) {
case EVENT_FINISHED: case EVENT_FINISHED:
{ {
nsCOMPtr<nsIRunnable> runnable = if (!mStarted) {
mStarted = true;
nsCOMPtr<nsIRunnable> startRunnable =
NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(startRunnable.forget());
}
nsCOMPtr<nsIRunnable> endRunnable =
NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished); NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget()); aGraph->DispatchToMainThreadAfterStreamStateUpdate(endRunnable.forget());
} }
break; break;
case EVENT_REMOVED: case EVENT_REMOVED:

View File

@ -24,6 +24,7 @@
#endif #endif
#include "mozilla/X11Util.h" #include "mozilla/X11Util.h"
static void plug_added_cb(GtkWidget *widget, gpointer data);
static gboolean plug_removed_cb (GtkWidget *widget, gpointer data); static gboolean plug_removed_cb (GtkWidget *widget, gpointer data);
static void socket_unrealize_cb (GtkWidget *widget, gpointer data); static void socket_unrealize_cb (GtkWidget *widget, gpointer data);
@ -163,6 +164,9 @@ nsresult nsPluginNativeWindowGtk::CreateXEmbedWindow(bool aEnableXtFocus) {
// see plugin_window_filter_func() for details // see plugin_window_filter_func() for details
g_object_set_data(G_OBJECT(mSocketWidget), "enable-xt-focus", (void *)aEnableXtFocus); g_object_set_data(G_OBJECT(mSocketWidget), "enable-xt-focus", (void *)aEnableXtFocus);
g_signal_connect(mSocketWidget, "plug_added",
G_CALLBACK(plug_added_cb), nullptr);
// Make sure to handle the plug_removed signal. If we don't the // Make sure to handle the plug_removed signal. If we don't the
// socket will automatically be destroyed when the plug is // socket will automatically be destroyed when the plug is
// removed, which means we're destroying it more than once. // removed, which means we're destroying it more than once.
@ -278,6 +282,32 @@ nsresult nsPluginNativeWindowGtk::CreateXtWindow() {
} }
#endif #endif
static void
plug_window_finalize_cb(gpointer socket, GObject* plug_window)
{
g_object_unref(socket);
}
static void
plug_added_cb(GtkWidget *socket, gpointer data)
{
// The plug window has been embedded, and gtk_socket_add_window() has added
// a filter to the socket's plug_window, passing the socket as data for the
// filter, so the socket must live as long as events may be received on the
// plug window.
//
// https://git.gnome.org/browse/gtk+/tree/gtk/gtksocket.c?h=3.18.7#n1124
g_object_ref(socket);
// When the socket is unrealized, perhaps during gtk_widget_destroy() from
// ~nsPluginNativeWindowGtk, the plug is removed. The plug in the child
// process then destroys its widget and window. When the browser process
// receives the DestroyNotify event for the plug window, GDK releases its
// reference to plugWindow. This is typically the last reference and so the
// weak ref callback triggers release of the socket.
GdkWindow* plugWindow = gtk_socket_get_plug_window(GTK_SOCKET(socket));
g_object_weak_ref(G_OBJECT(plugWindow), plug_window_finalize_cb, socket);
}
/* static */ /* static */
gboolean gboolean
plug_removed_cb (GtkWidget *widget, gpointer data) plug_removed_cb (GtkWidget *widget, gpointer data)

View File

@ -1974,12 +1974,6 @@ RuntimeService::Shutdown()
// That's it, no more workers. // That's it, no more workers.
mShuttingDown = true; mShuttingDown = true;
// Remove all listeners from the worker debugger manager to ensure that it
// gets properly destroyed.
if (NS_FAILED(ClearWorkerDebuggerManagerListeners())) {
NS_WARNING("Failed to clear worker debugger manager listeners!");
}
nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
NS_WARN_IF_FALSE(obs, "Failed to get observer service?!"); NS_WARN_IF_FALSE(obs, "Failed to get observer service?!");

View File

@ -8,22 +8,23 @@
#include "nsISimpleEnumerator.h" #include "nsISimpleEnumerator.h"
#include "mozilla/ClearOnShutdown.h"
#include "WorkerPrivate.h" #include "WorkerPrivate.h"
USING_WORKERS_NAMESPACE USING_WORKERS_NAMESPACE
namespace {
class RegisterDebuggerMainThreadRunnable final : public nsRunnable class RegisterDebuggerMainThreadRunnable final : public nsRunnable
{ {
RefPtr<WorkerDebuggerManager> mManager;
WorkerPrivate* mWorkerPrivate; WorkerPrivate* mWorkerPrivate;
bool mNotifyListeners; bool mNotifyListeners;
public: public:
RegisterDebuggerMainThreadRunnable(WorkerDebuggerManager* aManager, RegisterDebuggerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
WorkerPrivate* aWorkerPrivate,
bool aNotifyListeners) bool aNotifyListeners)
: mManager(aManager), : mWorkerPrivate(aWorkerPrivate),
mWorkerPrivate(aWorkerPrivate),
mNotifyListeners(aNotifyListeners) mNotifyListeners(aNotifyListeners)
{ } { }
@ -34,21 +35,21 @@ private:
NS_IMETHOD NS_IMETHOD
Run() override Run() override
{ {
mManager->RegisterDebuggerMainThread(mWorkerPrivate, mNotifyListeners); WorkerDebuggerManager* manager = WorkerDebuggerManager::Get();
MOZ_ASSERT(manager);
manager->RegisterDebuggerMainThread(mWorkerPrivate, mNotifyListeners);
return NS_OK; return NS_OK;
} }
}; };
class UnregisterDebuggerMainThreadRunnable final : public nsRunnable class UnregisterDebuggerMainThreadRunnable final : public nsRunnable
{ {
RefPtr<WorkerDebuggerManager> mManager;
WorkerPrivate* mWorkerPrivate; WorkerPrivate* mWorkerPrivate;
public: public:
UnregisterDebuggerMainThreadRunnable(WorkerDebuggerManager* aManager, explicit UnregisterDebuggerMainThreadRunnable(WorkerPrivate* aWorkerPrivate)
WorkerPrivate* aWorkerPrivate) : mWorkerPrivate(aWorkerPrivate)
: mManager(aManager), mWorkerPrivate(aWorkerPrivate)
{ } { }
private: private:
@ -58,12 +59,19 @@ private:
NS_IMETHOD NS_IMETHOD
Run() override Run() override
{ {
mManager->UnregisterDebuggerMainThread(mWorkerPrivate); WorkerDebuggerManager* manager = WorkerDebuggerManager::Get();
MOZ_ASSERT(manager);
manager->UnregisterDebuggerMainThread(mWorkerPrivate);
return NS_OK; return NS_OK;
} }
}; };
// Does not hold an owning reference.
static WorkerDebuggerManager* gWorkerDebuggerManager;
} /* anonymous namespace */
BEGIN_WORKERS_NAMESPACE BEGIN_WORKERS_NAMESPACE
class WorkerDebuggerEnumerator final : public nsISimpleEnumerator class WorkerDebuggerEnumerator final : public nsISimpleEnumerator
@ -116,7 +124,54 @@ WorkerDebuggerManager::~WorkerDebuggerManager()
AssertIsOnMainThread(); AssertIsOnMainThread();
} }
NS_IMPL_ISUPPORTS(WorkerDebuggerManager, nsIWorkerDebuggerManager); // static
already_AddRefed<WorkerDebuggerManager>
WorkerDebuggerManager::GetInstance()
{
RefPtr<WorkerDebuggerManager> manager = WorkerDebuggerManager::GetOrCreate();
return manager.forget();
}
// static
WorkerDebuggerManager*
WorkerDebuggerManager::GetOrCreate()
{
AssertIsOnMainThread();
if (!gWorkerDebuggerManager) {
// The observer service now owns us until shutdown.
gWorkerDebuggerManager = new WorkerDebuggerManager();
if (NS_FAILED(gWorkerDebuggerManager->Init())) {
NS_WARNING("Failed to initialize worker debugger manager!");
gWorkerDebuggerManager = nullptr;
return nullptr;
}
}
return gWorkerDebuggerManager;
}
WorkerDebuggerManager*
WorkerDebuggerManager::Get()
{
MOZ_ASSERT(gWorkerDebuggerManager);
return gWorkerDebuggerManager;
}
NS_IMPL_ISUPPORTS(WorkerDebuggerManager, nsIObserver, nsIWorkerDebuggerManager);
NS_IMETHODIMP
WorkerDebuggerManager::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
Shutdown();
return NS_OK;
}
NS_NOTREACHED("Unknown observer topic!");
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
WorkerDebuggerManager::GetWorkerDebuggerEnumerator( WorkerDebuggerManager::GetWorkerDebuggerEnumerator(
@ -161,8 +216,20 @@ WorkerDebuggerManager::RemoveListener(
return NS_OK; return NS_OK;
} }
nsresult
WorkerDebuggerManager::Init()
{
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void void
WorkerDebuggerManager::ClearListeners() WorkerDebuggerManager::Shutdown()
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
@ -205,8 +272,7 @@ WorkerDebuggerManager::RegisterDebugger(WorkerPrivate* aWorkerPrivate)
} }
nsCOMPtr<nsIRunnable> runnable = nsCOMPtr<nsIRunnable> runnable =
new RegisterDebuggerMainThreadRunnable(this, aWorkerPrivate, new RegisterDebuggerMainThreadRunnable(aWorkerPrivate, hasListeners);
hasListeners);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED( MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))); NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL)));
@ -225,7 +291,7 @@ WorkerDebuggerManager::UnregisterDebugger(WorkerPrivate* aWorkerPrivate)
UnregisterDebuggerMainThread(aWorkerPrivate); UnregisterDebuggerMainThread(aWorkerPrivate);
} else { } else {
nsCOMPtr<nsIRunnable> runnable = nsCOMPtr<nsIRunnable> runnable =
new UnregisterDebuggerMainThreadRunnable(this, aWorkerPrivate); new UnregisterDebuggerMainThreadRunnable(aWorkerPrivate);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED( MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))); NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL)));

View File

@ -9,6 +9,7 @@
#include "Workers.h" #include "Workers.h"
#include "nsIObserver.h"
#include "nsIWorkerDebuggerManager.h" #include "nsIWorkerDebuggerManager.h"
#include "nsServiceManagerUtils.h" #include "nsServiceManagerUtils.h"
@ -24,7 +25,8 @@ BEGIN_WORKERS_NAMESPACE
class WorkerDebugger; class WorkerDebugger;
class WorkerDebuggerManager final : public nsIWorkerDebuggerManager class WorkerDebuggerManager final : public nsIObserver,
public nsIWorkerDebuggerManager
{ {
Mutex mMutex; Mutex mMutex;
@ -35,54 +37,58 @@ class WorkerDebuggerManager final : public nsIWorkerDebuggerManager
nsTArray<RefPtr<WorkerDebugger>> mDebuggers; nsTArray<RefPtr<WorkerDebugger>> mDebuggers;
public: public:
static already_AddRefed<WorkerDebuggerManager>
GetInstance();
static WorkerDebuggerManager* static WorkerDebuggerManager*
GetOrCreateService() GetOrCreate();
{
nsCOMPtr<nsIWorkerDebuggerManager> manager = static WorkerDebuggerManager*
do_GetService(WORKERDEBUGGERMANAGER_CONTRACTID); Get();
return static_cast<WorkerDebuggerManager*>(manager.get());
}
WorkerDebuggerManager(); WorkerDebuggerManager();
NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIWORKERDEBUGGERMANAGER NS_DECL_NSIWORKERDEBUGGERMANAGER
void ClearListeners(); nsresult
Init();
void RegisterDebugger(WorkerPrivate* aWorkerPrivate); void
Shutdown();
void UnregisterDebugger(WorkerPrivate* aWorkerPrivate); void
RegisterDebugger(WorkerPrivate* aWorkerPrivate);
void RegisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate, void
bool aNotifyListeners); RegisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate,
bool aNotifyListeners);
void UnregisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate); void
UnregisterDebugger(WorkerPrivate* aWorkerPrivate);
void
UnregisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate);
private: private:
virtual ~WorkerDebuggerManager(); virtual ~WorkerDebuggerManager();
}; };
inline nsresult
ClearWorkerDebuggerManagerListeners()
{
RefPtr<WorkerDebuggerManager> manager =
WorkerDebuggerManager::GetOrCreateService();
if (!manager) {
return NS_ERROR_FAILURE;
}
manager->ClearListeners();
return NS_OK;
}
inline nsresult inline nsresult
RegisterWorkerDebugger(WorkerPrivate* aWorkerPrivate) RegisterWorkerDebugger(WorkerPrivate* aWorkerPrivate)
{ {
RefPtr<WorkerDebuggerManager> manager = WorkerDebuggerManager* manager;
WorkerDebuggerManager::GetOrCreateService();
if (!manager) { if (NS_IsMainThread()) {
return NS_ERROR_FAILURE; manager = WorkerDebuggerManager::GetOrCreate();
if (!manager) {
NS_WARNING("Failed to create worker debugger manager!");
return NS_ERROR_FAILURE;
}
}
else {
manager = WorkerDebuggerManager::Get();
} }
manager->RegisterDebugger(aWorkerPrivate); manager->RegisterDebugger(aWorkerPrivate);
@ -92,10 +98,17 @@ RegisterWorkerDebugger(WorkerPrivate* aWorkerPrivate)
inline nsresult inline nsresult
UnregisterWorkerDebugger(WorkerPrivate* aWorkerPrivate) UnregisterWorkerDebugger(WorkerPrivate* aWorkerPrivate)
{ {
RefPtr<WorkerDebuggerManager> manager = WorkerDebuggerManager* manager;
WorkerDebuggerManager::GetOrCreateService();
if (!manager) { if (NS_IsMainThread()) {
return NS_ERROR_FAILURE; manager = WorkerDebuggerManager::GetOrCreate();
if (!manager) {
NS_WARNING("Failed to create worker debugger manager!");
return NS_ERROR_FAILURE;
}
}
else {
manager = WorkerDebuggerManager::Get();
} }
manager->UnregisterDebugger(aWorkerPrivate); manager->UnregisterDebugger(aWorkerPrivate);

View File

@ -1832,7 +1832,7 @@ XULDocument::RemoveElementFromRefMap(Element* aElement)
if (!entry) if (!entry)
return; return;
if (entry->RemoveElement(aElement)) { if (entry->RemoveElement(aElement)) {
mRefMap.RawRemoveEntry(entry); mRefMap.RemoveEntry(entry);
} }
} }
} }

View File

@ -1571,7 +1571,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
PermissionHashKey* entry = mPermissionTable.PutEntry(key); PermissionHashKey* entry = mPermissionTable.PutEntry(key);
if (!entry) return NS_ERROR_FAILURE; if (!entry) return NS_ERROR_FAILURE;
if (!entry->GetKey()) { if (!entry->GetKey()) {
mPermissionTable.RawRemoveEntry(entry); mPermissionTable.RemoveEntry(entry);
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
} }
@ -1694,7 +1694,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
// If there are no more permissions stored for that entry, clear it. // If there are no more permissions stored for that entry, clear it.
if (entry->GetPermissions().IsEmpty()) { if (entry->GetPermissions().IsEmpty()) {
mPermissionTable.RawRemoveEntry(entry); mPermissionTable.RemoveEntry(entry);
} }
break; break;

View File

@ -238,7 +238,7 @@ uint16 Face::getGlyphMetric(uint16 gid, uint8 metric) const
case kgmetAscent : return m_ascent; case kgmetAscent : return m_ascent;
case kgmetDescent : return m_descent; case kgmetDescent : return m_descent;
default: default:
if (gid > glyphs().numGlyphs()) return 0; if (gid >= glyphs().numGlyphs()) return 0;
return glyphs().glyph(gid)->getMetric(metric); return glyphs().glyph(gid)->getMetric(metric);
} }
} }

View File

@ -60,8 +60,12 @@ FileFace::FileFace(const char *filename)
if (!TtfUtil::GetTableDirInfo(_header_tbl, tbl_offset, tbl_len)) return; if (!TtfUtil::GetTableDirInfo(_header_tbl, tbl_offset, tbl_len)) return;
_table_dir = (TtfUtil::Sfnt::OffsetSubTable::Entry*)gralloc<char>(tbl_len); _table_dir = (TtfUtil::Sfnt::OffsetSubTable::Entry*)gralloc<char>(tbl_len);
if (fseek(_file, tbl_offset, SEEK_SET)) return; if (fseek(_file, tbl_offset, SEEK_SET)) return;
if (_table_dir) if (_table_dir && fread(_table_dir, 1, tbl_len, _file) != tbl_len)
if (fread(_table_dir, 1, tbl_len, _file) != tbl_len) return; {
free(_table_dir);
_table_dir = NULL;
}
return;
} }
FileFace::~FileFace() FileFace::~FileFace()

View File

@ -100,7 +100,9 @@ bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t su
if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e); if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e);
// Read in basic values // Read in basic values
const byte flags = be::read<byte>(p); const byte flags = be::read<byte>(p);
if (e.test((flags & 0x1f) && pt < PASS_TYPE_POSITIONING, E_BADCOLLISIONPASS)) if (e.test((flags & 0x1f) &&
(pt < PASS_TYPE_POSITIONING || !m_silf->aCollision() || !face.glyphs().hasBoxes()),
E_BADCOLLISIONPASS))
return face.error(e); return face.error(e);
m_numCollRuns = flags & 0x7; m_numCollRuns = flags & 0x7;
m_kernColls = (flags >> 3) & 0x3; m_kernColls = (flags >> 3) & 0x3;

View File

@ -112,6 +112,7 @@ public:
const SlantBox & getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const; const SlantBox & getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const;
const BBox & getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const; const BBox & getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const;
bool check(unsigned short glyphid) const; bool check(unsigned short glyphid) const;
bool hasBoxes() const { return _boxes != 0; }
CLASS_NEW_DELETE; CLASS_NEW_DELETE;

View File

@ -31,3 +31,4 @@ skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel ev
skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
[test_scroll_subframe_scrollbar.html] [test_scroll_subframe_scrollbar.html]
skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
[test_frame_reconstruction.html]

View File

@ -0,0 +1,231 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1235899
-->
<head>
<title>Test for bug 1235899</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style>
.outer {
height: 400px;
width: 415px;
overflow: hidden;
position: relative;
}
.inner {
height: 100%;
outline: none;
overflow-x: hidden;
overflow-y: scroll;
position: relative;
scroll-behavior: smooth;
}
.outer.contentBefore::before {
top: 0;
content: '';
display: block;
height: 2px;
position: absolute;
width: 100%;
z-index: 99;
}
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1235899">Mozilla Bug 1235899</a>
<p id="display"></p>
<div id="content">
<p>You should be able to fling this list without it stopping abruptly</p>
<div class="outer">
<div class="inner">
<ol>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
</ol>
</div>
</div>
</div>
<pre id="test">
<script type="application/javascript;version=1.7">
function* runTest() {
var elm = document.getElementsByClassName('inner')[0];
elm.scrollTop = 0;
yield flushApzRepaints(driveTest);
// Take over control of the refresh driver and compositor
var utils = SpecialPowers.DOMWindowUtils;
utils.advanceTimeAndRefresh(0);
// Kick off an APZ smooth-scroll to 0,200
elm.scrollTo(0, 200);
yield waitForAllPaints(function() { setTimeout(driveTest, 0); });
// Let's do a couple of frames of the animation, and make sure it's going
utils.advanceTimeAndRefresh(16);
utils.advanceTimeAndRefresh(16);
yield flushApzRepaints(driveTest);
ok(elm.scrollTop > 0, "APZ animation in progress", "scrollTop is now " + elm.scrollTop);
ok(elm.scrollTop < 200, "APZ animation not yet completed", "scrollTop is now " + elm.scrollTop);
var frameReconstructionTriggered = 0;
// Register the listener that triggers the frame reconstruction
elm.onscroll = function() {
// Do the reconstruction
elm.parentNode.classList.add('contentBefore');
frameReconstructionTriggered++;
// schedule a thing to undo the changes above
setTimeout(function() {
elm.parentNode.classList.remove('contentBefore');
}, 0);
}
// and do a few more frames of the animation, this should trigger the listener
// and the frame reconstruction
utils.advanceTimeAndRefresh(16);
utils.advanceTimeAndRefresh(16);
yield flushApzRepaints(driveTest);
ok(elm.scrollTop < 200, "APZ animation not yet completed", "scrollTop is now " + elm.scrollTop);
ok(frameReconstructionTriggered > 0, "Frame reconstruction triggered", "reconstruction triggered " + frameReconstructionTriggered + " times");
// and now run to completion
for (var i = 0; i < 100; i++) {
utils.advanceTimeAndRefresh(16);
}
utils.restoreNormalRefresh();
yield waitForAllPaints(function() { setTimeout(driveTest, 0); });
yield flushApzRepaints(driveTest);
is(elm.scrollTop, 200, "Element should have scrolled by 200px");
}
var gTestContinuation = null;
function driveTest() {
if (!gTestContinuation) {
gTestContinuation = runTest();
}
var ret = gTestContinuation.next();
if (ret.done) {
SimpleTest.finish();
}
}
SimpleTest.waitForExplicitFinish();
var apzEnabled = SpecialPowers.getDOMWindowUtils(window).asyncPanZoomEnabled;
if (!apzEnabled) {
ok(true, "APZ not enabled, skipping test");
SimpleTest.finish();
} else {
SimpleTest.expectAssertions(0, 1); // this test triggers an assertion, see bug 1247050
SimpleTest.waitForFocus(driveTest, window);
}
</script>
</body>
</html>

View File

@ -61,12 +61,6 @@
SimpleTest.finish(); 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({ SpecialPowers.pushPrefEnv({
"set" : [ "set" : [
[testPref, true] [testPref, true]

View File

@ -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 // 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). // 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 // since the last layers update, then we don't want to push our scroll request
// because we'll clobber that one, which is bad. // because we'll clobber that one, which is bad.
bool scrollInProgress = aFrame->IsProcessingAsyncScroll() bool scrollInProgress = aFrame->IsProcessingAsyncScroll()
|| (aFrame->LastScrollOrigin() && aFrame->LastScrollOrigin() != nsGkAtoms::apz) || nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin())
|| aFrame->LastSmoothScrollOrigin(); || aFrame->LastSmoothScrollOrigin();
if (!scrollInProgress) { if (!scrollInProgress) {
aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition, nsGkAtoms::apz); aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition, nsGkAtoms::apz);

View File

@ -564,8 +564,8 @@ RenderLayers(ContainerT* aContainer,
if (layerToRender->HasLayerBeenComposited()) { if (layerToRender->HasLayerBeenComposited()) {
// Composer2D will compose this layer so skip GPU composition // Composer2D will compose this layer so skip GPU composition
// this time & reset composition flag for next composition phase // this time. The flag will be reset for the next composition phase
layerToRender->SetLayerComposited(false); // at the beginning of LayerManagerComposite::Rener().
gfx::IntRect clearRect = layerToRender->GetClearRect(); gfx::IntRect clearRect = layerToRender->GetClearRect();
if (!clearRect.IsEmpty()) { if (!clearRect.IsEmpty()) {
// Clear layer's visible rect on FrameBuffer with transparent pixels // Clear layer's visible rect on FrameBuffer with transparent pixels

View File

@ -762,6 +762,21 @@ LayerManagerComposite::PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> a
Matrix4x4()); 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 void
LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion) LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion)
{ {
@ -773,6 +788,8 @@ LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion)
return; return;
} }
ClearLayerFlags(mRoot);
// At this time, it doesn't really matter if these preferences change // At this time, it doesn't really matter if these preferences change
// during the execution of the function; we should be safe in all // during the execution of the function; we should be safe in all
// permutations. However, may as well just get the values onces and // permutations. However, may as well just get the values onces and

View File

@ -58,6 +58,37 @@ using namespace mozilla::media;
typedef std::vector<CompositableOperation> OpVector; typedef std::vector<CompositableOperation> OpVector;
typedef nsTArray<OpDestroy> OpDestroyVector; typedef nsTArray<OpDestroy> 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 struct CompositableTransaction
{ {
CompositableTransaction() CompositableTransaction()
@ -376,7 +407,7 @@ bool ImageBridgeChild::IsCreated()
void ImageBridgeChild::StartUp() void ImageBridgeChild::StartUp()
{ {
NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!"); NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
ImageBridgeChild::StartUpOnThread(new Thread("ImageBridgeChild")); ImageBridgeChild::StartUpOnThread(new ImageBridgeThread());
} }
#ifdef MOZ_NUWA_PROCESS #ifdef MOZ_NUWA_PROCESS
@ -709,7 +740,7 @@ ImageBridgeChild::StartUpInChildProcess(Transport* aTransport,
gfxPlatform::GetPlatform(); gfxPlatform::GetPlatform();
sImageBridgeChildThread = new Thread("ImageBridgeChild"); sImageBridgeChildThread = new ImageBridgeThread();
if (!sImageBridgeChildThread->Start()) { if (!sImageBridgeChildThread->Start()) {
return nullptr; return nullptr;
} }

View File

@ -590,11 +590,12 @@ ShareTableAndGetBlob(nsTArray<uint8_t>&& aTable,
Clear(); Clear();
// adopts elements of aTable // adopts elements of aTable
mSharedBlobData = new FontTableBlobData(Move(aTable)); mSharedBlobData = new FontTableBlobData(Move(aTable));
mBlob = hb_blob_create(mSharedBlobData->GetTable(), mBlob = hb_blob_create(mSharedBlobData->GetTable(),
mSharedBlobData->GetTableLength(), mSharedBlobData->GetTableLength(),
HB_MEMORY_MODE_READONLY, HB_MEMORY_MODE_READONLY,
mSharedBlobData, DeleteFontTableBlobData); mSharedBlobData, DeleteFontTableBlobData);
if (!mSharedBlobData) { if (mBlob == hb_blob_get_empty() ) {
// The FontTableBlobData was destroyed during hb_blob_create(). // The FontTableBlobData was destroyed during hb_blob_create().
// The (empty) blob is still be held in the hashtable with a strong // The (empty) blob is still be held in the hashtable with a strong
// reference. // reference.

View File

@ -617,13 +617,13 @@ gfxFontconfigUtils::UpdateFontListInternal(bool aForce)
bool added = entry->AddFont(font); bool added = entry->AddFont(font);
if (!entry->mKey) { if (!entry->mKey) {
// The reference to the font pattern keeps the pointer to // The reference to the font pattern keeps the pointer
// string for the key valid. If adding the font failed // to string for the key valid. If adding the font
// then the entry must be removed. // failed then the entry must be removed.
if (added) { if (added) {
entry->mKey = family; entry->mKey = family;
} else { } else {
mFontsByFamily.RawRemoveEntry(entry); mFontsByFamily.RemoveEntry(entry);
} }
} }
} }

View File

@ -217,6 +217,15 @@ BackgroundParentImpl::DeallocPBackgroundIndexedDBUtilsParent(
mozilla::dom::indexedDB::DeallocPBackgroundIndexedDBUtilsParent(aActor); mozilla::dom::indexedDB::DeallocPBackgroundIndexedDBUtilsParent(aActor);
} }
bool
BackgroundParentImpl::RecvFlushPendingFileDeletions()
{
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
return mozilla::dom::indexedDB::RecvFlushPendingFileDeletions();
}
auto auto
BackgroundParentImpl::AllocPBlobParent(const BlobConstructorParams& aParams) BackgroundParentImpl::AllocPBlobParent(const BlobConstructorParams& aParams)
-> PBlobParent* -> PBlobParent*

View File

@ -58,6 +58,9 @@ protected:
PBackgroundIndexedDBUtilsParent* aActor) PBackgroundIndexedDBUtilsParent* aActor)
override; override;
virtual bool
RecvFlushPendingFileDeletions() override;
virtual PBlobParent* virtual PBlobParent*
AllocPBlobParent(const BlobConstructorParams& aParams) override; AllocPBlobParent(const BlobConstructorParams& aParams) override;

View File

@ -157,6 +157,7 @@ public:
MOZ_RELEASE_ASSERT(aOther.mMessageName); MOZ_RELEASE_ASSERT(aOther.mMessageName);
mMessageName = aOther.mMessageName; mMessageName = aOther.mMessageName;
aOther.mMessageName = nullptr; aOther.mMessageName = nullptr;
mMoved = aOther.mMoved;
aOther.mMoved = true; aOther.mMoved = true;
mMessageRoutingId = aOther.mMessageRoutingId; mMessageRoutingId = aOther.mMessageRoutingId;

View File

@ -66,6 +66,9 @@ parent:
async PBackgroundIndexedDBUtils(); async PBackgroundIndexedDBUtils();
// Use only for testing!
async FlushPendingFileDeletions();
async PVsync(); async PVsync();
async PCameras(); async PCameras();

View File

@ -468,7 +468,6 @@ struct RuntimeSizes
macro(_, MallocHeap, object) \ macro(_, MallocHeap, object) \
macro(_, MallocHeap, atomsTable) \ macro(_, MallocHeap, atomsTable) \
macro(_, MallocHeap, contexts) \ macro(_, MallocHeap, contexts) \
macro(_, MallocHeap, dtoa) \
macro(_, MallocHeap, temporary) \ macro(_, MallocHeap, temporary) \
macro(_, MallocHeap, interpreterStack) \ macro(_, MallocHeap, interpreterStack) \
macro(_, MallocHeap, mathCache) \ macro(_, MallocHeap, mathCache) \

View File

@ -934,7 +934,7 @@ MutableHandle<T>::MutableHandle(PersistentRooted<T>* root)
* These roots can be used in heap-allocated data structures, so they are not * 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 * associated with any particular JSContext or stack. They are registered with
* the JSRuntime itself, without locking, so they require a full JSContext to be * 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 * take place on construction, or in two phases if the no-argument constructor
* is called followed by init(). * is called followed by init().
* *
@ -1048,9 +1048,10 @@ class PersistentRooted : public js::PersistentRootedBase<T>,
} }
private: private:
void set(T value) { template <typename U>
void set(U&& value) {
MOZ_ASSERT(initialized()); MOZ_ASSERT(initialized());
ptr = value; ptr = mozilla::Forward<U>(value);
} }
// See the comment above Rooted::ptr. // See the comment above Rooted::ptr.

View File

@ -30,6 +30,8 @@
using namespace js; using namespace js;
using namespace js::wasm; using namespace js::wasm;
using mozilla::IsNaN;
typedef Handle<WasmModuleObject*> HandleWasmModule; typedef Handle<WasmModuleObject*> HandleWasmModule;
typedef MutableHandle<WasmModuleObject*> MutableHandleWasmModule; typedef MutableHandle<WasmModuleObject*> MutableHandleWasmModule;
@ -952,6 +954,9 @@ DecodeDataSection(JSContext* cx, Decoder& d, Handle<ArrayBufferObject*> heap)
if (!d.readCStringIf(DataSection)) if (!d.readCStringIf(DataSection))
return true; return true;
if (!heap)
return Fail(cx, d, "data section requires a memory section");
uint32_t sectionStart; uint32_t sectionStart;
if (!d.startSection(&sectionStart)) if (!d.startSection(&sectionStart))
return Fail(cx, d, "expected data section byte size"); return Fail(cx, d, "expected data section byte size");

View File

@ -96,6 +96,7 @@ FrameIterator::settle()
case CodeRange::ImportJitExit: case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit: case CodeRange::ImportInterpExit:
case CodeRange::Inline: case CodeRange::Inline:
case CodeRange::CallThunk:
MOZ_CRASH("Should not encounter an exit during iteration"); MOZ_CRASH("Should not encounter an exit during iteration");
} }
} }
@ -491,6 +492,7 @@ ProfilingFrameIterator::initFromFP(const WasmActivation& activation)
case CodeRange::ImportJitExit: case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit: case CodeRange::ImportInterpExit:
case CodeRange::Inline: case CodeRange::Inline:
case CodeRange::CallThunk:
MOZ_CRASH("Unexpected CodeRange kind"); MOZ_CRASH("Unexpected CodeRange kind");
} }
@ -541,6 +543,7 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
const CodeRange* codeRange = module_->lookupCodeRange(state.pc); const CodeRange* codeRange = module_->lookupCodeRange(state.pc);
switch (codeRange->kind()) { switch (codeRange->kind()) {
case CodeRange::Function: case CodeRange::Function:
case CodeRange::CallThunk:
case CodeRange::ImportJitExit: case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit: { case CodeRange::ImportInterpExit: {
// When the pc is inside the prologue/epilogue, the innermost // 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(); uint32_t offsetInCodeRange = offsetInModule - codeRange->begin();
void** sp = (void**)state.sp; void** sp = (void**)state.sp;
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) #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 // First instruction of the ARM/MIPS function; the return address is
// still in lr and fp still holds the caller's fp. // still in lr and fp still holds the caller's fp.
callerPC_ = state.lr; callerPC_ = state.lr;
@ -571,7 +574,9 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
AssertMatchesCallSite(*module_, callerPC_, callerFP_, sp); AssertMatchesCallSite(*module_, callerPC_, callerFP_, sp);
} else } else
#endif #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 // The return address has been pushed on the stack but not fp; fp
// still points to the caller's fp. // still points to the caller's fp.
callerPC_ = *sp; callerPC_ = *sp;
@ -655,6 +660,7 @@ ProfilingFrameIterator::operator++()
case CodeRange::ImportJitExit: case CodeRange::ImportJitExit:
case CodeRange::ImportInterpExit: case CodeRange::ImportInterpExit:
case CodeRange::Inline: case CodeRange::Inline:
case CodeRange::CallThunk:
stackAddress_ = callerFP_; stackAddress_ = callerFP_;
callerPC_ = ReturnAddressFromFP(callerFP_); callerPC_ = ReturnAddressFromFP(callerFP_);
AssertMatchesCallSite(*module_, callerPC_, CallerFPFromFP(callerFP_), callerFP_); AssertMatchesCallSite(*module_, callerPC_, CallerFPFromFP(callerFP_), callerFP_);
@ -696,6 +702,7 @@ ProfilingFrameIterator::label() const
case CodeRange::ImportJitExit: return importJitDescription; case CodeRange::ImportJitExit: return importJitDescription;
case CodeRange::ImportInterpExit: return importInterpDescription; case CodeRange::ImportInterpExit: return importInterpDescription;
case CodeRange::Inline: return "inline stub (in asm.js)"; case CodeRange::Inline: return "inline stub (in asm.js)";
case CodeRange::CallThunk: return "call thunk (in asm.js)";
} }
MOZ_CRASH("bad code range kind"); MOZ_CRASH("bad code range kind");
@ -771,6 +778,14 @@ wasm::EnableProfilingPrologue(const Module& module, const CallSite& callSite, bo
#endif #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 // Replace all the nops in all the epilogues of asm.js functions with jumps
// to the profiling epilogues. // to the profiling epilogues.
void void

View File

@ -33,6 +33,7 @@ namespace wasm {
class CallSite; class CallSite;
class CodeRange; class CodeRange;
class Module; class Module;
struct CallThunk;
struct FuncOffsets; struct FuncOffsets;
struct ProfilingOffsets; struct ProfilingOffsets;
@ -112,6 +113,9 @@ GenerateFunctionEpilogue(jit::MacroAssembler& masm, unsigned framePushed, FuncOf
void void
EnableProfilingPrologue(const Module& module, const CallSite& callSite, bool enabled); EnableProfilingPrologue(const Module& module, const CallSite& callSite, bool enabled);
void
EnableProfilingThunk(const Module& module, const CallThunk& callThunk, bool enabled);
void void
EnableProfilingEpilogue(const Module& module, const CodeRange& codeRange, bool enabled); EnableProfilingEpilogue(const Module& module, const CodeRange& codeRange, bool enabled);

View File

@ -18,6 +18,8 @@
#include "asmjs/WasmGenerator.h" #include "asmjs/WasmGenerator.h"
#include "mozilla/EnumeratedRange.h"
#include "asmjs/WasmStubs.h" #include "asmjs/WasmStubs.h"
#include "jit/MacroAssembler-inl.h" #include "jit/MacroAssembler-inl.h"
@ -26,6 +28,8 @@ using namespace js;
using namespace js::jit; using namespace js::jit;
using namespace js::wasm; using namespace js::wasm;
using mozilla::MakeEnumeratedRange;
// **************************************************************************** // ****************************************************************************
// ModuleGenerator // ModuleGenerator
@ -41,6 +45,8 @@ ModuleGenerator::ModuleGenerator(ExclusiveContext* cx)
alloc_(&lifo_), alloc_(&lifo_),
masm_(MacroAssembler::AsmJSToken(), alloc_), masm_(MacroAssembler::AsmJSToken(), alloc_),
funcIndexToExport_(cx), funcIndexToExport_(cx),
lastPatchedCallsite_(0),
startOfUnpatchedBranches_(0),
parallel_(false), parallel_(false),
outstanding_(0), outstanding_(0),
tasks_(cx), tasks_(cx),
@ -173,13 +179,111 @@ ModuleGenerator::finishOutstandingTask()
return finishTask(task); return finishTask(task);
} }
static const uint32_t BadEntry = UINT32_MAX; static const uint32_t BadCodeRange = UINT32_MAX;
bool bool
ModuleGenerator::funcIsDefined(uint32_t funcIndex) const ModuleGenerator::funcIsDefined(uint32_t funcIndex) const
{ {
return funcIndex < funcEntryOffsets_.length() && return funcIndex < funcIndexToCodeRange_.length() &&
funcEntryOffsets_[funcIndex] != BadEntry; 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<uint32_t, uint32_t> 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 bool
@ -188,19 +292,33 @@ ModuleGenerator::finishTask(IonCompileTask* task)
const FuncBytecode& func = task->func(); const FuncBytecode& func = task->func();
FuncCompileResults& results = task->results(); 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 // Offset the recorded FuncOffsets by the offset of the function in the
// whole module's code segment. // whole module's code segment.
uint32_t offsetInWhole = masm_.size(); uint32_t offsetInWhole = masm_.size();
results.offsets().offsetBy(offsetInWhole); results.offsets().offsetBy(offsetInWhole);
// Record the non-profiling entry for whole-module linking later. // Add the CodeRange for this function.
// Cannot simply append because funcIndex order is nonlinear. uint32_t funcCodeRangeIndex = module_->codeRanges.length();
if (func.index() >= funcEntryOffsets_.length()) { if (!module_->codeRanges.emplaceBack(func.index(), func.lineOrBytecode(), results.offsets()))
if (!funcEntryOffsets_.appendN(BadEntry, func.index() - funcEntryOffsets_.length() + 1)) 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; return false;
} }
MOZ_ASSERT(!funcIsDefined(func.index())); MOZ_ASSERT(!funcIsDefined(func.index()));
funcEntryOffsets_[func.index()] = results.offsets().nonProfilingEntry; funcIndexToCodeRange_[func.index()] = funcCodeRangeIndex;
// Merge the compiled results into the whole-module masm. // Merge the compiled results into the whole-module masm.
DebugOnly<size_t> sizeBefore = masm_.size(); DebugOnly<size_t> sizeBefore = masm_.size();
@ -208,10 +326,6 @@ ModuleGenerator::finishTask(IonCompileTask* task)
return false; return false;
MOZ_ASSERT(masm_.size() == offsetInWhole + results.masm().size()); 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. // Keep a record of slow functions for printing in the final console message.
unsigned totalTime = func.generateTime() + results.compileTime(); unsigned totalTime = func.generateTime() + results.compileTime();
if (totalTime >= SlowFunction::msThreshold) { if (totalTime >= SlowFunction::msThreshold) {
@ -223,6 +337,113 @@ ModuleGenerator::finishTask(IonCompileTask* task)
return true; 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<Offsets> entries(cx_);
Vector<ProfilingOffsets> interpExits(cx_);
Vector<ProfilingOffsets> jitExits(cx_);
EnumeratedArray<JumpTarget, JumpTarget::Limit, Offsets> 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 bool
ModuleGenerator::addImport(const Sig& sig, uint32_t globalDataOffset) ModuleGenerator::addImport(const Sig& sig, uint32_t globalDataOffset)
{ {
@ -335,11 +556,12 @@ ModuleGenerator::funcSig(uint32_t funcIndex) const
bool bool
ModuleGenerator::initImport(uint32_t importIndex, uint32_t sigIndex) ModuleGenerator::initImport(uint32_t importIndex, uint32_t sigIndex)
{ {
MOZ_ASSERT(isAsmJS());
uint32_t globalDataOffset; uint32_t globalDataOffset;
if (!allocateGlobalBytes(Module::SizeOfImportExit, sizeof(void*), &globalDataOffset)) if (!allocateGlobalBytes(Module::SizeOfImportExit, sizeof(void*), &globalDataOffset))
return false; return false;
MOZ_ASSERT(isAsmJS());
MOZ_ASSERT(importIndex == module_->imports.length()); MOZ_ASSERT(importIndex == module_->imports.length());
if (!addImport(sig(sigIndex), globalDataOffset)) if (!addImport(sig(sigIndex), globalDataOffset))
return false; return false;
@ -364,16 +586,6 @@ ModuleGenerator::import(uint32_t index) const
return shared_->imports[index]; 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 bool
ModuleGenerator::declareExport(UniqueChars fieldName, uint32_t funcIndex, uint32_t* exportIndex) 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); 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 uint32_t
ModuleGenerator::numExports() const ModuleGenerator::numExports() const
{ {
return module_->exports.length(); 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 bool
ModuleGenerator::addMemoryExport(UniqueChars fieldName) ModuleGenerator::addMemoryExport(UniqueChars fieldName)
{ {
@ -561,24 +746,9 @@ ModuleGenerator::finishFuncDefs()
return false; return false;
} }
for (uint32_t funcIndex = 0; funcIndex < funcEntryOffsets_.length(); funcIndex++) for (uint32_t funcIndex = 0; funcIndex < funcIndexToCodeRange_.length(); funcIndex++)
MOZ_ASSERT(funcIsDefined(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(); module_->functionBytes = masm_.size();
finishedFuncs_ = true; finishedFuncs_ = true;
return true; return true;
@ -628,31 +798,10 @@ ModuleGenerator::defineFuncPtrTable(uint32_t index, const Vector<uint32_t>& elem
for (size_t i = 0; i < elemFuncIndices.length(); i++) { for (size_t i = 0; i < elemFuncIndices.length(); i++) {
uint32_t funcIndex = elemFuncIndices[i]; uint32_t funcIndex = elemFuncIndices[i];
MOZ_ASSERT(funcIsDefined(funcIndex)); 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 bool
ModuleGenerator::finish(CacheableCharsVector&& prettyFuncNames, ModuleGenerator::finish(CacheableCharsVector&& prettyFuncNames,
UniqueModuleData* module, UniqueModuleData* module,
@ -663,15 +812,11 @@ ModuleGenerator::finish(CacheableCharsVector&& prettyFuncNames,
MOZ_ASSERT(!activeFunc_); MOZ_ASSERT(!activeFunc_);
MOZ_ASSERT(finishedFuncs_); MOZ_ASSERT(finishedFuncs_);
if (!finishCodegen())
return false;
module_->prettyFuncNames = Move(prettyFuncNames); 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 // Start global data on a new page so JIT code may be given independent
// protection flags. Note assumption that global data starts right after // protection flags. Note assumption that global data starts right after
// code below. // code below.

View File

@ -142,8 +142,11 @@ class MOZ_STACK_CLASS ModuleGenerator
LifoAlloc lifo_; LifoAlloc lifo_;
jit::TempAllocator alloc_; jit::TempAllocator alloc_;
jit::MacroAssembler masm_; jit::MacroAssembler masm_;
Uint32Vector funcEntryOffsets_; Uint32Vector funcIndexToCodeRange_;
FuncIndexMap funcIndexToExport_; FuncIndexMap funcIndexToExport_;
uint32_t lastPatchedCallsite_;
uint32_t startOfUnpatchedBranches_;
JumpSiteArray jumpThunks_;
// Parallel compilation // Parallel compilation
bool parallel_; bool parallel_;
@ -158,7 +161,10 @@ class MOZ_STACK_CLASS ModuleGenerator
bool finishOutstandingTask(); bool finishOutstandingTask();
bool funcIsDefined(uint32_t funcIndex) const; bool funcIsDefined(uint32_t funcIndex) const;
uint32_t funcEntry(uint32_t funcIndex) const;
bool convertOutOfRangeBranchesToThunks();
bool finishTask(IonCompileTask* task); bool finishTask(IonCompileTask* task);
bool finishCodegen();
bool addImport(const Sig& sig, uint32_t globalDataOffset); bool addImport(const Sig& sig, uint32_t globalDataOffset);
bool startedFuncDefs() const { return !!threadView_; } bool startedFuncDefs() const { return !!threadView_; }
bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset); 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); bool initImport(uint32_t importIndex, uint32_t sigIndex);
uint32_t numImports() const; uint32_t numImports() const;
const ModuleImportGeneratorData& import(uint32_t index) const; const ModuleImportGeneratorData& import(uint32_t index) const;
bool defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit);
// Exports: // Exports:
bool declareExport(UniqueChars fieldName, uint32_t funcIndex, uint32_t* exportIndex = nullptr); bool declareExport(UniqueChars fieldName, uint32_t funcIndex, uint32_t* exportIndex = nullptr);
uint32_t numExports() const; 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); bool addMemoryExport(UniqueChars fieldName);
// Function definitions: // Function definitions:
@ -217,11 +218,6 @@ class MOZ_STACK_CLASS ModuleGenerator
uint32_t funcPtrTableGlobalDataOffset(uint32_t index) const; uint32_t funcPtrTableGlobalDataOffset(uint32_t index) const;
void defineFuncPtrTable(uint32_t index, const Vector<uint32_t>& elemFuncIndices); void defineFuncPtrTable(uint32_t index, const Vector<uint32_t>& 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 // Return a ModuleData object which may be used to construct a Module, the
// StaticLinkData required to call Module::staticallyLink, and the list of // StaticLinkData required to call Module::staticallyLink, and the list of
// functions that took a long time to compile. // functions that took a long time to compile.

View File

@ -360,7 +360,7 @@ CodeRange::CodeRange(Kind kind, Offsets offsets)
u.kind_ = kind; u.kind_ = kind;
MOZ_ASSERT(begin_ <= end_); 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) CodeRange::CodeRange(Kind kind, ProfilingOffsets offsets)
@ -508,6 +508,7 @@ ModuleData::serializedSize() const
SerializedPodVectorSize(heapAccesses) + SerializedPodVectorSize(heapAccesses) +
SerializedPodVectorSize(codeRanges) + SerializedPodVectorSize(codeRanges) +
SerializedPodVectorSize(callSites) + SerializedPodVectorSize(callSites) +
SerializedPodVectorSize(callThunks) +
SerializedVectorSize(prettyFuncNames) + SerializedVectorSize(prettyFuncNames) +
filename.serializedSize(); filename.serializedSize();
} }
@ -522,6 +523,7 @@ ModuleData::serialize(uint8_t* cursor) const
cursor = SerializePodVector(cursor, heapAccesses); cursor = SerializePodVector(cursor, heapAccesses);
cursor = SerializePodVector(cursor, codeRanges); cursor = SerializePodVector(cursor, codeRanges);
cursor = SerializePodVector(cursor, callSites); cursor = SerializePodVector(cursor, callSites);
cursor = SerializePodVector(cursor, callThunks);
cursor = SerializeVector(cursor, prettyFuncNames); cursor = SerializeVector(cursor, prettyFuncNames);
cursor = filename.serialize(cursor); cursor = filename.serialize(cursor);
return cursor; return cursor;
@ -542,6 +544,7 @@ ModuleData::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
(cursor = DeserializePodVector(cx, cursor, &heapAccesses)) && (cursor = DeserializePodVector(cx, cursor, &heapAccesses)) &&
(cursor = DeserializePodVector(cx, cursor, &codeRanges)) && (cursor = DeserializePodVector(cx, cursor, &codeRanges)) &&
(cursor = DeserializePodVector(cx, cursor, &callSites)) && (cursor = DeserializePodVector(cx, cursor, &callSites)) &&
(cursor = DeserializePodVector(cx, cursor, &callThunks)) &&
(cursor = DeserializeVector(cx, cursor, &prettyFuncNames)) && (cursor = DeserializeVector(cx, cursor, &prettyFuncNames)) &&
(cursor = filename.deserialize(cx, cursor)); (cursor = filename.deserialize(cx, cursor));
return cursor; return cursor;
@ -562,6 +565,7 @@ ModuleData::clone(JSContext* cx, ModuleData* out) const
ClonePodVector(cx, heapAccesses, &out->heapAccesses) && ClonePodVector(cx, heapAccesses, &out->heapAccesses) &&
ClonePodVector(cx, codeRanges, &out->codeRanges) && ClonePodVector(cx, codeRanges, &out->codeRanges) &&
ClonePodVector(cx, callSites, &out->callSites) && ClonePodVector(cx, callSites, &out->callSites) &&
ClonePodVector(cx, callThunks, &out->callThunks) &&
CloneVector(cx, prettyFuncNames, &out->prettyFuncNames) && CloneVector(cx, prettyFuncNames, &out->prettyFuncNames) &&
filename.clone(cx, &out->filename); filename.clone(cx, &out->filename);
} }
@ -575,6 +579,7 @@ ModuleData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
heapAccesses.sizeOfExcludingThis(mallocSizeOf) + heapAccesses.sizeOfExcludingThis(mallocSizeOf) +
codeRanges.sizeOfExcludingThis(mallocSizeOf) + codeRanges.sizeOfExcludingThis(mallocSizeOf) +
callSites.sizeOfExcludingThis(mallocSizeOf) + callSites.sizeOfExcludingThis(mallocSizeOf) +
callThunks.sizeOfExcludingThis(mallocSizeOf) +
prettyFuncNames.sizeOfExcludingThis(mallocSizeOf) + prettyFuncNames.sizeOfExcludingThis(mallocSizeOf) +
filename.sizeOfExcludingThis(mallocSizeOf); filename.sizeOfExcludingThis(mallocSizeOf);
} }
@ -792,6 +797,9 @@ Module::setProfilingEnabled(JSContext* cx, bool enabled)
for (const CallSite& callSite : module_->callSites) for (const CallSite& callSite : module_->callSites)
EnableProfilingPrologue(*this, callSite, enabled); EnableProfilingPrologue(*this, callSite, enabled);
for (const CallThunk& callThunk : module_->callThunks)
EnableProfilingThunk(*this, callThunk, enabled);
for (const CodeRange& codeRange : module_->codeRanges) for (const CodeRange& codeRange : module_->codeRanges)
EnableProfilingEpilogue(*this, codeRange, enabled); EnableProfilingEpilogue(*this, codeRange, enabled);
} }

View File

@ -67,9 +67,7 @@ struct StaticLinkData
typedef Vector<InternalLink, 0, SystemAllocPolicy> InternalLinkVector; typedef Vector<InternalLink, 0, SystemAllocPolicy> InternalLinkVector;
typedef Vector<uint32_t, 0, SystemAllocPolicy> OffsetVector; typedef Vector<uint32_t, 0, SystemAllocPolicy> OffsetVector;
struct SymbolicLinkArray : mozilla::EnumeratedArray<SymbolicAddress, struct SymbolicLinkArray : EnumeratedArray<SymbolicAddress, SymbolicAddress::Limit, OffsetVector> {
SymbolicAddress::Limit,
OffsetVector> {
WASM_DECLARE_SERIALIZABLE(SymbolicLinkArray) WASM_DECLARE_SERIALIZABLE(SymbolicLinkArray)
}; };
@ -213,7 +211,7 @@ class CodeRange
void assertValid(); void assertValid();
public: public:
enum Kind { Function, Entry, ImportJitExit, ImportInterpExit, Inline }; enum Kind { Function, Entry, ImportJitExit, ImportInterpExit, Inline, CallThunk };
CodeRange() = default; CodeRange() = default;
CodeRange(Kind kind, Offsets offsets); CodeRange(Kind kind, Offsets offsets);
@ -237,7 +235,7 @@ class CodeRange
// which is used for asynchronous profiling to determine the frame pointer. // which is used for asynchronous profiling to determine the frame pointer.
uint32_t profilingReturn() const { uint32_t profilingReturn() const {
MOZ_ASSERT(kind() != Entry && kind() != Inline); MOZ_ASSERT(isFunction() || isImportExit());
return profilingReturn_; return profilingReturn_;
} }
@ -247,6 +245,9 @@ class CodeRange
bool isFunction() const { bool isFunction() const {
return kind() == Function; return kind() == Function;
} }
bool isImportExit() const {
return kind() == ImportJitExit || kind() == ImportInterpExit;
}
uint32_t funcProfilingEntry() const { uint32_t funcProfilingEntry() const {
MOZ_ASSERT(isFunction()); MOZ_ASSERT(isFunction());
return begin(); return begin();
@ -288,6 +289,25 @@ class CodeRange
typedef Vector<CodeRange, 0, SystemAllocPolicy> CodeRangeVector; typedef Vector<CodeRange, 0, SystemAllocPolicy> 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<CallThunk, 0, SystemAllocPolicy> CallThunkVector;
// CacheableChars is used to cacheably store UniqueChars. // CacheableChars is used to cacheably store UniqueChars.
struct CacheableChars : UniqueChars struct CacheableChars : UniqueChars
@ -389,6 +409,7 @@ struct ModuleData : ModuleCacheablePod
HeapAccessVector heapAccesses; HeapAccessVector heapAccesses;
CodeRangeVector codeRanges; CodeRangeVector codeRanges;
CallSiteVector callSites; CallSiteVector callSites;
CallThunkVector callThunks;
CacheableCharsVector prettyFuncNames; CacheableCharsVector prettyFuncNames;
CacheableChars filename; CacheableChars filename;
bool loadedFromCache; bool loadedFromCache;
@ -496,6 +517,7 @@ class Module
CompileArgs compileArgs() const { return module_->compileArgs; } CompileArgs compileArgs() const { return module_->compileArgs; }
const ImportVector& imports() const { return module_->imports; } const ImportVector& imports() const { return module_->imports; }
const ExportVector& exports() const { return module_->exports; } const ExportVector& exports() const { return module_->exports; }
const CodeRangeVector& codeRanges() const { return module_->codeRanges; }
const char* filename() const { return module_->filename.get(); } const char* filename() const { return module_->filename.get(); }
bool loadedFromCache() const { return module_->loadedFromCache; } bool loadedFromCache() const { return module_->loadedFromCache; }
bool staticallyLinked() const { return staticallyLinked_; } bool staticallyLinked() const { return staticallyLinked_; }

View File

@ -19,7 +19,6 @@
#include "asmjs/WasmStubs.h" #include "asmjs/WasmStubs.h"
#include "mozilla/ArrayUtils.h" #include "mozilla/ArrayUtils.h"
#include "mozilla/EnumeratedRange.h"
#include "jit/MacroAssembler-inl.h" #include "jit/MacroAssembler-inl.h"
@ -28,7 +27,6 @@ using namespace js::jit;
using namespace js::wasm; using namespace js::wasm;
using mozilla::ArrayLength; using mozilla::ArrayLength;
using mozilla::MakeEnumeratedRange;
static void static void
AssertStackAlignment(MacroAssembler& masm, uint32_t alignment, uint32_t addBeforeAssert = 0) 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 // The signature of the entry point is Module::CodePtr. The exported wasm
// function has an ABI derived from its specific signature, so this function // 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. // must map from the ABI of CodePtr to the export's signature's ABI.
static bool Offsets
GenerateEntry(ModuleGenerator& mg, unsigned exportIndex) wasm::GenerateEntry(MacroAssembler& masm, unsigned target, const Sig& sig, bool usesHeap)
{ {
MacroAssembler& masm = mg.masm();
const Sig& sig = mg.exportSig(exportIndex);
masm.haltingAlign(CodeAlignment); masm.haltingAlign(CodeAlignment);
Offsets offsets; 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 // ARM, MIPS/MIPS64 and x64 have a globally-pinned HeapReg (x86 uses immediates in
// effective addresses). Loading the heap register depends on the global // effective addresses). Loading the heap register depends on the global
// register already having been loaded. // register already having been loaded.
if (mg.usesHeap()) if (usesHeap)
masm.loadAsmJSHeapRegisterFromGlobalData(); masm.loadAsmJSHeapRegisterFromGlobalData();
// Put the 'argv' argument into a non-argument/return register so that we // 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. // Call into the real function.
masm.assertStackAlignment(AsmJSStackAlignment); masm.assertStackAlignment(AsmJSStackAlignment);
Label target; masm.call(CallSiteDesc(CallSiteDesc::Relative), AsmJSInternalCallee(target));
target.bind(mg.exportEntryOffset(exportIndex));
masm.call(CallSiteDesc(CallSiteDesc::Relative), &target);
// Recover the stack pointer value before dynamic alignment. // Recover the stack pointer value before dynamic alignment.
masm.loadWasmActivation(scratch); masm.loadWasmActivation(scratch);
@ -283,11 +276,8 @@ GenerateEntry(ModuleGenerator& mg, unsigned exportIndex)
masm.move32(Imm32(true), ReturnReg); masm.move32(Imm32(true), ReturnReg);
masm.ret(); masm.ret();
if (masm.oom())
return false;
offsets.end = masm.currentOffset(); offsets.end = masm.currentOffset();
return mg.defineExport(exportIndex, offsets); return offsets;
} }
static void 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 // Generate a stub that is called via the internal ABI derived from the
// signature of the import and calls into an appropriate InvokeImport C++ // signature of the import and calls into an appropriate InvokeImport C++
// function, having boxed all the ABI arguments into a homogeneous Value array. // function, having boxed all the ABI arguments into a homogeneous Value array.
static bool ProfilingOffsets
GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets* offsets) wasm::GenerateInterpExit(MacroAssembler& masm, const Import& import, uint32_t importIndex)
{ {
MacroAssembler& masm = mg.masm(); const Sig& sig = import.sig();
const Sig& sig = *mg.import(importIndex).sig;
masm.setFramePushed(0); masm.setFramePushed(0);
@ -356,7 +345,8 @@ GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffse
unsigned argBytes = Max<size_t>(1, sig.args().length()) * sizeof(Value); unsigned argBytes = Max<size_t>(1, sig.args().length()) * sizeof(Value);
unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, argOffset + argBytes); 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. // Fill the argument array.
unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed(); unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed();
@ -407,7 +397,6 @@ GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffse
case ExprType::I64: case ExprType::I64:
MOZ_CRASH("no int64 in asm.js"); MOZ_CRASH("no int64 in asm.js");
case ExprType::F32: case ExprType::F32:
MOZ_ASSERT(!mg.isAsmJS(), "import can't return float32 in asm.js");
masm.call(SymbolicAddress::InvokeImport_F64); masm.call(SymbolicAddress::InvokeImport_F64);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw); masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
masm.loadDouble(argv, ReturnDoubleReg); masm.loadDouble(argv, ReturnDoubleReg);
@ -426,13 +415,10 @@ GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffse
MOZ_CRASH("Limit"); MOZ_CRASH("Limit");
} }
GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, offsets); GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, &offsets);
if (masm.oom()) offsets.end = masm.currentOffset();
return false; return offsets;
offsets->end = masm.currentOffset();
return true;
} }
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) #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 // Generate a stub that is called via the internal ABI derived from the
// signature of the import and calls into a compatible JIT function, // signature of the import and calls into a compatible JIT function,
// having boxed all the ABI arguments into the JIT stack frame layout. // having boxed all the ABI arguments into the JIT stack frame layout.
static bool ProfilingOffsets
GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets* offsets) wasm::GenerateJitExit(MacroAssembler& masm, const Import& import, bool usesHeap)
{ {
MacroAssembler& masm = mg.masm(); const Sig& sig = import.sig();
const Sig& sig = *mg.import(importIndex).sig;
masm.setFramePushed(0); masm.setFramePushed(0);
@ -465,7 +450,8 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets*
unsigned jitFramePushed = StackDecrementForCall(masm, JitStackAlignment, totalJitFrameBytes) - unsigned jitFramePushed = StackDecrementForCall(masm, JitStackAlignment, totalJitFrameBytes) -
sizeOfRetAddr; sizeOfRetAddr;
GenerateExitPrologue(masm, jitFramePushed, ExitReason::ImportJit, offsets); ProfilingOffsets offsets;
GenerateExitPrologue(masm, jitFramePushed, ExitReason::ImportJit, &offsets);
// 1. Descriptor // 1. Descriptor
size_t argOffset = 0; size_t argOffset = 0;
@ -479,7 +465,7 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets*
Register scratch = ABIArgGenerator::NonArgReturnReg1; // repeatedly clobbered Register scratch = ABIArgGenerator::NonArgReturnReg1; // repeatedly clobbered
// 2.1. Get ExitDatum // 2.1. Get ExitDatum
unsigned globalDataOffset = mg.import(importIndex).globalDataOffset; uint32_t globalDataOffset = import.exitGlobalDataOffset();
#if defined(JS_CODEGEN_X64) #if defined(JS_CODEGEN_X64)
masm.append(AsmJSGlobalAccess(masm.leaRipRelative(callee), globalDataOffset)); masm.append(AsmJSGlobalAccess(masm.leaRipRelative(callee), globalDataOffset));
#elif defined(JS_CODEGEN_X86) #elif defined(JS_CODEGEN_X86)
@ -663,7 +649,6 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets*
case ExprType::I64: case ExprType::I64:
MOZ_CRASH("no int64 in asm.js"); MOZ_CRASH("no int64 in asm.js");
case ExprType::F32: case ExprType::F32:
MOZ_ASSERT(!mg.isAsmJS(), "import can't return float32 in asm.js");
masm.convertValueToFloat(JSReturnOperand, ReturnFloat32Reg, &oolConvert); masm.convertValueToFloat(JSReturnOperand, ReturnFloat32Reg, &oolConvert);
break; break;
case ExprType::F64: case ExprType::F64:
@ -682,10 +667,10 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets*
// Ion code does not respect system callee-saved register conventions so // Ion code does not respect system callee-saved register conventions so
// reload the heap register. // reload the heap register.
if (mg.usesHeap()) if (usesHeap)
masm.loadAsmJSHeapRegisterFromGlobalData(); masm.loadAsmJSHeapRegisterFromGlobalData();
GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::ImportJit, offsets); GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::ImportJit, &offsets);
if (oolConvert.used()) { if (oolConvert.used()) {
masm.bind(&oolConvert); masm.bind(&oolConvert);
@ -728,7 +713,6 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets*
masm.loadDouble(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnDoubleReg); masm.loadDouble(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnDoubleReg);
break; break;
case ExprType::F32: case ExprType::F32:
MOZ_ASSERT(!mg.isAsmJS(), "import can't return float32 in asm.js");
masm.call(SymbolicAddress::CoerceInPlace_ToNumber); masm.call(SymbolicAddress::CoerceInPlace_ToNumber);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw); masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
masm.loadDouble(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnDoubleReg); masm.loadDouble(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnDoubleReg);
@ -744,37 +728,18 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets*
MOZ_ASSERT(masm.framePushed() == 0); MOZ_ASSERT(masm.framePushed() == 0);
if (masm.oom()) offsets.end = masm.currentOffset();
return false; return offsets;
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);
}
} }
// Generate a stub that is called immediately after the prologue when there is a // 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 // stack overflow. This stub calls a C++ function to report the error and then
// jumps to the throw stub to pop the activation. // jumps to the throw stub to pop the activation.
static bool static Offsets
GenerateStackOverflowStub(ModuleGenerator& mg) GenerateStackOverflow(MacroAssembler& masm)
{ {
MacroAssembler& masm = mg.masm();
masm.haltingAlign(CodeAlignment); masm.haltingAlign(CodeAlignment);
if (masm.jumpSites()[JumpTarget::StackOverflow].empty())
return true;
BindJumps(masm, JumpTarget::StackOverflow);
Offsets offsets; Offsets offsets;
offsets.begin = masm.currentOffset(); offsets.begin = masm.currentOffset();
@ -797,27 +762,18 @@ GenerateStackOverflowStub(ModuleGenerator& mg)
masm.call(SymbolicAddress::ReportOverRecursed); masm.call(SymbolicAddress::ReportOverRecursed);
masm.jump(JumpTarget::Throw); masm.jump(JumpTarget::Throw);
if (masm.oom())
return false;
offsets.end = masm.currentOffset(); 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 // 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 // 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. // error and then jumps to the throw stub to pop the activation.
static bool static Offsets
GenerateConversionErrorStub(ModuleGenerator& mg) GenerateConversionError(MacroAssembler& masm)
{ {
MacroAssembler& masm = mg.masm();
masm.haltingAlign(CodeAlignment); masm.haltingAlign(CodeAlignment);
if (masm.jumpSites()[JumpTarget::ConversionError].empty())
return true;
BindJumps(masm, JumpTarget::ConversionError);
Offsets offsets; Offsets offsets;
offsets.begin = masm.currentOffset(); offsets.begin = masm.currentOffset();
@ -830,28 +786,18 @@ GenerateConversionErrorStub(ModuleGenerator& mg)
masm.call(SymbolicAddress::OnImpreciseConversion); masm.call(SymbolicAddress::OnImpreciseConversion);
masm.jump(JumpTarget::Throw); masm.jump(JumpTarget::Throw);
if (masm.oom())
return false;
offsets.end = masm.currentOffset(); 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 // 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 // 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. // error and then jumps to the throw stub to pop the activation.
static bool static Offsets
GenerateOutOfBoundsStub(ModuleGenerator& mg) GenerateOutOfBounds(MacroAssembler& masm)
{ {
MacroAssembler& masm = mg.masm();
masm.haltingAlign(CodeAlignment); 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 offsets;
offsets.begin = masm.currentOffset(); offsets.begin = masm.currentOffset();
@ -864,11 +810,54 @@ GenerateOutOfBoundsStub(ModuleGenerator& mg)
masm.call(SymbolicAddress::OnOutOfBounds); masm.call(SymbolicAddress::OnOutOfBounds);
masm.jump(JumpTarget::Throw); masm.jump(JumpTarget::Throw);
if (masm.oom()) offsets.end = masm.currentOffset();
return false; 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(); 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( static const LiveRegisterSet AllRegsExceptSP(
@ -884,16 +873,11 @@ static const LiveRegisterSet AllRegsExceptSP(
// Unfortunately, loading this requires a scratch register which we don't have // 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 // after restoring all registers. To hack around this, push the resumePC on the
// stack so that it can be popped directly into PC. // stack so that it can be popped directly into PC.
static bool Offsets
GenerateInterruptStub(ModuleGenerator& mg) wasm::GenerateInterruptStub(MacroAssembler& masm)
{ {
MacroAssembler& masm = mg.masm();
masm.haltingAlign(CodeAlignment); 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 offsets;
offsets.begin = masm.currentOffset(); offsets.begin = masm.currentOffset();
@ -1040,89 +1024,6 @@ GenerateInterruptStub(ModuleGenerator& mg)
# error "Unknown architecture!" # error "Unknown architecture!"
#endif #endif
if (masm.oom())
return false;
offsets.end = masm.currentOffset(); 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);
}

View File

@ -24,8 +24,20 @@
namespace js { namespace js {
namespace wasm { namespace wasm {
bool extern Offsets
GenerateStubs(ModuleGenerator& mg); 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 wasm
} // namespace js } // namespace js

View File

@ -38,6 +38,7 @@ class PropertyName;
namespace wasm { namespace wasm {
using mozilla::EnumeratedArray;
using mozilla::Move; using mozilla::Move;
using mozilla::DebugOnly; using mozilla::DebugOnly;
using mozilla::MallocSizeOf; using mozilla::MallocSizeOf;
@ -266,7 +267,7 @@ typedef Vector<const DeclaredSig*, 0, SystemAllocPolicy> DeclaredSigPtrVector;
struct Offsets 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) : begin(begin), end(end)
{} {}
@ -571,7 +572,7 @@ enum class JumpTarget
Limit Limit
}; };
typedef mozilla::EnumeratedArray<JumpTarget, JumpTarget::Limit, Uint32Vector> JumpSiteArray; typedef EnumeratedArray<JumpTarget, JumpTarget::Limit, Uint32Vector> JumpSiteArray;
// The CompileArgs struct captures global parameters that affect all wasm code // The CompileArgs struct captures global parameters that affect all wasm code
// generation. It also currently is the single source of truth for whether or // generation. It also currently is the single source of truth for whether or

Some files were not shown because too many files have changed in this diff Show More