зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
bd81ddd0b0
|
@ -10,8 +10,6 @@ var testData = [
|
|||
[ 'http://example.com/search?oe=utf-8', 'q', 'http://example.com/search?oe=utf-8&q=%s' ],
|
||||
];
|
||||
|
||||
var mm = gBrowser.selectedBrowser.messageManager;
|
||||
|
||||
add_task(function*() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
|
@ -23,6 +21,8 @@ add_task(function*() {
|
|||
doc.head.appendChild(base);
|
||||
});
|
||||
|
||||
var mm = browser.messageManager;
|
||||
|
||||
for (let [baseURI, fieldName, expected] of testData) {
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(document.getElementById("contentAreaContextMenu"),
|
||||
"popupshown");
|
||||
|
|
|
@ -8,14 +8,32 @@ var testPage3 = "<html id='html3'><body id='body3'><button id='button3'>Tab 3</b
|
|||
|
||||
const fm = Services.focus;
|
||||
|
||||
function EventStore() {
|
||||
this["main-window"] = [];
|
||||
this["window1"] = [];
|
||||
this["window2"] = [];
|
||||
};
|
||||
|
||||
EventStore.prototype = {
|
||||
"push": function (event) {
|
||||
if (event.indexOf("1") > -1) {
|
||||
this["window1"].push(event);
|
||||
} else if (event.indexOf("2") > -1) {
|
||||
this["window2"].push(event);
|
||||
} else {
|
||||
this["main-window"].push(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tab1 = null;
|
||||
var tab2 = null;
|
||||
var browser1 = null;
|
||||
var browser2 = null;
|
||||
var _browser_tabfocus_test_lastfocus;
|
||||
var _browser_tabfocus_test_lastfocuswindow = null;
|
||||
var actualEvents = [];
|
||||
var expectedEvents = [];
|
||||
var _lastfocus;
|
||||
var _lastfocuswindow = null;
|
||||
var actualEvents = new EventStore();
|
||||
var expectedEvents = new EventStore();
|
||||
var currentTestName = "";
|
||||
var _expectedElement = null;
|
||||
var _expectedWindow = null;
|
||||
|
@ -138,8 +156,8 @@ add_task(function*() {
|
|||
});
|
||||
}
|
||||
|
||||
_browser_tabfocus_test_lastfocus = "urlbar";
|
||||
_browser_tabfocus_test_lastfocuswindow = "main-window";
|
||||
_lastfocus = "urlbar";
|
||||
_lastfocuswindow = "main-window";
|
||||
|
||||
window.addEventListener("focus", _browser_tabfocus_test_eventOccured, true);
|
||||
window.addEventListener("blur", _browser_tabfocus_test_eventOccured, true);
|
||||
|
@ -329,9 +347,9 @@ add_task(function*() {
|
|||
// Document navigation with F6 does not yet work in mutli-process browsers.
|
||||
if (!gMultiProcessBrowser) {
|
||||
gURLBar.focus();
|
||||
actualEvents = [];
|
||||
_browser_tabfocus_test_lastfocus = "urlbar";
|
||||
_browser_tabfocus_test_lastfocuswindow = "main-window";
|
||||
actualEvents = new EventStore();
|
||||
_lastfocus = "urlbar";
|
||||
_lastfocuswindow = "main-window";
|
||||
|
||||
yield expectFocusShift(() => EventUtils.synthesizeKey("VK_F6", { }),
|
||||
"window1", "html1",
|
||||
|
@ -416,26 +434,19 @@ function compareFocusResults()
|
|||
if (!currentPromiseResolver)
|
||||
return;
|
||||
|
||||
if (actualEvents.length < expectedEvents.length)
|
||||
return;
|
||||
let winIds = ["main-window", "window1", "window2"];
|
||||
|
||||
for (let e = 0; e < expectedEvents.length; e++) {
|
||||
// When remote browsers are used, the child process events can occur before or after the
|
||||
// browser focusing. Allow either order.
|
||||
if (gMultiProcessBrowser && actualEvents[e] != expectedEvents[e] &&
|
||||
actualEvents[e].startsWith("focus: browser")) {
|
||||
|
||||
// Ensure that this event is expected later, and remove the later expected event and
|
||||
// reinsert it at the current index.
|
||||
let foundLaterIndex = expectedEvents.indexOf(actualEvents[e], e + 1);
|
||||
if (foundLaterIndex > e) {
|
||||
expectedEvents.splice(e, 0, expectedEvents.splice(foundLaterIndex, 1)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
is(actualEvents[e], expectedEvents[e], currentTestName + " events [event " + e + "]");
|
||||
for (let winId of winIds) {
|
||||
if (actualEvents[winId].length < expectedEvents[winId].length)
|
||||
return;
|
||||
}
|
||||
|
||||
for (let winId of winIds) {
|
||||
for (let e = 0; e < expectedEvents.length; e++) {
|
||||
is(actualEvents[winId][e], expectedEvents[winId][e], currentTestName + " events [event " + e + "]");
|
||||
}
|
||||
actualEvents[winId] = [];
|
||||
}
|
||||
actualEvents = [];
|
||||
|
||||
// Use executeSoon as this will be called during a focus/blur event handler
|
||||
executeSoon(() => {
|
||||
|
@ -480,7 +491,7 @@ function* expectFocusShift(callback, expectedWindow, expectedElement, focusChang
|
|||
currentPromiseResolver = null;
|
||||
currentTestName = testid;
|
||||
|
||||
expectedEvents = [];
|
||||
expectedEvents = new EventStore();
|
||||
|
||||
if (focusChanged) {
|
||||
_expectedElement = expectedElement;
|
||||
|
@ -498,34 +509,34 @@ function* expectFocusShift(callback, expectedWindow, expectedElement, focusChang
|
|||
_expectedWindow = "main-window";
|
||||
}
|
||||
|
||||
if (gMultiProcessBrowser && _browser_tabfocus_test_lastfocuswindow != "main-window" &&
|
||||
_browser_tabfocus_test_lastfocuswindow != expectedWindow) {
|
||||
let browserid = _browser_tabfocus_test_lastfocuswindow == "window1" ? "browser1" : "browser2";
|
||||
if (gMultiProcessBrowser && _lastfocuswindow != "main-window" &&
|
||||
_lastfocuswindow != expectedWindow) {
|
||||
let browserid = _lastfocuswindow == "window1" ? "browser1" : "browser2";
|
||||
expectedEvents.push("blur: " + browserid);
|
||||
}
|
||||
|
||||
var newElementIsFocused = (expectedElement && !expectedElement.startsWith("html"));
|
||||
if (newElementIsFocused && gMultiProcessBrowser &&
|
||||
_browser_tabfocus_test_lastfocuswindow != "main-window" &&
|
||||
_lastfocuswindow != "main-window" &&
|
||||
expectedWindow == "main-window") {
|
||||
// When switching from a child to a chrome element, the focus on the element will arrive first.
|
||||
expectedEvents.push("focus: " + expectedElement);
|
||||
newElementIsFocused = false;
|
||||
}
|
||||
|
||||
if (_browser_tabfocus_test_lastfocus && _browser_tabfocus_test_lastfocus != _expectedElement)
|
||||
expectedEvents.push("blur: " + _browser_tabfocus_test_lastfocus);
|
||||
if (_lastfocus && _lastfocus != _expectedElement)
|
||||
expectedEvents.push("blur: " + _lastfocus);
|
||||
|
||||
if (_browser_tabfocus_test_lastfocuswindow &&
|
||||
_browser_tabfocus_test_lastfocuswindow != expectedWindow) {
|
||||
if (_lastfocuswindow &&
|
||||
_lastfocuswindow != expectedWindow) {
|
||||
|
||||
if (!gMultiProcessBrowser || _browser_tabfocus_test_lastfocuswindow != "main-window") {
|
||||
expectedEvents.push("blur: " + _browser_tabfocus_test_lastfocuswindow + "-document");
|
||||
expectedEvents.push("blur: " + _browser_tabfocus_test_lastfocuswindow + "-window");
|
||||
if (!gMultiProcessBrowser || _lastfocuswindow != "main-window") {
|
||||
expectedEvents.push("blur: " + _lastfocuswindow + "-document");
|
||||
expectedEvents.push("blur: " + _lastfocuswindow + "-window");
|
||||
}
|
||||
}
|
||||
|
||||
if (expectedWindow && _browser_tabfocus_test_lastfocuswindow != expectedWindow) {
|
||||
if (expectedWindow && _lastfocuswindow != expectedWindow) {
|
||||
if (gMultiProcessBrowser && expectedWindow != "main-window") {
|
||||
let browserid = expectedWindow == "window1" ? "browser1" : "browser2";
|
||||
expectedEvents.push("focus: " + browserid);
|
||||
|
@ -541,8 +552,8 @@ function* expectFocusShift(callback, expectedWindow, expectedElement, focusChang
|
|||
expectedEvents.push("focus: " + expectedElement);
|
||||
}
|
||||
|
||||
_browser_tabfocus_test_lastfocus = expectedElement;
|
||||
_browser_tabfocus_test_lastfocuswindow = expectedWindow;
|
||||
_lastfocus = expectedElement;
|
||||
_lastfocuswindow = expectedWindow;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -550,7 +561,7 @@ function* expectFocusShift(callback, expectedWindow, expectedElement, focusChang
|
|||
callback();
|
||||
|
||||
// No events are expected, so resolve the promise immediately.
|
||||
if (!expectedEvents.length) {
|
||||
if (expectedEvents["main-window"].length + expectedEvents["window1"].length + expectedEvents["window2"].length == 0) {
|
||||
currentPromiseResolver();
|
||||
currentPromiseResolver = null;
|
||||
}
|
||||
|
|
|
@ -8,10 +8,14 @@ requestLongerTimeout(10);
|
|||
const PAGE_1 = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
|
||||
const PAGE_2 = "data:text/html,<html><body>Another%20regular,%20everyday,%20normal%20page.";
|
||||
|
||||
// Turn off tab animations for testing
|
||||
Services.prefs.setBoolPref("browser.tabs.animate", false);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("browser.tabs.animate");
|
||||
// Turn off tab animations for testing and use a single content process
|
||||
// for these tests since we want to test tabs within the crashing process here.
|
||||
add_task(function* test_initialize() {
|
||||
yield SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[ "dom.ipc.processCount", 1 ],
|
||||
[ "browser.tabs.animate", false]
|
||||
] });
|
||||
});
|
||||
|
||||
// Allow tabs to restore on demand so we can test pending states
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
},
|
||||
{
|
||||
"version": "Visual Studio 2015 Update 2 / SDK 10.0.10586.0/212",
|
||||
"size": 332343834,
|
||||
"digest": "55814aaabcd4aa51fe85918ec02a8c29bc067d41ee79ddcfd628daaba5a06d4241a73a51bf5a8bc69cc762b52551009f44b05e65682c45b4684c17fb2d017c2c",
|
||||
"size": 332442800,
|
||||
"digest": "995394a4a515c7cb0f8595f26f5395361a638870dd0bbfcc22193fe1d98a0c47126057d5999cc494f3f3eac5cb49160e79757c468f83ee5797298e286ef6252c",
|
||||
"algorithm": "sha512",
|
||||
"filename": "vs2015u2.zip",
|
||||
"unpack": true
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
},
|
||||
{
|
||||
"version": "Visual Studio 2015 Update 2 / SDK 10.0.10586.0/212",
|
||||
"size": 332343834,
|
||||
"digest": "55814aaabcd4aa51fe85918ec02a8c29bc067d41ee79ddcfd628daaba5a06d4241a73a51bf5a8bc69cc762b52551009f44b05e65682c45b4684c17fb2d017c2c",
|
||||
"size": 332442800,
|
||||
"digest": "995394a4a515c7cb0f8595f26f5395361a638870dd0bbfcc22193fe1d98a0c47126057d5999cc494f3f3eac5cb49160e79757c468f83ee5797298e286ef6252c",
|
||||
"algorithm": "sha512",
|
||||
"filename": "vs2015u2.zip",
|
||||
"unpack": true
|
||||
|
|
|
@ -25,7 +25,6 @@ case "$target" in
|
|||
if test -z "$CXX"; then CXX=cl; fi
|
||||
if test -z "$CPP"; then CPP="$CC -E -nologo"; fi
|
||||
if test -z "$CXXCPP"; then CXXCPP="$CXX -TP -E -nologo"; ac_cv_prog_CXXCPP="$CXXCPP"; fi
|
||||
if test -z "$LD"; then LD=link; fi
|
||||
if test -z "$AS"; then
|
||||
case "${target_cpu}" in
|
||||
i*86)
|
||||
|
|
|
@ -61,6 +61,49 @@ def is_absolute_or_relative(path):
|
|||
def normsep(path):
|
||||
return mozpath.normsep(path)
|
||||
|
||||
|
||||
@imports('ctypes')
|
||||
@imports(_from='ctypes', _import='wintypes')
|
||||
def get_GetShortPathNameW():
|
||||
GetShortPathNameW = ctypes.windll.kernel32.GetShortPathNameW
|
||||
GetShortPathNameW.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR,
|
||||
wintypes.DWORD]
|
||||
GetShortPathNameW.restype = wintypes.DWORD
|
||||
return GetShortPathNameW
|
||||
|
||||
|
||||
@template
|
||||
@imports('ctypes')
|
||||
@imports('platform')
|
||||
@imports(_from='mozbuild.shellutil', _import='quote')
|
||||
def normalize_path():
|
||||
# Until the build system can properly handle programs that need quoting,
|
||||
# transform those paths into their short version on Windows (e.g.
|
||||
# c:\PROGRA~1...).
|
||||
if platform.system() == 'Windows':
|
||||
GetShortPathNameW = get_GetShortPathNameW()
|
||||
|
||||
def normalize_path(path):
|
||||
path = normsep(path)
|
||||
if quote(path) == path:
|
||||
return path
|
||||
size = 0
|
||||
while True:
|
||||
out = ctypes.create_unicode_buffer(size)
|
||||
needed = GetShortPathNameW(path, out, size)
|
||||
if size >= needed:
|
||||
return normsep(out.value)
|
||||
size = needed
|
||||
|
||||
else:
|
||||
def normalize_path(path):
|
||||
return normsep(path)
|
||||
|
||||
return normalize_path
|
||||
|
||||
normalize_path = normalize_path()
|
||||
|
||||
|
||||
# Locates the given program using which, or returns the given path if it
|
||||
# exists.
|
||||
# The `paths` parameter may be passed to search the given paths instead of
|
||||
|
@ -72,15 +115,15 @@ def normsep(path):
|
|||
def find_program(file, paths=None):
|
||||
try:
|
||||
if is_absolute_or_relative(file):
|
||||
return normsep(which(os.path.basename(file),
|
||||
[os.path.dirname(file)]))
|
||||
return normalize_path(which(os.path.basename(file),
|
||||
[os.path.dirname(file)]))
|
||||
if paths:
|
||||
if not isinstance(paths, (list, tuple)):
|
||||
die("Paths provided to find_program must be a list of strings, "
|
||||
"not %r", paths)
|
||||
paths = list(itertools.chain(
|
||||
*(p.split(pathsep) for p in paths if p)))
|
||||
return normsep(which(file, path=paths))
|
||||
return normalize_path(which(file, path=paths))
|
||||
except WhichError:
|
||||
return None
|
||||
|
||||
|
|
|
@ -50,31 +50,48 @@ def windows_sdk_dir(value, host):
|
|||
# The Windows SDK 8.1 and 10 have different layouts. The former has
|
||||
# $SDK/include/$subdir, while the latter has $SDK/include/$version/$subdir.
|
||||
# The vcvars* scripts don't actually care about the version, they just take
|
||||
# the last.
|
||||
# the last alphanumerically.
|
||||
# The $SDK/lib directories always have version subdirectories, but while the
|
||||
# versions match the one in $SDK/include for SDK 10, it's "winv6.3" for SDK
|
||||
# 8.1.
|
||||
@imports('os')
|
||||
@imports('re')
|
||||
@imports(_from='__builtin__', _import='sorted')
|
||||
@imports(_from='__builtin__', _import='WindowsError')
|
||||
def get_include_dir(sdk, subdir):
|
||||
base = os.path.join(sdk, 'include')
|
||||
try:
|
||||
subdirs = [d for d in os.listdir(base)
|
||||
if os.path.isdir(os.path.join(base, d))]
|
||||
except WindowsError:
|
||||
subdirs = []
|
||||
if not subdirs:
|
||||
return None
|
||||
if subdir in subdirs:
|
||||
return os.path.join(base, subdir)
|
||||
# At this point, either we have an incomplete or invalid SDK directory,
|
||||
# or we exclusively have version numbers in subdirs.
|
||||
versions = sorted((Version(d) for d in subdirs), reverse=True)
|
||||
# Version('non-number').major is 0, so if the biggest version we have is
|
||||
# 0, we have a problem.
|
||||
if versions[0].major == 0:
|
||||
return None
|
||||
path = os.path.join(base, str(versions[0]), subdir)
|
||||
return path if os.path.isdir(path) else None
|
||||
def get_sdk_dirs(sdk, subdir):
|
||||
def get_dirs_containing(sdk, stem, subdir):
|
||||
base = os.path.join(sdk, stem)
|
||||
try:
|
||||
subdirs = [d for d in os.listdir(base)
|
||||
if os.path.isdir(os.path.join(base, d))]
|
||||
except WindowsError:
|
||||
subdirs = []
|
||||
if not subdirs:
|
||||
return ()
|
||||
if subdir in subdirs:
|
||||
return (base,)
|
||||
# At this point, either we have an incomplete or invalid SDK directory,
|
||||
# or we exclusively have version numbers in subdirs.
|
||||
return tuple(os.path.join(base, s) for s in subdirs
|
||||
if os.path.isdir(os.path.join(base, s, subdir)))
|
||||
|
||||
def categorize(dirs):
|
||||
return {os.path.basename(d): d for d in dirs}
|
||||
|
||||
include_dirs = categorize(get_dirs_containing(sdk, 'include', subdir))
|
||||
lib_dirs = categorize(get_dirs_containing(sdk, 'lib', subdir))
|
||||
|
||||
if 'include' in include_dirs:
|
||||
include_dirs['winv6.3'] = include_dirs['include']
|
||||
del include_dirs['include']
|
||||
|
||||
valid_versions = sorted(set(include_dirs) & set(lib_dirs), reverse=True)
|
||||
if valid_versions:
|
||||
return namespace(
|
||||
path=sdk,
|
||||
lib=lib_dirs[valid_versions[0]],
|
||||
include=include_dirs[valid_versions[0]],
|
||||
)
|
||||
|
||||
|
||||
@imports(_from='mozbuild.shellutil', _import='quote')
|
||||
|
@ -93,13 +110,14 @@ def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version,
|
|||
windows_sdk_dir_env = windows_sdk_dir_env[0]
|
||||
sdks = {}
|
||||
for d in windows_sdk_dir:
|
||||
um_dir = get_include_dir(d, 'um')
|
||||
shared_dir = get_include_dir(d, 'shared')
|
||||
if um_dir and shared_dir:
|
||||
sdk = get_sdk_dirs(d, 'um')
|
||||
if sdk:
|
||||
check = dedent('''\
|
||||
#include <winsdkver.h>
|
||||
WINVER_MAXVER
|
||||
''')
|
||||
um_dir = os.path.join(sdk.include, 'um')
|
||||
shared_dir = os.path.join(sdk.include, 'shared')
|
||||
result = try_preprocess(compiler.wrapper + [compiler.compiler] +
|
||||
compiler.flags +
|
||||
['-I', um_dir, '-I', shared_dir], 'C',
|
||||
|
@ -111,7 +129,7 @@ def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version,
|
|||
except:
|
||||
pass
|
||||
else:
|
||||
sdks[d] = maxver
|
||||
sdks[d] = maxver, sdk
|
||||
continue
|
||||
if d == windows_sdk_dir_env:
|
||||
raise FatalCheckError(
|
||||
|
@ -119,9 +137,9 @@ def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version,
|
|||
'WINDOWSSDKDIR (%s). Please verify it contains a valid and '
|
||||
'complete SDK installation.' % windows_sdk_dir_env)
|
||||
|
||||
valid_sdks = sorted(sdks, key=lambda x: sdks[x], reverse=True)
|
||||
valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True)
|
||||
if valid_sdks:
|
||||
biggest_version = sdks[valid_sdks[0]]
|
||||
biggest_version, sdk = sdks[valid_sdks[0]]
|
||||
if not valid_sdks or biggest_version < target_version:
|
||||
if windows_sdk_dir_env:
|
||||
raise FatalCheckError(
|
||||
|
@ -137,7 +155,9 @@ def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version,
|
|||
'Cannot find a Windows SDK for version >= 0x%04x.' % target_version)
|
||||
|
||||
return namespace(
|
||||
path=valid_sdks[0],
|
||||
path=sdk.path,
|
||||
include=sdk.include,
|
||||
lib=sdk.lib,
|
||||
version=biggest_version,
|
||||
)
|
||||
|
||||
|
@ -151,6 +171,166 @@ add_old_configure_assignment(
|
|||
lambda x: '0x%04X0000' % x.version if x else None))
|
||||
|
||||
|
||||
@imports(_from='mozbuild.shellutil', _import='quote')
|
||||
def valid_ucrt_sdk_dir_result(value):
|
||||
if value:
|
||||
return '%s in %s' % (value.version, quote(value.path))
|
||||
|
||||
@depends_win(windows_sdk_dir, 'WINDOWSSDKDIR')
|
||||
@checking('for Universal CRT SDK', valid_ucrt_sdk_dir_result)
|
||||
@imports(_from='__builtin__', _import='sorted')
|
||||
def valid_ucrt_sdk_dir(windows_sdk_dir, windows_sdk_dir_env):
|
||||
if windows_sdk_dir_env:
|
||||
windows_sdk_dir_env = windows_sdk_dir_env[0]
|
||||
sdks = {}
|
||||
for d in windows_sdk_dir:
|
||||
sdk = get_sdk_dirs(d, 'ucrt')
|
||||
if sdk:
|
||||
version = os.path.basename(sdk.include)
|
||||
# We're supposed to always find a version in the directory, because
|
||||
# the 8.1 SDK, which doesn't have a version in the directory, doesn't
|
||||
# contain the Universal CRT SDK. When the main SDK is 8.1, there
|
||||
# is, however, supposed to be a reduced install of the SDK 10
|
||||
# with the UCRT.
|
||||
if version != 'include':
|
||||
sdks[d] = Version(version), sdk
|
||||
continue
|
||||
if d == windows_sdk_dir_env:
|
||||
raise FatalCheckError(
|
||||
'The SDK in WINDOWSSDKDIR (%s) does not contain the Universal '
|
||||
'CRT.' % windows_sdk_dir_env)
|
||||
|
||||
valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True)
|
||||
if not valid_sdks:
|
||||
raise FatalCheckError('Cannot find the Universal CRT SDK. '
|
||||
'Please install it.')
|
||||
|
||||
version, sdk = sdks[valid_sdks[0]]
|
||||
|
||||
return namespace(
|
||||
path=sdk.path,
|
||||
include=sdk.include,
|
||||
lib=sdk.lib,
|
||||
version=version,
|
||||
)
|
||||
|
||||
|
||||
@depends_win(c_compiler)
|
||||
@imports('os')
|
||||
def vc_path(c_compiler):
|
||||
if c_compiler.type != 'msvc':
|
||||
return
|
||||
# Normally, we'd start from c_compiler.compiler, but for now, it's not the
|
||||
# ideal full path to the compiler. At least, we're guaranteed find_program
|
||||
# will get us the one we found in toolchain.configure.
|
||||
cl = find_program(c_compiler.compiler)
|
||||
result = os.path.dirname(cl)
|
||||
while True:
|
||||
next, p = os.path.split(result)
|
||||
if next == result:
|
||||
die('Cannot determine the Visual C++ directory the compiler (%s) '
|
||||
'is in' % cl)
|
||||
result = next
|
||||
if p.lower() == 'bin':
|
||||
break
|
||||
return result
|
||||
|
||||
|
||||
@depends_win(vc_path)
|
||||
@checking('for the Debug Interface Access SDK', lambda x: x or 'not found')
|
||||
def dia_sdk_dir(vc_path):
|
||||
if vc_path:
|
||||
path = os.path.join(os.path.dirname(vc_path), 'DIA SDK')
|
||||
if os.path.isdir(path):
|
||||
return path
|
||||
|
||||
|
||||
@depends_win(vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_dir)
|
||||
@imports('os')
|
||||
def include_path(vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk_dir):
|
||||
if not vc_path:
|
||||
return
|
||||
atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'include')
|
||||
if not os.path.isdir(atlmfc_dir):
|
||||
die('Cannot find the ATL/MFC headers in the Visual C++ directory (%s). '
|
||||
'Please install them.' % vc_path)
|
||||
|
||||
winrt_dir = os.path.join(windows_sdk_dir.include, 'winrt')
|
||||
if not os.path.isdir(winrt_dir):
|
||||
die('Cannot find the WinRT headers in the Windows SDK directory (%s). '
|
||||
'Please install them.' % windows_sdk_dir.path)
|
||||
|
||||
includes = []
|
||||
include_env = os.environ.get('INCLUDE')
|
||||
if include_env:
|
||||
includes.append(include_env)
|
||||
includes.extend((
|
||||
os.path.join(vc_path, 'include'),
|
||||
atlmfc_dir,
|
||||
os.path.join(windows_sdk_dir.include, 'shared'),
|
||||
os.path.join(windows_sdk_dir.include, 'um'),
|
||||
winrt_dir,
|
||||
os.path.join(ucrt_sdk_dir.include, 'ucrt'),
|
||||
))
|
||||
if dia_sdk_dir:
|
||||
includes.append(os.path.join(dia_sdk_dir, 'include'))
|
||||
# Set in the environment for old-configure
|
||||
includes = os.pathsep.join(includes)
|
||||
os.environ['INCLUDE'] = includes
|
||||
return includes
|
||||
|
||||
set_config('INCLUDE', include_path)
|
||||
|
||||
|
||||
@depends_win(target, vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_dir)
|
||||
@imports('os')
|
||||
def lib_path(target, vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk_dir):
|
||||
if not vc_path:
|
||||
return
|
||||
vc_target = {
|
||||
'x86': '',
|
||||
'x86_64': 'amd64',
|
||||
'arm': 'arm',
|
||||
}.get(target.cpu)
|
||||
if vc_target is None:
|
||||
return
|
||||
# As vc_target can be '', and os.path.join will happily use the empty
|
||||
# string, leading to a string ending with a backslash, that Make will
|
||||
# interpret as a "string continues on next line" indicator, use variable
|
||||
# args.
|
||||
vc_target = (vc_target,) if vc_target else ()
|
||||
sdk_target = {
|
||||
'x86': 'x86',
|
||||
'x86_64': 'x64',
|
||||
'arm': 'arm',
|
||||
}.get(target.cpu)
|
||||
|
||||
atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'lib', *vc_target)
|
||||
if not os.path.isdir(atlmfc_dir):
|
||||
die('Cannot find the ATL/MFC libraries in the Visual C++ directory (%s). '
|
||||
'Please install them.' % vc_path)
|
||||
|
||||
|
||||
libs = []
|
||||
lib_env = os.environ.get('LIB')
|
||||
if lib_env:
|
||||
libs.append(lib_env)
|
||||
libs.extend((
|
||||
os.path.join(vc_path, 'lib', *vc_target),
|
||||
atlmfc_dir,
|
||||
os.path.join(windows_sdk_dir.lib, 'um', sdk_target),
|
||||
os.path.join(ucrt_sdk_dir.lib, 'ucrt', sdk_target),
|
||||
))
|
||||
if dia_sdk_dir:
|
||||
libs.append(os.path.join(dia_sdk_dir, 'lib', *vc_target))
|
||||
# Set in the environment for old-configure
|
||||
libs = os.pathsep.join(libs)
|
||||
os.environ['LIB'] = libs
|
||||
return libs
|
||||
|
||||
set_config('LIB', lib_path)
|
||||
|
||||
|
||||
option(env='MT', nargs=1, help='Path to the Microsoft Manifest Tool')
|
||||
|
||||
@depends_win(valid_windows_sdk_dir)
|
||||
|
@ -175,10 +355,8 @@ def sdk_bin_path(valid_windows_sdk_dir):
|
|||
return result
|
||||
|
||||
|
||||
# Normally, we'd use `MT` instead of `_MT`, but for now, we want MT to only contain
|
||||
# mt.exe.
|
||||
mt = check_prog('_MT', depends_win()(lambda: ('mt.exe',)), what='mt',
|
||||
input='MT', paths=sdk_bin_path)
|
||||
mt = check_prog('MT', depends_win()(lambda: ('mt.exe',)), input='MT',
|
||||
paths=sdk_bin_path)
|
||||
|
||||
|
||||
# Check that MT is not something unexpected like "magnetic tape manipulation
|
||||
|
@ -198,10 +376,19 @@ def valid_mt(path):
|
|||
raise FatalCheckError('%s is not Microsoft Manifest Tool')
|
||||
|
||||
|
||||
set_config('MT', depends_if(valid_mt)(lambda x: os.path.basename(x)))
|
||||
set_config('MSMANIFEST_TOOL', depends(valid_mt)(lambda x: bool(x)))
|
||||
|
||||
|
||||
# Ultimately, this will move to toolchain.configure and be turned into a
|
||||
# cross-platform check.
|
||||
option(env='LD', nargs=1, help='Path to the linker')
|
||||
|
||||
link = check_prog('LINK', depends_win()(lambda: ('link.exe',)), input='LD',
|
||||
paths=vc_compiler_path)
|
||||
|
||||
add_old_configure_assignment('LD', depends_win(link)(lambda x: x))
|
||||
|
||||
|
||||
# Normally, we'd just have CC, etc. set to absolute paths, but the build system
|
||||
# doesn't currently handle properly the case where the paths contain spaces.
|
||||
# Additionally, there's the issue described in toolchain.configure, in
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
if [ -z "${VSPATH}" ]; then
|
||||
TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
|
||||
VSPATH="$(cd ${TOOLTOOL_DIR} && pwd)/vs2015u2"
|
||||
VSWINPATH="$(cd ${TOOLTOOL_DIR} && pwd -W)/vs2015u2"
|
||||
fi
|
||||
|
||||
VSWINPATH="$(cd ${VSPATH} && pwd -W)"
|
||||
|
||||
export WINDOWSSDKDIR="${VSWINPATH}/SDK"
|
||||
export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT"
|
||||
export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x86"
|
||||
|
||||
export PATH="${VSPATH}/VC/bin/amd64_x86:${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x86:${VSPATH}/SDK/bin/x64:${VSPATH}/DIASDK/bin:${PATH}"
|
||||
export PATH="${VSPATH}/VC/bin/amd64_x86:${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x86:${VSPATH}/SDK/bin/x64:${VSPATH}/DIA SDK/bin:${PATH}"
|
||||
export PATH="${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x86:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${PATH}"
|
||||
|
||||
export INCLUDE="${VSPATH}/VC/include:${VSPATH}/VC/atlmfc/include:${VSPATH}/SDK/Include/ucrt:${VSPATH}/SDK/Include/shared:${VSPATH}/SDK/Include/um:${VSPATH}/SDK/Include/winrt:${VSPATH}/DIASDK/include"
|
||||
export LIB="${VSPATH}/VC/lib:${VSPATH}/VC/atlmfc/lib:${VSPATH}/SDK/lib/ucrt/x86:${VSPATH}/SDK/lib/um/x86:${VSPATH}/DIASDK/lib"
|
||||
export INCLUDE="${VSPATH}/VC/include:${VSPATH}/VC/atlmfc/include:${VSPATH}/SDK/Include/10.0.10586.0/ucrt:${VSPATH}/SDK/Include/10.0.10586.0/shared:${VSPATH}/SDK/Include/10.0.10586.0/um:${VSPATH}/SDK/Include/10.0.10586.0/winrt:${VSPATH}/DIA SDK/include"
|
||||
export LIB="${VSPATH}/VC/lib:${VSPATH}/VC/atlmfc/lib:${VSPATH}/SDK/lib/10.0.10586.0/ucrt/x86:${VSPATH}/SDK/lib/10.0.10586.0/um/x86:${VSPATH}/DIA SDK/lib"
|
||||
|
||||
. $topsrcdir/build/mozconfig.vs-common
|
||||
|
||||
mk_export_correct_style WINDOWSSDKDIR
|
||||
mk_export_correct_style INCLUDE
|
||||
mk_export_correct_style LIB
|
||||
mk_export_correct_style PATH
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
if [ -z "${VSPATH}" ]; then
|
||||
TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
|
||||
VSPATH="$(cd ${TOOLTOOL_DIR} && pwd)/vs2015u2"
|
||||
VSWINPATH="$(cd ${TOOLTOOL_DIR} && pwd -W)/vs2015u2"
|
||||
fi
|
||||
|
||||
VSWINPATH="$(cd ${VSPATH} && pwd -W)"
|
||||
|
||||
export WINDOWSSDKDIR="${VSWINPATH}/SDK"
|
||||
export WIN32_REDIST_DIR=${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT
|
||||
export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x64"
|
||||
|
||||
export PATH="${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x64:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${VSPATH}/DIASDK/bin/amd64:${PATH}"
|
||||
export PATH="${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x64:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${VSPATH}/DIA SDK/bin/amd64:${PATH}"
|
||||
|
||||
export INCLUDE="${VSPATH}/VC/include:${VSPATH}/VC/atlmfc/include:${VSPATH}/SDK/Include/ucrt:${VSPATH}/SDK/Include/shared:${VSPATH}/SDK/Include/um:${VSPATH}/SDK/Include/winrt:${VSPATH}/DIASDK/include"
|
||||
export LIB="${VSPATH}/VC/lib/amd64:${VSPATH}/VC/atlmfc/lib/amd64:${VSPATH}/SDK/lib/ucrt/x64:${VSPATH}/SDK/lib/um/x64:${VSPATH}/DIASDK/lib/amd64"
|
||||
export INCLUDE="${VSPATH}/VC/include:${VSPATH}/VC/atlmfc/include:${VSPATH}/SDK/Include/10.0.10586.0/ucrt:${VSPATH}/SDK/Include/10.0.10586.0/shared:${VSPATH}/SDK/Include/10.0.10586.0/um:${VSPATH}/SDK/Include/10.0.10586.0/winrt:${VSPATH}/DIA SDK/include"
|
||||
export LIB="${VSPATH}/VC/lib/amd64:${VSPATH}/VC/atlmfc/lib/amd64:${VSPATH}/SDK/lib/10.0.10586.0/ucrt/x64:${VSPATH}/SDK/lib/10.0.10586.0/um/x64:${VSPATH}/DIA SDK/lib/amd64"
|
||||
|
||||
. $topsrcdir/build/mozconfig.vs-common
|
||||
|
||||
mk_export_correct_style WINDOWSSDKDIR
|
||||
mk_export_correct_style INCLUDE
|
||||
mk_export_correct_style LIB
|
||||
mk_export_correct_style PATH
|
||||
|
|
|
@ -29,23 +29,18 @@ import mozpack.path as mozpath
|
|||
VS_PATTERNS = [
|
||||
{
|
||||
'pattern': 'DIA SDK/bin/**',
|
||||
# Various tools don't like spaces in filenames. So remove it.
|
||||
'rewrite': [('DIA SDK/', 'DIASDK/')],
|
||||
'ignore': (
|
||||
'DIA SDK/bin/arm/**',
|
||||
),
|
||||
},
|
||||
{
|
||||
'pattern': 'DIA SDK/idl/**',
|
||||
'rewrite': [('DIA SDK/', 'DIASDK/')],
|
||||
},
|
||||
{
|
||||
'pattern': 'DIA SDK/include/**',
|
||||
'rewrite': [('DIA SDK/', 'DIASDK/')],
|
||||
},
|
||||
{
|
||||
'pattern': 'DIA SDK/lib/**',
|
||||
'rewrite': [('DIA SDK/', 'DIASDK/')],
|
||||
'ignore': (
|
||||
'DIA SDK/lib/arm/**',
|
||||
),
|
||||
|
@ -160,18 +155,12 @@ def resolve_files():
|
|||
for p, f in finder.find(entry['pattern']):
|
||||
assert p.startswith(('VC/', 'DIA SDK/'))
|
||||
|
||||
for source, dest in entry.get('rewrite', []):
|
||||
p = p.replace(source, dest)
|
||||
|
||||
yield p.encode('utf-8'), f
|
||||
|
||||
for entry in SDK_PATTERNS:
|
||||
finder = FileFinder(sdk_path, find_executables=False,
|
||||
ignore=entry.get('ignore', []))
|
||||
for p, f in finder.find(entry['pattern']):
|
||||
# We remove the SDK version from the path so we don't have
|
||||
# to update other configs when we change the SDK version.
|
||||
p = p.replace('/%s/' % SDK_RELEASE, '/')
|
||||
relpath = 'SDK/%s' % p
|
||||
|
||||
yield relpath.encode('utf-8'), f
|
||||
|
|
|
@ -358,6 +358,18 @@ ifdef MACOSX_DEPLOYMENT_TARGET
|
|||
export MACOSX_DEPLOYMENT_TARGET
|
||||
endif # MACOSX_DEPLOYMENT_TARGET
|
||||
|
||||
# Export to propagate to cl and submake for third-party code.
|
||||
# Eventually, we'll want to just use -I.
|
||||
ifdef INCLUDE
|
||||
export INCLUDE
|
||||
endif
|
||||
|
||||
# Export to propagate to link.exe and submake for third-party code.
|
||||
# Eventually, we'll want to just use -LIBPATH.
|
||||
ifdef LIB
|
||||
export LIB
|
||||
endif
|
||||
|
||||
ifdef MOZ_USING_CCACHE
|
||||
ifdef CLANG_CXX
|
||||
export CCACHE_CPP2=1
|
||||
|
|
|
@ -8089,7 +8089,8 @@ nsContentUtils::SendMouseEvent(nsCOMPtr<nsIPresShell> aPresShell,
|
|||
unsigned short aInputSourceArg,
|
||||
bool aToWindow,
|
||||
bool *aPreventDefault,
|
||||
bool aIsSynthesized)
|
||||
bool aIsDOMEventSynthesized,
|
||||
bool aIsWidgetEventSynthesized)
|
||||
{
|
||||
nsPoint offset;
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
|
||||
|
@ -8123,7 +8124,10 @@ nsContentUtils::SendMouseEvent(nsCOMPtr<nsIPresShell> aPresShell,
|
|||
aInputSourceArg = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
|
||||
}
|
||||
|
||||
WidgetMouseEvent event(true, msg, widget, WidgetMouseEvent::eReal,
|
||||
WidgetMouseEvent event(true, msg, widget,
|
||||
aIsWidgetEventSynthesized ?
|
||||
WidgetMouseEvent::eSynthesized :
|
||||
WidgetMouseEvent::eReal,
|
||||
contextMenuKey ? WidgetMouseEvent::eContextMenuKey :
|
||||
WidgetMouseEvent::eNormal);
|
||||
event.mModifiers = GetWidgetModifiers(aModifiers);
|
||||
|
@ -8133,7 +8137,7 @@ nsContentUtils::SendMouseEvent(nsCOMPtr<nsIPresShell> aPresShell,
|
|||
event.inputSource = aInputSourceArg;
|
||||
event.mClickCount = aClickCount;
|
||||
event.mTime = PR_IntervalNow();
|
||||
event.mFlags.mIsSynthesizedForTests = aIsSynthesized;
|
||||
event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
|
||||
|
||||
nsPresContext* presContext = aPresShell->GetPresContext();
|
||||
if (!presContext)
|
||||
|
|
|
@ -2552,7 +2552,8 @@ public:
|
|||
unsigned short aInputSourceArg,
|
||||
bool aToWindow,
|
||||
bool *aPreventDefault,
|
||||
bool aIsSynthesized);
|
||||
bool aIsDOMEventSynthesized,
|
||||
bool aIsWidgetEventSynthesized);
|
||||
|
||||
static void FirePageShowEvent(nsIDocShellTreeItem* aItem,
|
||||
mozilla::dom::EventTarget* aChromeEventHandler,
|
||||
|
|
|
@ -648,14 +648,18 @@ nsDOMWindowUtils::SendMouseEvent(const nsAString& aType,
|
|||
bool aIgnoreRootScrollFrame,
|
||||
float aPressure,
|
||||
unsigned short aInputSourceArg,
|
||||
bool aIsSynthesized,
|
||||
bool aIsDOMEventSynthesized,
|
||||
bool aIsWidgetEventSynthesized,
|
||||
uint8_t aOptionalArgCount,
|
||||
bool *aPreventDefault)
|
||||
{
|
||||
return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
|
||||
aIgnoreRootScrollFrame, aPressure,
|
||||
aInputSourceArg, false, aPreventDefault,
|
||||
aOptionalArgCount >= 4 ? aIsSynthesized : true);
|
||||
aOptionalArgCount >= 4 ?
|
||||
aIsDOMEventSynthesized : true,
|
||||
aOptionalArgCount >= 5 ?
|
||||
aIsWidgetEventSynthesized : false);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -668,7 +672,8 @@ nsDOMWindowUtils::SendMouseEventToWindow(const nsAString& aType,
|
|||
bool aIgnoreRootScrollFrame,
|
||||
float aPressure,
|
||||
unsigned short aInputSourceArg,
|
||||
bool aIsSynthesized,
|
||||
bool aIsDOMEventSynthesized,
|
||||
bool aIsWidgetEventSynthesized,
|
||||
uint8_t aOptionalArgCount)
|
||||
{
|
||||
PROFILER_LABEL("nsDOMWindowUtils", "SendMouseEventToWindow",
|
||||
|
@ -677,7 +682,10 @@ nsDOMWindowUtils::SendMouseEventToWindow(const nsAString& aType,
|
|||
return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
|
||||
aIgnoreRootScrollFrame, aPressure,
|
||||
aInputSourceArg, true, nullptr,
|
||||
aOptionalArgCount >= 4 ? aIsSynthesized : true);
|
||||
aOptionalArgCount >= 4 ?
|
||||
aIsDOMEventSynthesized : true,
|
||||
aOptionalArgCount >= 5 ?
|
||||
aIsWidgetEventSynthesized : false);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -692,12 +700,14 @@ nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType,
|
|||
unsigned short aInputSourceArg,
|
||||
bool aToWindow,
|
||||
bool *aPreventDefault,
|
||||
bool aIsSynthesized)
|
||||
bool aIsDOMEventSynthesized,
|
||||
bool aIsWidgetEventSynthesized)
|
||||
{
|
||||
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
|
||||
return nsContentUtils::SendMouseEvent(presShell, aType, aX, aY, aButton,
|
||||
aClickCount, aModifiers, aIgnoreRootScrollFrame, aPressure,
|
||||
aInputSourceArg, aToWindow, aPreventDefault, aIsSynthesized);
|
||||
aInputSourceArg, aToWindow, aPreventDefault, aIsDOMEventSynthesized,
|
||||
aIsWidgetEventSynthesized);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -93,7 +93,8 @@ protected:
|
|||
unsigned short aInputSourceArg,
|
||||
bool aToWindow,
|
||||
bool *aPreventDefault,
|
||||
bool aIsSynthesized);
|
||||
bool aIsDOMEventSynthesized,
|
||||
bool aIsWidgetEventSynthesized);
|
||||
|
||||
NS_IMETHOD SendPointerEventCommon(const nsAString& aType,
|
||||
float aX,
|
||||
|
|
|
@ -3704,6 +3704,38 @@ nsPIDOMWindow<T>::MaybeCreateDoc()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsPIDOMWindowOuter::SetInitialKeyboardIndicators(
|
||||
UIStateChangeType aShowAccelerators, UIStateChangeType aShowFocusRings)
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
MOZ_ASSERT(!GetCurrentInnerWindow());
|
||||
|
||||
nsPIDOMWindowOuter* piWin = GetPrivateRoot();
|
||||
if (!piWin) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(piWin == AsOuter());
|
||||
|
||||
// only change the flags that have been modified
|
||||
nsCOMPtr<nsPIWindowRoot> windowRoot = do_QueryInterface(mChromeEventHandler);
|
||||
if (!windowRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aShowAccelerators != UIStateChangeType_NoChange) {
|
||||
windowRoot->SetShowAccelerators(aShowAccelerators == UIStateChangeType_Set);
|
||||
}
|
||||
if (aShowFocusRings != UIStateChangeType_NoChange) {
|
||||
windowRoot->SetShowFocusRings(aShowFocusRings == UIStateChangeType_Set);
|
||||
}
|
||||
|
||||
nsContentUtils::SetKeyboardIndicatorsOnRemoteChildren(GetOuterWindow(),
|
||||
aShowAccelerators,
|
||||
aShowFocusRings);
|
||||
}
|
||||
|
||||
Element*
|
||||
nsPIDOMWindowOuter::GetFrameElementInternal() const
|
||||
{
|
||||
|
|
|
@ -848,6 +848,12 @@ public:
|
|||
return GetCurrentInnerWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set initial keyboard indicator state for accelerators and focus rings.
|
||||
*/
|
||||
void SetInitialKeyboardIndicators(UIStateChangeType aShowAccelerators,
|
||||
UIStateChangeType aShowFocusRings);
|
||||
|
||||
// Internal getter/setter for the frame element, this version of the
|
||||
// getter crosses chrome boundaries whereas the public scriptable
|
||||
// one doesn't for security reasons.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
dump('loaded child cpow test\n');
|
||||
|
||||
var Cu = Components.utils;
|
||||
var Ci = Components.interfaces;
|
||||
|
||||
(function start() {
|
||||
[is_remote] = sendRpcMessage("cpows:is_remote");
|
||||
|
@ -20,6 +21,7 @@ var Cu = Components.utils;
|
|||
lifetime_test,
|
||||
cancel_test,
|
||||
cancel_test2,
|
||||
dead_test,
|
||||
unsafe_test,
|
||||
];
|
||||
|
||||
|
@ -355,3 +357,23 @@ function unsafe_test(finish)
|
|||
addMessageListener("cpows:safe_done", finish);
|
||||
});
|
||||
}
|
||||
|
||||
function dead_test(finish)
|
||||
{
|
||||
if (!is_remote) {
|
||||
// Only run this test when running out-of-process.
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
let thing = { value: "Gonna croak" };
|
||||
sendAsyncMessage("cpows:dead", null, { thing });
|
||||
}
|
||||
// Force the GC to dead-ify the thing.
|
||||
content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
|
||||
addMessageListener("cpows:dead_done", finish);
|
||||
}
|
||||
|
|
|
@ -417,6 +417,17 @@
|
|||
msg.target.messageManager.sendAsyncMessage("cpows:safe_done");
|
||||
}
|
||||
|
||||
function recvDead(msg) {
|
||||
try {
|
||||
msg.objects.thing.value;
|
||||
ok(false, "Should have been a dead CPOW");
|
||||
} catch(e if /dead CPOW/.test(String(e))) {
|
||||
ok(true, "Got the expected dead CPOW");
|
||||
ok(e.stack, "The exception has a stack");
|
||||
}
|
||||
msg.target.messageManager.sendAsyncMessage("cpows:dead_done");
|
||||
}
|
||||
|
||||
function run_tests(type) {
|
||||
info("Running tests: " + type);
|
||||
var node = document.getElementById('cpowbrowser_' + type);
|
||||
|
@ -458,6 +469,7 @@
|
|||
mm.addMessageListener("cpows:cancel_test2", recvCancelTest2);
|
||||
mm.addMessageListener("cpows:unsafe", recvUnsafe);
|
||||
mm.addMessageListener("cpows:safe", recvSafe);
|
||||
mm.addMessageListener("cpows:dead", recvDead);
|
||||
mm.loadFrameScript("chrome://mochitests/content/chrome/dom/base/test/chrome/cpows_child.js", true);
|
||||
}
|
||||
|
||||
|
|
|
@ -101,34 +101,43 @@ WebGLContext::InitWebGL2(FailureReason* const out_failReason)
|
|||
{
|
||||
MOZ_ASSERT(IsWebGL2(), "WebGLContext is not a WebGL 2 context!");
|
||||
|
||||
// check OpenGL features
|
||||
if (!gl->IsSupported(gl::GLFeature::occlusion_query) &&
|
||||
!gl->IsSupported(gl::GLFeature::occlusion_query_boolean))
|
||||
{
|
||||
// On desktop, we fake occlusion_query_boolean with occlusion_query if
|
||||
// necessary. (See WebGL2ContextQueries.cpp)
|
||||
*out_failReason = FailureReason("FEATURE_FAILURE_WEBGL2_OCCL",
|
||||
"WebGL 2 requires occlusion query support.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<gl::GLFeature> missingList;
|
||||
|
||||
for (size_t i = 0; i < ArrayLength(kRequiredFeatures); i++) {
|
||||
if (!gl->IsSupported(kRequiredFeatures[i])) {
|
||||
missingList.push_back(kRequiredFeatures[i]);
|
||||
const auto fnGatherMissing = [&](gl::GLFeature cur) {
|
||||
if (!gl->IsSupported(cur)) {
|
||||
missingList.push_back(cur);
|
||||
}
|
||||
};
|
||||
|
||||
const auto fnGatherMissing2 = [&](gl::GLFeature main, gl::GLFeature alt) {
|
||||
if (!gl->IsSupported(main) && !gl->IsSupported(alt)) {
|
||||
missingList.push_back(main);
|
||||
}
|
||||
};
|
||||
|
||||
////
|
||||
|
||||
for (const auto& cur : kRequiredFeatures) {
|
||||
fnGatherMissing(cur);
|
||||
}
|
||||
|
||||
// On desktop, we fake occlusion_query_boolean with occlusion_query if
|
||||
// necessary. (See WebGL2ContextQueries.cpp)
|
||||
fnGatherMissing2(gl::GLFeature::occlusion_query_boolean,
|
||||
gl::GLFeature::occlusion_query);
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// On OSX, GL core profile is used. This requires texture swizzle
|
||||
// support to emulate legacy texture formats: ALPHA, LUMINANCE,
|
||||
// and LUMINANCE_ALPHA.
|
||||
if (!gl->IsSupported(gl::GLFeature::texture_swizzle)) {
|
||||
missingList.push_back(gl::GLFeature::texture_swizzle);
|
||||
}
|
||||
fnGatherMissing(gl::GLFeature::texture_swizzle);
|
||||
#endif
|
||||
|
||||
fnGatherMissing2(gl::GLFeature::prim_restart_fixed,
|
||||
gl::GLFeature::prim_restart);
|
||||
|
||||
////
|
||||
|
||||
if (missingList.size()) {
|
||||
nsAutoCString exts;
|
||||
for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) {
|
||||
|
@ -155,13 +164,21 @@ WebGLContext::InitWebGL2(FailureReason* const out_failReason)
|
|||
mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0);
|
||||
mBoundTransformFeedback = mDefaultTransformFeedback;
|
||||
|
||||
////
|
||||
|
||||
if (!gl->IsGLES()) {
|
||||
// Desktop OpenGL requires the following to be enabled in order to
|
||||
// support sRGB operations on framebuffers.
|
||||
gl->MakeCurrent();
|
||||
gl->fEnable(LOCAL_GL_FRAMEBUFFER_SRGB_EXT);
|
||||
}
|
||||
|
||||
if (gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
|
||||
gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART_FIXED_INDEX);
|
||||
} else {
|
||||
MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
|
||||
gl->fEnable(LOCAL_GL_PRIMITIVE_RESTART);
|
||||
}
|
||||
|
||||
//////
|
||||
|
||||
static const GLenum kWebGL2_CompressedFormats[] = {
|
||||
|
|
|
@ -919,6 +919,8 @@ protected:
|
|||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Vertices Feature (WebGLContextVertices.cpp)
|
||||
GLenum mPrimRestartTypeBytes;
|
||||
|
||||
public:
|
||||
void DrawArrays(GLenum mode, GLint first, GLsizei count);
|
||||
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count,
|
||||
|
|
|
@ -258,6 +258,16 @@ WebGLContext::DrawArrays_check(GLint first, GLsizei count, GLsizei primcount,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
|
||||
MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
|
||||
if (mPrimRestartTypeBytes != 4) {
|
||||
mPrimRestartTypeBytes = 4;
|
||||
|
||||
// OSX has issues leaving this as 0.
|
||||
gl->fPrimitiveRestartIndex(UINT32_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
// If count is 0, there's nothing to do.
|
||||
if (count == 0 || primcount == 0) {
|
||||
return false;
|
||||
|
@ -414,6 +424,20 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type,
|
|||
return false;
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
if (IsWebGL2() && !gl->IsSupported(gl::GLFeature::prim_restart_fixed)) {
|
||||
MOZ_ASSERT(gl->IsSupported(gl::GLFeature::prim_restart));
|
||||
if (mPrimRestartTypeBytes != bytesPerElem) {
|
||||
mPrimRestartTypeBytes = bytesPerElem;
|
||||
|
||||
const uint32_t ones = UINT32_MAX >> (4 - mPrimRestartTypeBytes);
|
||||
gl->fPrimitiveRestartIndex(ones);
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
const GLsizei first = byteOffset / bytesPerElem;
|
||||
const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(count);
|
||||
|
||||
|
|
|
@ -1001,6 +1001,8 @@ WebGLContext::InitAndValidateGL(FailureReason* const out_failReason)
|
|||
mPixelStore_PackSkipPixels = 0;
|
||||
mPixelStore_PackAlignment = 4;
|
||||
|
||||
mPrimRestartTypeBytes = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -5778,7 +5778,7 @@ skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.
|
|||
[generated/test_2_conformance__rendering__multisample-corruption.html]
|
||||
skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
|
||||
[generated/test_2_conformance__rendering__negative-one-index.html]
|
||||
fail-if = (os == 'mac') || (os == 'win')
|
||||
fail-if = (os == 'mac')
|
||||
skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
|
||||
[generated/test_2_conformance__rendering__point-no-attributes.html]
|
||||
skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
|
||||
|
|
|
@ -151,7 +151,7 @@ fail-if = (os == 'mac') || (os == 'win')
|
|||
[generated/test_2_conformance2__vertex_arrays__vertex-array-object.html]
|
||||
fail-if = (os == 'mac') || (os == 'win')
|
||||
[generated/test_2_conformance__rendering__negative-one-index.html]
|
||||
fail-if = (os == 'mac') || (os == 'win')
|
||||
fail-if = (os == 'mac')
|
||||
[generated/test_conformance__extensions__oes-texture-half-float.html]
|
||||
fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
|
||||
[generated/test_2_conformance2__reading__read-pixels-pack-parameters.html]
|
||||
|
|
|
@ -147,7 +147,7 @@ SimpleTest.waitForExplicitFinish();
|
|||
try {
|
||||
var prefArrArr = [
|
||||
['webgl.force-enabled', true],
|
||||
['webgl.enable-prototype-webgl2', true],
|
||||
['webgl.enable-webgl2', true],
|
||||
];
|
||||
var prefEnv = {'set': prefArrArr};
|
||||
SpecialPowers.pushPrefEnv(prefEnv, run);
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
<script src="driver-info.js"></script>
|
||||
<script src="webgl-util.js"></script>
|
||||
<script id="vs" type="x-shader/x-vertex">
|
||||
#version 300 es
|
||||
<script id="vs" type="x-shader/x-vertex"
|
||||
>#version 300 es
|
||||
|
||||
in vec2 aTexCoord;
|
||||
out vec2 vTexCoord;
|
||||
|
@ -17,8 +17,8 @@ void main() {
|
|||
vTexCoord = aTexCoord;
|
||||
}
|
||||
</script>
|
||||
<script id="fs" type="x-shader/x-fragment">
|
||||
#version 300 es
|
||||
<script id="fs" type="x-shader/x-fragment"
|
||||
>#version 300 es
|
||||
precision mediump float;
|
||||
|
||||
in vec2 vTexCoord;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
function ShouldExpose() {
|
||||
try {
|
||||
return SpecialPowers.getBoolPref('webgl.enable-prototype-webgl2');
|
||||
return SpecialPowers.getBoolPref('webgl.enable-webgl2');
|
||||
} catch (e) {}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -86,9 +86,7 @@ WebGLUtil = (function() {
|
|||
try {
|
||||
var prefArrArr = [
|
||||
['webgl.force-enabled', true],
|
||||
['webgl.disable-angle', true],
|
||||
['webgl.bypass-shader-validation', true],
|
||||
['webgl.enable-prototype-webgl2', true],
|
||||
['webgl.enable-webgl2', true],
|
||||
];
|
||||
var prefEnv = {'set': prefArrArr};
|
||||
SpecialPowers.pushPrefEnv(prefEnv, run);
|
||||
|
|
|
@ -124,6 +124,6 @@ support-files =
|
|||
pointerevent_touch-action-span-test_touch-manual.html
|
||||
pointerevent_touch-action-svg-test_touch-manual.html
|
||||
pointerevent_touch-action-table-test_touch-manual.html
|
||||
|
||||
[test_bug1285128.html]
|
||||
[test_empty_file.html]
|
||||
disabled = disabled # Bug 1150091 - Issue with support-files
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1285128
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1285128</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1285128">Mozilla Bug 1285128</a>
|
||||
<p id="display"></p>
|
||||
<div id="target0" style="width: 200px; height: 200px; background: green"></div>
|
||||
<script type="text/javascript">
|
||||
|
||||
/** Test for Bug 1285128 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTests() {
|
||||
let target0 = window.document.getElementById("target0");
|
||||
let pointerEventsList = ["pointerover", "pointerenter", "pointerdown",
|
||||
"pointerup", "pointerleave", "pointerout"];
|
||||
let receivedPointerEvents = false;
|
||||
pointerEventsList.forEach((elem, index, arr) => {
|
||||
target0.addEventListener(elem, (event) => {
|
||||
ok(false, "receiving event " + event.type);
|
||||
receivedPointerEvents = true;
|
||||
}, false);
|
||||
});
|
||||
|
||||
target0.addEventListener("mouseenter", () => {
|
||||
ok(!receivedPointerEvents, "synthesized mousemove should not trigger any pointer events");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
synthesizeMouseAtCenter(target0, { type: "mousemove",
|
||||
inputSource: SpecialPowers.Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE,
|
||||
isWidgetEventSynthesized: true });
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true]]}, runTests);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -797,6 +797,17 @@ HTMLMediaElement::MozDumpDebugInfo()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::SetVisible(bool aVisible)
|
||||
{
|
||||
if (!mDecoder) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDecoder->NotifyOwnerActivityChanged(aVisible);
|
||||
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMediaStream>
|
||||
HTMLMediaElement::GetSrcObject() const
|
||||
{
|
||||
|
|
|
@ -606,6 +606,8 @@ public:
|
|||
|
||||
void MozDumpDebugInfo();
|
||||
|
||||
void SetVisible(bool aVisible);
|
||||
|
||||
already_AddRefed<DOMMediaStream> GetSrcObject() const;
|
||||
void SetSrcObject(DOMMediaStream& aValue);
|
||||
void SetSrcObject(DOMMediaStream* aValue);
|
||||
|
|
|
@ -312,9 +312,11 @@ interface nsIDOMWindowUtils : nsISupports {
|
|||
* @param aPressure touch input pressure: 0.0 -> 1.0
|
||||
* @param aInputSourceArg input source, see nsIDOMMouseEvent for values,
|
||||
* defaults to mouse input.
|
||||
* @param aIsSynthesized controls nsIDOMEvent.isSynthesized value
|
||||
* that helps identifying test related events,
|
||||
* defaults to true
|
||||
* @param aIsDOMEventSynthesized controls nsIDOMEvent.isSynthesized value
|
||||
* that helps identifying test related events,
|
||||
* defaults to true
|
||||
* @param aIsWidgetEventSynthesized controls WidgetMouseEvent.mReason value
|
||||
* defaults to false (WidgetMouseEvent::eReal)
|
||||
*
|
||||
* returns true if the page called prevent default on this event
|
||||
*/
|
||||
|
@ -328,7 +330,8 @@ interface nsIDOMWindowUtils : nsISupports {
|
|||
[optional] in boolean aIgnoreRootScrollFrame,
|
||||
[optional] in float aPressure,
|
||||
[optional] in unsigned short aInputSourceArg,
|
||||
[optional] in boolean aIsSynthesized);
|
||||
[optional] in boolean aIsDOMEventSynthesized,
|
||||
[optional] in boolean aIsWidgetEventSynthesized);
|
||||
|
||||
|
||||
/** Synthesize a pointer event. The event types supported are:
|
||||
|
@ -447,7 +450,8 @@ interface nsIDOMWindowUtils : nsISupports {
|
|||
[optional] in boolean aIgnoreRootScrollFrame,
|
||||
[optional] in float aPressure,
|
||||
[optional] in unsigned short aInputSourceArg,
|
||||
[optional] in boolean aIsSynthesized);
|
||||
[optional] in boolean aIsDOMEventSynthesized,
|
||||
[optional] in boolean aIsWidgetEventSynthesized);
|
||||
|
||||
/** The same as sendPointerEvent but ensures that the event
|
||||
* is dispatched to this DOM window or one of its children.
|
||||
|
|
|
@ -54,6 +54,11 @@ interface nsITabParent : nsISupports
|
|||
|
||||
readonly attribute uint64_t tabId;
|
||||
|
||||
/**
|
||||
* The OS level process Id of the related child process.
|
||||
*/
|
||||
readonly attribute int32_t osPid;
|
||||
|
||||
/**
|
||||
* Navigate by key. If aForDocumentNavigation is true, navigate by document.
|
||||
* If aForDocumentNavigation is false, navigate by element.
|
||||
|
|
|
@ -62,6 +62,11 @@ public:
|
|||
{
|
||||
return mIsForBrowser;
|
||||
}
|
||||
virtual int32_t Pid() const override
|
||||
{
|
||||
// XXX: do we need this for ContentBridgeParent?
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~ContentBridgeParent();
|
||||
|
|
|
@ -386,8 +386,6 @@ public:
|
|||
return mSubprocess;
|
||||
}
|
||||
|
||||
int32_t Pid() const;
|
||||
|
||||
ContentParent* Opener() const
|
||||
{
|
||||
return mOpener;
|
||||
|
@ -591,6 +589,8 @@ public:
|
|||
virtual bool
|
||||
RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) override;
|
||||
|
||||
virtual int32_t Pid() const override;
|
||||
|
||||
protected:
|
||||
void OnChannelConnected(int32_t pid) override;
|
||||
|
||||
|
|
|
@ -842,7 +842,12 @@ TabChild::Init()
|
|||
do_QueryInterface(window->GetChromeEventHandler());
|
||||
docShell->SetChromeEventHandler(chromeHandler);
|
||||
|
||||
window->SetKeyboardIndicators(ShowAccelerators(), ShowFocusRings());
|
||||
if (window->GetCurrentInnerWindow()) {
|
||||
window->SetKeyboardIndicators(ShowAccelerators(), ShowFocusRings());
|
||||
} else {
|
||||
// Skip ShouldShowFocusRing check if no inner window is available
|
||||
window->SetInitialKeyboardIndicators(ShowAccelerators(), ShowFocusRings());
|
||||
}
|
||||
|
||||
// Set prerender flag if necessary.
|
||||
if (mIsPrerendered) {
|
||||
|
|
|
@ -2943,6 +2943,13 @@ TabParent::GetTabId(uint64_t* aId)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabParent::GetOsPid(int32_t* aId)
|
||||
{
|
||||
*aId = Manager()->Pid();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabParent::GetHasContentOpener(bool* aResult)
|
||||
{
|
||||
|
|
|
@ -82,6 +82,8 @@ public:
|
|||
|
||||
ContentBridgeParent* AsContentBridgeParent();
|
||||
|
||||
virtual int32_t Pid() const = 0;
|
||||
|
||||
protected: // methods
|
||||
bool CanOpenBrowser(const IPCTabContext& aContext);
|
||||
|
||||
|
|
|
@ -317,6 +317,11 @@ public:
|
|||
return &mIsSuspended;
|
||||
}
|
||||
|
||||
// Switch the video decoder to BlankDecoderModule. It might takes effective
|
||||
// since a few samples later depends on how much demuxed samples are already
|
||||
// queued in the original video decoder.
|
||||
virtual void SetVideoBlankDecode(bool aIsBlankDecode) {}
|
||||
|
||||
protected:
|
||||
virtual ~MediaDecoderReader();
|
||||
|
||||
|
|
|
@ -410,4 +410,14 @@ MediaDecoderReaderWrapper::OnMetadataRead(MetadataHolder* aMetadata)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderReaderWrapper::SetVideoBlankDecode(bool aIsBlankDecode)
|
||||
{
|
||||
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NewRunnableMethod<bool>(mReader, &MediaDecoderReader::SetVideoBlankDecode,
|
||||
aIsBlankDecode);
|
||||
mReader->OwnerThread()->Dispatch(r.forget());
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -121,6 +121,8 @@ public:
|
|||
void SetCDMProxy(CDMProxy* aProxy) { mReader->SetCDMProxy(aProxy); }
|
||||
#endif
|
||||
|
||||
void SetVideoBlankDecode(bool aIsBlankDecode);
|
||||
|
||||
private:
|
||||
~MediaDecoderReaderWrapper();
|
||||
|
||||
|
|
|
@ -1362,6 +1362,7 @@ void MediaDecoderStateMachine::VisibilityChanged()
|
|||
|
||||
if (mVideoDecodeSuspended) {
|
||||
mVideoDecodeSuspended = false;
|
||||
mReader->SetVideoBlankDecode(false);
|
||||
|
||||
if (mIsReaderSuspended) {
|
||||
return;
|
||||
|
@ -2640,7 +2641,7 @@ bool MediaDecoderStateMachine::IsStateMachineScheduled() const
|
|||
bool MediaDecoderStateMachine::IsVideoDecodeSuspended() const
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
return mVideoDecodeSuspended || mIsReaderSuspended;
|
||||
return mIsReaderSuspended;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2932,6 +2933,7 @@ MediaDecoderStateMachine::OnSuspendTimerResolved()
|
|||
DECODER_LOG("OnSuspendTimerResolved");
|
||||
mVideoDecodeSuspendTimer.CompleteRequest();
|
||||
mVideoDecodeSuspended = true;
|
||||
mReader->SetVideoBlankDecode(true);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -418,7 +418,8 @@ MediaFormatReader::EnsureDecoderCreated(TrackType aTrack)
|
|||
decoder.mInfo ? *decoder.mInfo->GetAsAudioInfo() : mInfo.mAudio,
|
||||
decoder.mTaskQueue,
|
||||
decoder.mCallback.get(),
|
||||
mCrashHelper
|
||||
mCrashHelper,
|
||||
decoder.mIsBlankDecode
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
@ -432,7 +433,8 @@ MediaFormatReader::EnsureDecoderCreated(TrackType aTrack)
|
|||
decoder.mCallback.get(),
|
||||
mLayersBackendType,
|
||||
GetImageContainer(),
|
||||
mCrashHelper
|
||||
mCrashHelper,
|
||||
decoder.mIsBlankDecode
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
@ -2056,4 +2058,32 @@ MediaFormatReader::GetMozDebugReaderData(nsAString& aString)
|
|||
aString += NS_ConvertUTF8toUTF16(result);
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::SetVideoBlankDecode(bool aIsBlankDecode)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
return SetBlankDecode(TrackType::kVideoTrack, aIsBlankDecode);
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::SetBlankDecode(TrackType aTrack, bool aIsBlankDecode)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
auto& decoder = GetDecoderData(aTrack);
|
||||
|
||||
LOG("%s, decoder.mIsBlankDecode = %d => aIsBlankDecode = %d",
|
||||
TrackTypeToStr(aTrack), decoder.mIsBlankDecode, aIsBlankDecode);
|
||||
|
||||
if (decoder.mIsBlankDecode == aIsBlankDecode) {
|
||||
return;
|
||||
}
|
||||
|
||||
decoder.mIsBlankDecode = aIsBlankDecode;
|
||||
decoder.Flush();
|
||||
decoder.ShutdownDecoder();
|
||||
NotifyDecodingRequested(TrackInfo::kVideoTrack); // Calls ScheduleUpdate().
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -101,6 +101,8 @@ public:
|
|||
// Used for debugging purposes.
|
||||
void GetMozDebugReaderData(nsAString& aString);
|
||||
|
||||
void SetVideoBlankDecode(bool aIsBlankDecode) override;
|
||||
|
||||
private:
|
||||
|
||||
bool HasVideo() { return mVideo.mTrackDemuxer; }
|
||||
|
@ -253,6 +255,7 @@ private:
|
|||
, mSizeOfQueue(0)
|
||||
, mIsHardwareAccelerated(false)
|
||||
, mLastStreamSourceID(UINT32_MAX)
|
||||
, mIsBlankDecode(false)
|
||||
{}
|
||||
|
||||
MediaFormatReader* mOwner;
|
||||
|
@ -427,6 +430,9 @@ private:
|
|||
Maybe<media::TimeUnit> mLastTimeRangesEnd;
|
||||
RefPtr<SharedTrackInfo> mInfo;
|
||||
Maybe<media::TimeUnit> mFirstDemuxedSampleTime;
|
||||
// Use BlankDecoderModule or not.
|
||||
bool mIsBlankDecode;
|
||||
|
||||
};
|
||||
|
||||
class DecoderDataWithPromise : public DecoderData {
|
||||
|
@ -571,6 +577,8 @@ private:
|
|||
RefPtr<CDMProxy> mCDMProxy;
|
||||
#endif
|
||||
RefPtr<GMPCrashHelper> mCrashHelper;
|
||||
|
||||
void SetBlankDecode(TrackType aTrack, bool aIsBlankDecode);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -1401,8 +1401,7 @@ FullBox::FullBox(const nsACString& aType, uint8_t aVersion, uint32_t aFlags,
|
|||
ISOControl* aControl)
|
||||
: Box(aType, aControl)
|
||||
{
|
||||
// Cast to uint64_t due to VC2010 bug.
|
||||
std::bitset<24> tmp_flags((uint64_t)aFlags);
|
||||
std::bitset<24> tmp_flags(aFlags);
|
||||
version = aVersion;
|
||||
flags = tmp_flags;
|
||||
size += sizeof(version) + flags.size() / CHAR_BIT;
|
||||
|
|
|
@ -81,6 +81,7 @@ PDMFactory::PDMFactory()
|
|||
{
|
||||
EnsureInit();
|
||||
CreatePDMs();
|
||||
CreateBlankPDM();
|
||||
}
|
||||
|
||||
PDMFactory::~PDMFactory()
|
||||
|
@ -120,6 +121,11 @@ PDMFactory::EnsureInit() const
|
|||
already_AddRefed<MediaDataDecoder>
|
||||
PDMFactory::CreateDecoder(const CreateDecoderParams& aParams)
|
||||
{
|
||||
if (aParams.mUseBlankDecoder) {
|
||||
MOZ_ASSERT(mBlankPDM);
|
||||
return CreateDecoderWithPDM(mBlankPDM, aParams);
|
||||
}
|
||||
|
||||
const TrackInfo& config = aParams.mConfig;
|
||||
bool isEncrypted = mEMEPDM && config.mCrypto.mValid;
|
||||
|
||||
|
@ -287,6 +293,13 @@ PDMFactory::CreatePDMs()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
PDMFactory::CreateBlankPDM()
|
||||
{
|
||||
mBlankPDM = CreateBlankDecoderModule();
|
||||
MOZ_ASSERT(mBlankPDM && NS_SUCCEEDED(mBlankPDM->Startup()));
|
||||
}
|
||||
|
||||
bool
|
||||
PDMFactory::StartupPDM(PlatformDecoderModule* aPDM)
|
||||
{
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
private:
|
||||
virtual ~PDMFactory();
|
||||
void CreatePDMs();
|
||||
void CreateBlankPDM();
|
||||
// Startup the provided PDM and add it to our list if successful.
|
||||
bool StartupPDM(PlatformDecoderModule* aPDM);
|
||||
// Returns the first PDM in our list supporting the mimetype.
|
||||
|
@ -60,6 +61,7 @@ private:
|
|||
|
||||
nsTArray<RefPtr<PlatformDecoderModule>> mCurrentPDMs;
|
||||
RefPtr<PlatformDecoderModule> mEMEPDM;
|
||||
RefPtr<PlatformDecoderModule> mBlankPDM;
|
||||
|
||||
bool mWMFFailedToLoad = false;
|
||||
bool mFFmpegFailedToLoad = false;
|
||||
|
|
|
@ -64,6 +64,7 @@ struct CreateDecoderParams {
|
|||
layers::ImageContainer* mImageContainer = nullptr;
|
||||
layers::LayersBackend mLayersBackend = layers::LayersBackend::LAYERS_NONE;
|
||||
RefPtr<GMPCrashHelper> mCrashHelper;
|
||||
bool mUseBlankDecoder = false;
|
||||
|
||||
private:
|
||||
void Set(TaskQueue* aTaskQueue) { mTaskQueue = aTaskQueue; }
|
||||
|
@ -72,6 +73,7 @@ private:
|
|||
void Set(layers::ImageContainer* aImageContainer) { mImageContainer = aImageContainer; }
|
||||
void Set(layers::LayersBackend aLayersBackend) { mLayersBackend = aLayersBackend; }
|
||||
void Set(GMPCrashHelper* aCrashHelper) { mCrashHelper = aCrashHelper; }
|
||||
void Set(bool aUseBlankDecoder) { mUseBlankDecoder = aUseBlankDecoder; }
|
||||
template <typename T1, typename T2, typename... Ts>
|
||||
void Set(T1&& a1, T2&& a2, Ts&&... args)
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "H264Converter.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "MediaInfo.h"
|
||||
|
@ -11,9 +12,11 @@
|
|||
#include "mozilla/mozalloc.h" // for operator new, and new (fallible)
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/TaskQueue.h"
|
||||
#include "mp4_demuxer/H264.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsRect.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "ReorderQueue.h"
|
||||
#include "TimeUnits.h"
|
||||
#include "VideoUtils.h"
|
||||
|
||||
|
@ -29,6 +32,10 @@ public:
|
|||
const CreateDecoderParams& aParams)
|
||||
: mCreator(aCreator)
|
||||
, mCallback(aParams.mCallback)
|
||||
, mMaxRefFrames(aParams.mConfig.GetType() == TrackInfo::kVideoTrack &&
|
||||
H264Converter::IsH264(aParams.mConfig)
|
||||
? mp4_demuxer::H264::ComputeMaxRefFrames(aParams.VideoConfig().mExtraData)
|
||||
: 0)
|
||||
, mType(aParams.mConfig.GetType())
|
||||
{
|
||||
}
|
||||
|
@ -47,19 +54,25 @@ public:
|
|||
mCreator->Create(media::TimeUnit::FromMicroseconds(aSample->mTime),
|
||||
media::TimeUnit::FromMicroseconds(aSample->mDuration),
|
||||
aSample->mOffset);
|
||||
if (!data) {
|
||||
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
|
||||
} else {
|
||||
mCallback->Output(data);
|
||||
|
||||
OutputFrame(data);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Flush() override
|
||||
{
|
||||
mReorderQueue.Clear();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Drain() override
|
||||
{
|
||||
while (!mReorderQueue.IsEmpty()) {
|
||||
mCallback->Output(mReorderQueue.Pop().get());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Flush() override {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Drain() override {
|
||||
mCallback->DrainComplete();
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -69,9 +82,32 @@ public:
|
|||
return "blank media data decoder";
|
||||
}
|
||||
|
||||
private:
|
||||
void OutputFrame(MediaData* aData)
|
||||
{
|
||||
if (!aData) {
|
||||
mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Frames come out in DTS order but we need to output them in PTS order.
|
||||
mReorderQueue.Push(aData);
|
||||
|
||||
while (mReorderQueue.Length() > mMaxRefFrames) {
|
||||
mCallback->Output(mReorderQueue.Pop().get());
|
||||
}
|
||||
|
||||
if (mReorderQueue.Length() <= mMaxRefFrames) {
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
nsAutoPtr<BlankMediaDataCreator> mCreator;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
const uint32_t mMaxRefFrames;
|
||||
ReorderQueue mReorderQueue;
|
||||
TrackInfo::TrackType mType;
|
||||
};
|
||||
|
||||
|
@ -93,10 +129,10 @@ public:
|
|||
{
|
||||
// Create a fake YUV buffer in a 420 format. That is, an 8bpp Y plane,
|
||||
// with a U and V plane that are half the size of the Y plane, i.e 8 bit,
|
||||
// 2x2 subsampled. Have the data pointers of each frame point to the
|
||||
// first plane, they'll always be zero'd memory anyway.
|
||||
auto frame = MakeUnique<uint8_t[]>(mFrameWidth * mFrameHeight);
|
||||
memset(frame.get(), 0, mFrameWidth * mFrameHeight);
|
||||
// 2x2 subsampled.
|
||||
const int sizeY = mFrameWidth * mFrameHeight;
|
||||
const int sizeCbCr = ((mFrameWidth + 1) / 2) * ((mFrameHeight + 1) / 2);
|
||||
auto frame = MakeUnique<uint8_t[]>(sizeY + sizeCbCr);
|
||||
VideoData::YCbCrBuffer buffer;
|
||||
|
||||
// Y plane.
|
||||
|
@ -108,7 +144,7 @@ public:
|
|||
buffer.mPlanes[0].mSkip = 0;
|
||||
|
||||
// Cb plane.
|
||||
buffer.mPlanes[1].mData = frame.get();
|
||||
buffer.mPlanes[1].mData = frame.get() + sizeY;
|
||||
buffer.mPlanes[1].mStride = mFrameWidth / 2;
|
||||
buffer.mPlanes[1].mHeight = mFrameHeight / 2;
|
||||
buffer.mPlanes[1].mWidth = mFrameWidth / 2;
|
||||
|
@ -116,13 +152,17 @@ public:
|
|||
buffer.mPlanes[1].mSkip = 0;
|
||||
|
||||
// Cr plane.
|
||||
buffer.mPlanes[2].mData = frame.get();
|
||||
buffer.mPlanes[2].mData = frame.get() + sizeY;
|
||||
buffer.mPlanes[2].mStride = mFrameWidth / 2;
|
||||
buffer.mPlanes[2].mHeight = mFrameHeight / 2;
|
||||
buffer.mPlanes[2].mWidth = mFrameWidth / 2;
|
||||
buffer.mPlanes[2].mOffset = 0;
|
||||
buffer.mPlanes[2].mSkip = 0;
|
||||
|
||||
// Set to color white.
|
||||
memset(buffer.mPlanes[0].mData, 255, sizeY);
|
||||
memset(buffer.mPlanes[1].mData, 128, sizeCbCr);
|
||||
|
||||
return VideoData::Create(mInfo,
|
||||
mImageContainer,
|
||||
nullptr,
|
||||
|
@ -134,6 +174,7 @@ public:
|
|||
aDTS.ToMicroseconds(),
|
||||
mPicture);
|
||||
}
|
||||
|
||||
private:
|
||||
VideoInfo mInfo;
|
||||
gfx::IntRect mPicture;
|
||||
|
@ -142,7 +183,6 @@ private:
|
|||
RefPtr<layers::ImageContainer> mImageContainer;
|
||||
};
|
||||
|
||||
|
||||
class BlankAudioDataCreator {
|
||||
public:
|
||||
BlankAudioDataCreator(uint32_t aChannelCount, uint32_t aSampleRate)
|
||||
|
@ -229,7 +269,11 @@ public:
|
|||
ConversionRequired
|
||||
DecoderNeedsConversion(const TrackInfo& aConfig) const override
|
||||
{
|
||||
return kNeedNone;
|
||||
if (aConfig.IsVideo() && H264Converter::IsH264(aConfig)) {
|
||||
return kNeedAVCC;
|
||||
} else {
|
||||
return kNeedNone;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
#include "AppleUtils.h"
|
||||
#include "AppleVTDecoder.h"
|
||||
#include "AppleVTLinker.h"
|
||||
#include "mp4_demuxer/H264.h"
|
||||
#include "MediaData.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mp4_demuxer/H264.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
@ -24,22 +24,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
static uint32_t ComputeMaxRefFrames(const MediaByteBuffer* aExtraData)
|
||||
{
|
||||
uint32_t maxRefFrames = 4;
|
||||
// Retrieve video dimensions from H264 SPS NAL.
|
||||
mp4_demuxer::SPSData spsdata;
|
||||
if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtraData, spsdata)) {
|
||||
// max_num_ref_frames determines the size of the sliding window
|
||||
// we need to queue that many frames in order to guarantee proper
|
||||
// pts frames ordering. Use a minimum of 4 to ensure proper playback of
|
||||
// non compliant videos.
|
||||
maxRefFrames =
|
||||
std::min(std::max(maxRefFrames, spsdata.max_num_ref_frames + 1), 16u);
|
||||
}
|
||||
return maxRefFrames;
|
||||
}
|
||||
|
||||
AppleVTDecoder::AppleVTDecoder(const VideoInfo& aConfig,
|
||||
TaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback,
|
||||
|
@ -52,7 +36,7 @@ AppleVTDecoder::AppleVTDecoder(const VideoInfo& aConfig,
|
|||
, mDisplayHeight(aConfig.mDisplay.height)
|
||||
, mQueuedSamples(0)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mMaxRefFrames(ComputeMaxRefFrames(aConfig.mExtraData))
|
||||
, mMaxRefFrames(mp4_demuxer::H264::ComputeMaxRefFrames(aConfig.mExtraData))
|
||||
, mImageContainer(aImageContainer)
|
||||
, mInputIncoming(0)
|
||||
, mIsShutDown(false)
|
||||
|
|
|
@ -106,7 +106,7 @@ MediaEngineDefaultVideoSource::Allocate(const dom::MediaTrackConstraints &aConst
|
|||
mOpts.mHeight = c.mHeight.Get(aPrefs.mHeight ? aPrefs.mHeight :
|
||||
MediaEngine::DEFAULT_43_VIDEO_HEIGHT);
|
||||
mState = kAllocated;
|
||||
aOutHandle = nullptr;
|
||||
*aOutHandle = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -415,7 +415,7 @@ MediaEngineDefaultAudioSource::Allocate(const dom::MediaTrackConstraints &aConst
|
|||
// generate sine wave (default 1KHz)
|
||||
mSineGenerator = new SineWaveGenerator(AUDIO_RATE,
|
||||
static_cast<uint32_t>(aPrefs.mFreq ? aPrefs.mFreq : 1000));
|
||||
aOutHandle = nullptr;
|
||||
*aOutHandle = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ MediaEngineGonkVideoSource::Allocate(const dom::MediaTrackConstraints& aConstrai
|
|||
}
|
||||
}
|
||||
|
||||
aOutHandle = nullptr;
|
||||
*aOutHandle = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ MediaEngineTabVideoSource::Allocate(const dom::MediaTrackConstraints& aConstrain
|
|||
|
||||
mWindowId = aConstraints.mBrowserWindow.WasPassed() ?
|
||||
aConstraints.mBrowserWindow.Value() : -1;
|
||||
aOutHandle = nullptr;
|
||||
*aOutHandle = nullptr;
|
||||
return Restart(nullptr, aConstraints, aPrefs, aDeviceId, aOutBadConstraint);
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ public:
|
|||
const char** aOutBadConstraint) override
|
||||
{
|
||||
// Nothing to do here, everything is managed in MediaManager.cpp
|
||||
aOutHandle = nullptr;
|
||||
*aOutHandle = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
nsresult Deallocate(AllocationHandle* aHandle) override
|
||||
|
|
|
@ -106,7 +106,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=803225
|
|||
testContent.appendChild(newscript);
|
||||
|
||||
// Test 7: mailto protocol
|
||||
SpecialPowers.loadChromeScript(function launchHandler() {
|
||||
let mm = SpecialPowers.loadChromeScript(function launchHandler() {
|
||||
var { classes: Cc, interfaces: Ci } = Components;
|
||||
var ioService = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
|
@ -116,25 +116,36 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=803225
|
|||
webHandler.name = "Web Handler";
|
||||
webHandler.uriTemplate = "http://example.com/tests/dom/security/test/mixedcontentblocker/file_bug803225_test_mailto.html?s=%";
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Services.ppmm.addMessageListener("Test:content-ready", function contentReadyListener() {
|
||||
Services.ppmm.removeMessageListener("Test:content-ready", contentReadyListener);
|
||||
sendAsyncMessage("Test:content-ready-forward");
|
||||
})
|
||||
Services.ppmm.loadProcessScript("data:,new " + function () {
|
||||
var os = Components.classes["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
var observer = {
|
||||
observe: function(subject, topic, data) {
|
||||
if (topic == "content-document-global-created" && data == "http://example.com") {
|
||||
sendAsyncMessage("Test:content-ready");
|
||||
os.removeObserver(observer, "content-document-global-created");
|
||||
}
|
||||
}
|
||||
};
|
||||
os.addObserver(observer, "content-document-global-created", false);
|
||||
}, false);
|
||||
|
||||
var uri = ioService.newURI("mailto:foo@bar.com", null, null);
|
||||
webHandler.launchWithURI(uri);
|
||||
});
|
||||
|
||||
var mailto = false;
|
||||
|
||||
// listen for a messages from a new window
|
||||
var os = SpecialPowers.Cc["@mozilla.org/observer-service;1"].
|
||||
getService(SpecialPowers.Components.interfaces.nsIObserverService);
|
||||
var observer = {
|
||||
observe: function(subject, topic, data) {
|
||||
if(topic == "content-document-global-created" && data =="http://example.com") {
|
||||
parent.postMessage({"test": "mailto", "msg": "resource with mailto protocol loaded"}, "http://mochi.test:8888");
|
||||
os.removeObserver(observer, "content-document-global-created");
|
||||
mailto = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
os.addObserver(observer, "content-document-global-created", false);
|
||||
mm.addMessageListener("Test:content-ready-forward", function contentReadyListener() {
|
||||
mm.removeMessageListener("Test:content-ready-forward", contentReadyListener);
|
||||
mailto = true;
|
||||
parent.postMessage({"test": "mailto", "msg": "resource with mailto protocol loaded"}, "http://mochi.test:8888");
|
||||
});
|
||||
|
||||
function mailtoProtocolStatus() {
|
||||
if(!mailto) {
|
||||
|
|
|
@ -214,3 +214,12 @@ partial interface HTMLMediaElement {
|
|||
[Throws, Pref="media.seekToNextFrame.enabled"]
|
||||
Promise<void> seekToNextFrame();
|
||||
};
|
||||
|
||||
/*
|
||||
* This is an API for simulating visibility changes to help debug and write
|
||||
* tests about suspend-video-decoding.
|
||||
*/
|
||||
partial interface HTMLMediaElement {
|
||||
[Pref="media.test.setVisible"]
|
||||
void setVisible(boolean aVisible);
|
||||
};
|
||||
|
|
|
@ -10,27 +10,27 @@
|
|||
typedef long long GLint64; // Should this be int64?
|
||||
typedef unsigned long long GLuint64;
|
||||
|
||||
[Pref="webgl.enable-prototype-webgl2"]
|
||||
[Pref="webgl.enable-webgl2"]
|
||||
interface WebGLQuery {
|
||||
};
|
||||
|
||||
[Pref="webgl.enable-prototype-webgl2"]
|
||||
[Pref="webgl.enable-webgl2"]
|
||||
interface WebGLSampler {
|
||||
};
|
||||
|
||||
[Pref="webgl.enable-prototype-webgl2"]
|
||||
[Pref="webgl.enable-webgl2"]
|
||||
interface WebGLSync {
|
||||
};
|
||||
|
||||
[Pref="webgl.enable-prototype-webgl2"]
|
||||
[Pref="webgl.enable-webgl2"]
|
||||
interface WebGLTransformFeedback {
|
||||
};
|
||||
|
||||
[Pref="webgl.enable-prototype-webgl2"]
|
||||
[Pref="webgl.enable-webgl2"]
|
||||
interface WebGLVertexArrayObject {
|
||||
};
|
||||
|
||||
[Pref="webgl.enable-prototype-webgl2"]
|
||||
[Pref="webgl.enable-webgl2"]
|
||||
interface WebGL2RenderingContext : WebGLRenderingContext
|
||||
{
|
||||
const GLenum READ_BUFFER = 0x0C02;
|
||||
|
|
|
@ -904,17 +904,6 @@ nsEditingSession::StartDocumentLoad(nsIWebProgress *aWebProgress,
|
|||
|
||||
NS_ENSURE_ARG_POINTER(aWebProgress);
|
||||
|
||||
// If we have an editor here, then we got a reload after making the editor.
|
||||
// We need to blow it away and make a new one at the end of the load.
|
||||
nsCOMPtr<mozIDOMWindowProxy> domWindow;
|
||||
aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
|
||||
if (domWindow)
|
||||
{
|
||||
nsIDocShell *docShell = nsPIDOMWindowOuter::From(domWindow)->GetDocShell();
|
||||
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
||||
docShell->DetachEditorFromWindow();
|
||||
}
|
||||
|
||||
if (aIsToBeMadeEditable)
|
||||
mEditorStatus = eEditorCreationInProgress;
|
||||
|
||||
|
|
|
@ -12,3 +12,4 @@ skip-if = buildapp == 'b2g' || os == 'android'
|
|||
[test_bug1205983.html]
|
||||
[test_bug1209414.html]
|
||||
[test_bug1219928.html]
|
||||
[test_bug1266815.html]
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<script type="text/javascript">
|
||||
const Cc = SpecialPowers.Cc;
|
||||
const Ci = SpecialPowers.Ci;
|
||||
const Cu = SpecialPowers.Cu;
|
||||
|
||||
const {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
|
||||
|
||||
const HELPERAPP_DIALOG_CID =
|
||||
SpecialPowers.wrap(SpecialPowers.Components)
|
||||
.ID(Cc["@mozilla.org/helperapplauncherdialog;1"].number);
|
||||
const HELPERAPP_DIALOG_CONTRACT_ID = "@mozilla.org/helperapplauncherdialog;1";
|
||||
const MOCK_HELPERAPP_DIALOG_CID =
|
||||
SpecialPowers.wrap(SpecialPowers.Components)
|
||||
.ID("{391832c8-5232-4676-b838-cc8ad373f3d8}");
|
||||
|
||||
var registrar = SpecialPowers.wrap(Components).manager
|
||||
.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
|
||||
var helperAppDlgPromise = new Promise(function(resolve) {
|
||||
var mockHelperAppService;
|
||||
|
||||
function HelperAppLauncherDialog() {
|
||||
}
|
||||
|
||||
HelperAppLauncherDialog.prototype = {
|
||||
show: function(aLauncher, aWindowContext, aReason) {
|
||||
ok(true, "Whether showing Dialog");
|
||||
resolve();
|
||||
registrar.unregisterFactory(MOCK_HELPERAPP_DIALOG_CID,
|
||||
mockHelperAppService);
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog])
|
||||
};
|
||||
|
||||
mockHelperAppService = XPCOMUtils._getFactory(HelperAppLauncherDialog);
|
||||
registrar.registerFactory(MOCK_HELPERAPP_DIALOG_CID, "",
|
||||
HELPERAPP_DIALOG_CONTRACT_ID,
|
||||
mockHelperAppService);
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
let promise = new Promise(function(resolve) {
|
||||
let iframe = document.createElement("iframe");
|
||||
iframe.onload = function() {
|
||||
is(iframe.contentDocument.getElementById("edit").innerText, "abc",
|
||||
"load iframe source");
|
||||
resolve();
|
||||
};
|
||||
iframe.id = "testframe";
|
||||
iframe.src = "data:text/html,<div id=edit contenteditable=true>abc</div>";
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
|
||||
yield promise;
|
||||
|
||||
let iframe = document.getElementById("testframe");
|
||||
let docShell = SpecialPowers.wrap(iframe.contentWindow)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
|
||||
ok(docShell.hasEditingSession, "Should have editing session");
|
||||
|
||||
document.getElementById("testframe").src =
|
||||
"data:application/octet-stream,TESTCONTENT";
|
||||
|
||||
yield helperAppDlgPromise;
|
||||
|
||||
ok(docShell.hasEditingSession, "Should have editing session");
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -273,6 +273,7 @@ const char *kWebBrowserPersistStringBundle =
|
|||
"chrome://global/locale/nsWebBrowserPersist.properties";
|
||||
|
||||
nsWebBrowserPersist::nsWebBrowserPersist() :
|
||||
mCurrentDataPathIsRelative(false),
|
||||
mCurrentThingsToPersist(0),
|
||||
mFirstAndOnlyUse(true),
|
||||
mSavingDocument(false),
|
||||
|
|
|
@ -1671,6 +1671,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
|
|||
id = aID;
|
||||
}
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
// When we do the initial addition of the permissions we don't want to
|
||||
// inherit session specific permissions from other tabs or apps
|
||||
// so we ignore them and set the permission to PROMPT_ACTION if it was
|
||||
|
@ -1680,6 +1681,7 @@ nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
|
|||
aPermission = nsIPermissionManager::PROMPT_ACTION;
|
||||
aExpireType = nsIPermissionManager::EXPIRE_NEVER;
|
||||
}
|
||||
#endif // MOZ_B2G
|
||||
|
||||
entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission,
|
||||
aExpireType, aExpireTime,
|
||||
|
|
|
@ -31,7 +31,7 @@ function run_test() {
|
|||
case "TESTING:Stage2A":
|
||||
// Permissions created after the child is present
|
||||
do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.org"), "cookie1"), pm.ALLOW_ACTION);
|
||||
do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.com"), "cookie2"), pm.PROMPT_ACTION);
|
||||
do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.com"), "cookie2"), pm.DENY_ACTION);
|
||||
do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.net"), "cookie3"), pm.ALLOW_ACTION);
|
||||
do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://firefox.org"), "cookie1"), pm.ALLOW_ACTION);
|
||||
do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://firefox.com"), "cookie2"), pm.DENY_ACTION);
|
||||
|
@ -50,7 +50,7 @@ function run_test() {
|
|||
// Permissions created before the child is present
|
||||
var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
|
||||
do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.org"), "cookie1"), pm.ALLOW_ACTION);
|
||||
do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.com"), "cookie2"), pm.PROMPT_ACTION);
|
||||
do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.com"), "cookie2"), pm.DENY_ACTION);
|
||||
do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.net"), "cookie3"), pm.ALLOW_ACTION);
|
||||
|
||||
mM.sendAsyncMessage("TESTING:Stage2");
|
||||
|
|
|
@ -163,6 +163,7 @@ static const char* const sExtensionNames[] = {
|
|||
"GL_NV_geometry_program4",
|
||||
"GL_NV_half_float",
|
||||
"GL_NV_instanced_arrays",
|
||||
"GL_NV_primitive_restart",
|
||||
"GL_NV_texture_barrier",
|
||||
"GL_NV_transform_feedback",
|
||||
"GL_NV_transform_feedback2",
|
||||
|
@ -1574,6 +1575,14 @@ GLContext::LoadMoreSymbols(const char* prefix, bool trygl)
|
|||
fnLoadForFeature(symbols, GLFeature::invalidate_framebuffer);
|
||||
}
|
||||
|
||||
if (IsSupported(GLFeature::prim_restart)) {
|
||||
const SymLoadStruct symbols[] = {
|
||||
{ (PRFuncPtr*) &mSymbols.fPrimitiveRestartIndex, { "PrimitiveRestartIndex", "PrimitiveRestartIndexNV", nullptr } },
|
||||
END_SYMBOLS
|
||||
};
|
||||
fnLoadForFeature(symbols, GLFeature::prim_restart);
|
||||
}
|
||||
|
||||
if (IsExtensionSupported(KHR_debug)) {
|
||||
const SymLoadStruct symbols[] = {
|
||||
{ (PRFuncPtr*) &mSymbols.fDebugMessageControl, { "DebugMessageControl", "DebugMessageControlKHR", nullptr } },
|
||||
|
|
|
@ -110,6 +110,8 @@ enum class GLFeature {
|
|||
occlusion_query_boolean,
|
||||
occlusion_query2,
|
||||
packed_depth_stencil,
|
||||
prim_restart,
|
||||
prim_restart_fixed,
|
||||
query_counter,
|
||||
query_objects,
|
||||
query_time_elapsed,
|
||||
|
@ -477,6 +479,7 @@ public:
|
|||
NV_geometry_program4,
|
||||
NV_half_float,
|
||||
NV_instanced_arrays,
|
||||
NV_primitive_restart,
|
||||
NV_texture_barrier,
|
||||
NV_transform_feedback,
|
||||
NV_transform_feedback2,
|
||||
|
@ -3171,6 +3174,16 @@ public:
|
|||
AFTER_GL_CALL;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// prim_restart
|
||||
|
||||
void fPrimitiveRestartIndex(GLuint index) {
|
||||
BEFORE_GL_CALL;
|
||||
ASSERT_SYMBOL_PRESENT(fPrimitiveRestartIndex);
|
||||
mSymbols.fPrimitiveRestartIndex(index);
|
||||
AFTER_GL_CALL;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Constructor
|
||||
protected:
|
||||
|
|
|
@ -439,6 +439,25 @@ static const FeatureInfo sFeatureInfoArr[] = {
|
|||
GLContext::Extensions_End
|
||||
}
|
||||
},
|
||||
{
|
||||
"prim_restart",
|
||||
GLVersion::GL3_1,
|
||||
GLESVersion::NONE,
|
||||
GLContext::Extension_None,
|
||||
{
|
||||
GLContext::NV_primitive_restart,
|
||||
GLContext::Extensions_End
|
||||
}
|
||||
},
|
||||
{
|
||||
"prim_restart_fixed",
|
||||
kGLCoreVersionForES3Compat,
|
||||
GLESVersion::ES3,
|
||||
GLContext::ARB_ES3_compatibility,
|
||||
{
|
||||
GLContext::Extensions_End
|
||||
}
|
||||
},
|
||||
{
|
||||
"query_counter",
|
||||
GLVersion::GL3_3,
|
||||
|
|
|
@ -694,6 +694,9 @@ struct GLContextSymbols
|
|||
// NV_texture_barrier
|
||||
typedef void (GLAPIENTRY * PFNTEXTUREBARRIERPROC) (void);
|
||||
PFNTEXTUREBARRIERPROC fTextureBarrier;
|
||||
|
||||
// NV_primitive_restart
|
||||
void (GLAPIENTRY * fPrimitiveRestartIndex) (GLuint index);
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
|
|
|
@ -499,7 +499,8 @@ APZCCallbackHelper::DispatchMouseEvent(const nsCOMPtr<nsIPresShell>& aPresShell,
|
|||
bool defaultPrevented = false;
|
||||
nsContentUtils::SendMouseEvent(aPresShell, aType, aPoint.x, aPoint.y,
|
||||
aButton, aClickCount, aModifiers, aIgnoreRootScrollFrame, 0,
|
||||
aInputSourceArg, false, &defaultPrevented, false);
|
||||
aInputSourceArg, false, &defaultPrevented, false,
|
||||
/* aIsWidgetEventSynthesized = */ false);
|
||||
return defaultPrevented;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,9 +58,6 @@ public:
|
|||
|
||||
for (uint32_t i = 0; i < children.Length(); i++) {
|
||||
Layer* child = children.ElementAt(i);
|
||||
if (!child->IsVisible()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ToClientLayer(child)->RenderLayerWithReadback(&readback);
|
||||
|
||||
|
|
|
@ -1238,15 +1238,15 @@ CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion,
|
|||
void
|
||||
CompositorD3D11::EndFrame()
|
||||
{
|
||||
Compositor::EndFrame();
|
||||
|
||||
if (!mDefaultRT) {
|
||||
Compositor::EndFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
LayoutDeviceIntSize oldSize = mSize;
|
||||
EnsureSize();
|
||||
if (mSize.width <= 0 || mSize.height <= 0) {
|
||||
Compositor::EndFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1334,6 +1334,8 @@ CompositorD3D11::EndFrame()
|
|||
// Store the query for this frame so we can flush it next time.
|
||||
mQuery = query;
|
||||
|
||||
Compositor::EndFrame();
|
||||
|
||||
mCurrentRT = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -720,8 +720,6 @@ CompositorD3D9::BeginFrame(const nsIntRegion& aInvalidRegion,
|
|||
void
|
||||
CompositorD3D9::EndFrame()
|
||||
{
|
||||
Compositor::EndFrame();
|
||||
|
||||
if (mDeviceManager) {
|
||||
device()->EndScene();
|
||||
|
||||
|
@ -736,6 +734,8 @@ CompositorD3D9::EndFrame()
|
|||
}
|
||||
}
|
||||
|
||||
Compositor::EndFrame();
|
||||
|
||||
mCurrentRT = nullptr;
|
||||
mDefaultRT = nullptr;
|
||||
}
|
||||
|
|
|
@ -1463,8 +1463,6 @@ CompositorOGL::EndFrame()
|
|||
|
||||
MOZ_ASSERT(mCurrentRenderTarget == mWindowRenderTarget, "Rendering target not properly restored");
|
||||
|
||||
Compositor::EndFrame();
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
if (gfxEnv::DumpCompositorTextures()) {
|
||||
LayoutDeviceIntSize size;
|
||||
|
@ -1489,6 +1487,7 @@ CompositorOGL::EndFrame()
|
|||
CopyToTarget(mTarget, mTargetBounds.TopLeft(), Matrix());
|
||||
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
|
||||
mCurrentRenderTarget = nullptr;
|
||||
Compositor::EndFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1509,6 +1508,8 @@ CompositorOGL::EndFrame()
|
|||
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Compositor::EndFrame();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -413,7 +413,6 @@ private:
|
|||
DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor", ImageMemSurfaceCacheSizeFactor, uint32_t, 64);
|
||||
DECL_GFX_PREF(Live, "image.mozsamplesize.enabled", ImageMozSampleSizeEnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit", ImageMTDecodingLimit, int32_t, -1);
|
||||
DECL_GFX_PREF(Live, "image.single-color-optimization.enabled", ImageSingleColorOptimizationEnabled, bool, true);
|
||||
|
||||
DECL_GFX_PREF(Once, "layers.acceleration.disabled", LayersAccelerationDisabledDoNotUseDirectly, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.acceleration.draw-fps", LayersDrawFPS, bool, false);
|
||||
|
@ -553,7 +552,7 @@ private:
|
|||
|
||||
DECL_GFX_PREF(Live, "webgl.enable-draft-extensions", WebGLDraftExtensionsEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "webgl.enable-privileged-extensions", WebGLPrivilegedExtensionsEnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "webgl.enable-prototype-webgl2", WebGL2Enabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "webgl.enable-webgl2", WebGL2Enabled, bool, true);
|
||||
DECL_GFX_PREF(Live, "webgl.force-enabled", WebGLForceEnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "webgl.force-layers-readback", WebGLForceLayersReadback, bool, false);
|
||||
DECL_GFX_PREF(Live, "webgl.lose-context-on-memory-pressure", WebGLLoseContextOnMemoryPressure, bool, false);
|
||||
|
|
|
@ -271,14 +271,16 @@ Decoder::AllocateFrame(uint32_t aFrameNum,
|
|||
mCurrentFrame->GetImageData(&mImageData, &mImageDataLength);
|
||||
mCurrentFrame->GetPaletteData(&mColormap, &mColormapSize);
|
||||
|
||||
if (aFrameNum + 1 == mFrameCount) {
|
||||
// If we're past the first frame, PostIsAnimated() should've been called.
|
||||
MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
|
||||
// We should now be on |aFrameNum|. (Note that we're comparing the frame
|
||||
// number, which is zero-based, with the frame count, which is one-based.)
|
||||
MOZ_ASSERT(aFrameNum + 1 == mFrameCount);
|
||||
|
||||
// Update our state to reflect the new frame
|
||||
MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
|
||||
mInFrame = true;
|
||||
}
|
||||
// If we're past the first frame, PostIsAnimated() should've been called.
|
||||
MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
|
||||
|
||||
// Update our state to reflect the new frame.
|
||||
MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
|
||||
mInFrame = true;
|
||||
}
|
||||
|
||||
return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
|
||||
|
|
|
@ -24,6 +24,12 @@ NotifyProgress(NotNull<Decoder*> aDecoder)
|
|||
{
|
||||
MOZ_ASSERT(aDecoder->HasProgress() && !aDecoder->IsMetadataDecode());
|
||||
|
||||
// Capture the decoder's state. If we need to notify asynchronously, it's
|
||||
// important that we don't wait until the lambda actually runs to capture the
|
||||
// state that we're going to notify. That would both introduce data races on
|
||||
// the decoder's state and cause inconsistencies between the NotifyProgress()
|
||||
// calls we make off-main-thread and the notifications that RasterImage
|
||||
// actually receives, which would cause bugs.
|
||||
Progress progress = aDecoder->TakeProgress();
|
||||
IntRect invalidRect = aDecoder->TakeInvalidRect();
|
||||
Maybe<uint32_t> frameCount = aDecoder->TakeCompleteFrameCount();
|
||||
|
|
|
@ -481,84 +481,6 @@ RasterImage::GetFirstFrameDelay()
|
|||
return mAnimationState->FirstFrameTimeout().AsEncodedValueDeprecated();
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface>
|
||||
RasterImage::CopyFrame(uint32_t aWhichFrame, uint32_t aFlags)
|
||||
{
|
||||
if (aWhichFrame > FRAME_MAX_VALUE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mError) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the frame. If it's not there, it's probably the caller's fault for
|
||||
// not waiting for the data to be loaded from the network or not passing
|
||||
// FLAG_SYNC_DECODE
|
||||
DrawableFrameRef frameRef =
|
||||
LookupFrame(GetRequestedFrameIndex(aWhichFrame), mSize, aFlags);
|
||||
if (!frameRef) {
|
||||
// The OS threw this frame away and we couldn't redecode it right now.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a 32-bit surface at the decoded size of the image. If
|
||||
// FLAG_SYNC_DECODE was not passed, we may have substituted a downscaled
|
||||
// version of the image we already had available, so this is not necessarily
|
||||
// the intrinsic size of the image. We'll take the frame rect of the image
|
||||
// into account when we draw, implicitly adding padding so that the caller
|
||||
// doesn't need to worry about frame rects.
|
||||
// XXX(seth): In bug 1247520 we'll remove support for frame rects, rendering
|
||||
// this additional padding unnecessary.
|
||||
RefPtr<DataSourceSurface> surf =
|
||||
Factory::CreateDataSourceSurface(frameRef->GetImageSize(),
|
||||
SurfaceFormat::B8G8R8A8,
|
||||
/* aZero = */ true);
|
||||
if (NS_WARN_IF(!surf)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DataSourceSurface::MappedSurface mapping;
|
||||
if (!surf->Map(DataSourceSurface::MapType::WRITE, &mapping)) {
|
||||
gfxCriticalError() << "RasterImage::CopyFrame failed to map surface";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget> target =
|
||||
Factory::CreateDrawTargetForData(BackendType::CAIRO,
|
||||
mapping.mData,
|
||||
frameRef->GetImageSize(),
|
||||
mapping.mStride,
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
if (!target) {
|
||||
gfxWarning() << "RasterImage::CopyFrame failed in CreateDrawTargetForData";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IntRect intFrameRect = frameRef->GetRect();
|
||||
Rect rect(intFrameRect.x, intFrameRect.y,
|
||||
intFrameRect.width, intFrameRect.height);
|
||||
if (frameRef->IsSinglePixel()) {
|
||||
target->FillRect(rect, ColorPattern(frameRef->SinglePixelColor()),
|
||||
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
|
||||
} else {
|
||||
RefPtr<SourceSurface> srcSurf = frameRef->GetSurface();
|
||||
if (!srcSurf) {
|
||||
RecoverFromInvalidFrames(mSize, aFlags);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Rect srcRect(0, 0, intFrameRect.width, intFrameRect.height);
|
||||
target->DrawSurface(srcSurf, srcRect, rect);
|
||||
}
|
||||
|
||||
target->Flush();
|
||||
surf->Unmap();
|
||||
|
||||
return surf.forget();
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
|
||||
RasterImage::GetFrame(uint32_t aWhichFrame,
|
||||
uint32_t aFlags)
|
||||
|
@ -603,21 +525,7 @@ RasterImage::GetFrameInternal(const IntSize& aSize,
|
|||
return MakePair(DrawResult::TEMPORARY_ERROR, RefPtr<SourceSurface>());
|
||||
}
|
||||
|
||||
// If this frame covers the entire image, we can just reuse its existing
|
||||
// surface.
|
||||
RefPtr<SourceSurface> frameSurf;
|
||||
if (!frameRef->NeedsPadding() &&
|
||||
frameRef->GetSize() == frameRef->GetImageSize()) {
|
||||
frameSurf = frameRef->GetSurface();
|
||||
}
|
||||
|
||||
// The image doesn't have a usable surface because it's been optimized away or
|
||||
// because it's a partial update frame from an animation. Create one. (In this
|
||||
// case we fall back to returning a surface at our intrinsic size, even if a
|
||||
// different size was originally specified.)
|
||||
if (!frameSurf) {
|
||||
frameSurf = CopyFrame(aWhichFrame, aFlags);
|
||||
}
|
||||
RefPtr<SourceSurface> frameSurf = frameRef->GetSurface();
|
||||
|
||||
if (!frameRef->IsFinished()) {
|
||||
return MakePair(DrawResult::INCOMPLETE, Move(frameSurf));
|
||||
|
@ -1640,7 +1548,7 @@ RasterImage::NotifyProgress(Progress aProgress,
|
|||
}
|
||||
|
||||
// We may have decoded new animation frames; update our animation state.
|
||||
MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState);
|
||||
MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState || mError);
|
||||
if (mAnimationState && aFrameCount) {
|
||||
mAnimationState->UpdateKnownFrameCount(*aFrameCount);
|
||||
}
|
||||
|
|
|
@ -258,9 +258,6 @@ private:
|
|||
gfx::SamplingFilter aSamplingFilter,
|
||||
uint32_t aFlags);
|
||||
|
||||
already_AddRefed<gfx::SourceSurface> CopyFrame(uint32_t aWhichFrame,
|
||||
uint32_t aFlags);
|
||||
|
||||
Pair<DrawResult, RefPtr<gfx::SourceSurface>>
|
||||
GetFrameInternal(const gfx::IntSize& aSize,
|
||||
uint32_t aWhichFrame,
|
||||
|
|
|
@ -172,7 +172,6 @@ imgFrame::imgFrame()
|
|||
, mPalettedImageData(nullptr)
|
||||
, mPaletteDepth(0)
|
||||
, mNonPremult(false)
|
||||
, mSinglePixel(false)
|
||||
, mCompositingFailed(false)
|
||||
{
|
||||
}
|
||||
|
@ -207,6 +206,19 @@ imgFrame::InitForDecoder(const nsIntSize& aImageSize,
|
|||
mImageSize = aImageSize;
|
||||
mFrameRect = aRect;
|
||||
|
||||
// We only allow a non-trivial frame rect (i.e., a frame rect that doesn't
|
||||
// cover the entire image) for paletted animation frames. We never draw those
|
||||
// frames directly; we just use FrameAnimator to composite them and produce a
|
||||
// BGRA surface that we actually draw. We enforce this here to make sure that
|
||||
// imgFrame::Draw(), which is responsible for drawing all other kinds of
|
||||
// frames, never has to deal with a non-trivial frame rect.
|
||||
if (aPaletteDepth == 0 &&
|
||||
!mFrameRect.IsEqualEdges(IntRect(IntPoint(), mImageSize))) {
|
||||
MOZ_ASSERT_UNREACHABLE("Creating a non-paletted imgFrame with a "
|
||||
"non-trivial frame rect");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mFormat = aFormat;
|
||||
mPaletteDepth = aPaletteDepth;
|
||||
mNonPremult = aNonPremult;
|
||||
|
@ -412,59 +424,16 @@ imgFrame::Optimize()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mPalettedImageData || mOptSurface || mSinglePixel) {
|
||||
if (mPalettedImageData || mOptSurface) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Don't do single-color opts on non-premult data.
|
||||
// Cairo doesn't support non-premult single-colors.
|
||||
// XXX(seth): It's currently unclear if there's any reason why we can't
|
||||
// optimize non-premult surfaces. We should look into removing this.
|
||||
if (mNonPremult) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* Figure out if the entire image is a constant color */
|
||||
|
||||
if (gfxPrefs::ImageSingleColorOptimizationEnabled() &&
|
||||
mImageSurface->Stride() == mFrameRect.width * 4) {
|
||||
uint32_t* imgData = (uint32_t*) ((uint8_t*) mVBufPtr);
|
||||
uint32_t firstPixel = * (uint32_t*) imgData;
|
||||
uint32_t pixelCount = mFrameRect.Area() + 1;
|
||||
|
||||
while (--pixelCount && *imgData++ == firstPixel)
|
||||
;
|
||||
|
||||
if (pixelCount == 0) {
|
||||
// all pixels were the same
|
||||
if (mFormat == SurfaceFormat::B8G8R8A8 ||
|
||||
mFormat == SurfaceFormat::B8G8R8X8) {
|
||||
mSinglePixel = true;
|
||||
mSinglePixelColor.a = ((firstPixel >> 24) & 0xFF) * (1.0f / 255.0f);
|
||||
mSinglePixelColor.r = ((firstPixel >> 16) & 0xFF) * (1.0f / 255.0f);
|
||||
mSinglePixelColor.g = ((firstPixel >> 8) & 0xFF) * (1.0f / 255.0f);
|
||||
mSinglePixelColor.b = ((firstPixel >> 0) & 0xFF) * (1.0f / 255.0f);
|
||||
mSinglePixelColor.r /= mSinglePixelColor.a;
|
||||
mSinglePixelColor.g /= mSinglePixelColor.a;
|
||||
mSinglePixelColor.b /= mSinglePixelColor.a;
|
||||
|
||||
// blow away the older surfaces (if they exist), to release their memory
|
||||
mVBuf = nullptr;
|
||||
mVBufPtr = nullptr;
|
||||
mImageSurface = nullptr;
|
||||
mOptSurface = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// if it's not RGB24/ARGB32, don't optimize, but we never hit this at the
|
||||
// moment
|
||||
}
|
||||
|
||||
const bool usedSingleColorOptimizationUsefully = mSinglePixel &&
|
||||
mFrameRect.Area() > 1;
|
||||
Telemetry::Accumulate(Telemetry::IMAGE_OPTIMIZE_TO_SINGLE_COLOR_USED,
|
||||
usedSingleColorOptimizationUsefully);
|
||||
|
||||
#ifdef ANDROID
|
||||
SurfaceFormat optFormat = gfxPlatform::GetPlatform()
|
||||
->Optimal2DFormatForContent(gfxContentType::COLOR);
|
||||
|
@ -559,63 +528,48 @@ imgFrame::SetRawAccessOnly()
|
|||
|
||||
|
||||
imgFrame::SurfaceWithFormat
|
||||
imgFrame::SurfaceForDrawing(bool aDoPadding,
|
||||
bool aDoPartialDecode,
|
||||
imgFrame::SurfaceForDrawing(bool aDoPartialDecode,
|
||||
bool aDoTile,
|
||||
gfxContext* aContext,
|
||||
const nsIntMargin& aPadding,
|
||||
gfxRect& aImageRect,
|
||||
ImageRegion& aRegion,
|
||||
SourceSurface* aSurface)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
|
||||
IntSize size(int32_t(aImageRect.Width()), int32_t(aImageRect.Height()));
|
||||
if (!aDoPadding && !aDoPartialDecode) {
|
||||
NS_ASSERTION(!mSinglePixel, "This should already have been handled");
|
||||
return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, size), mFormat);
|
||||
if (!aDoPartialDecode) {
|
||||
return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, mImageSize),
|
||||
mFormat);
|
||||
}
|
||||
|
||||
gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width,
|
||||
mDecoded.height);
|
||||
|
||||
if (aDoTile || mSinglePixel) {
|
||||
if (aDoTile) {
|
||||
// Create a temporary surface.
|
||||
// Give this surface an alpha channel because there are
|
||||
// transparent pixels in the padding or undecoded area
|
||||
RefPtr<DrawTarget> target =
|
||||
gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenContentDrawTarget(size, SurfaceFormat::B8G8R8A8);
|
||||
CreateOffscreenContentDrawTarget(mImageSize, SurfaceFormat::B8G8R8A8);
|
||||
if (!target) {
|
||||
return SurfaceWithFormat();
|
||||
}
|
||||
|
||||
// Fill 'available' with whatever we've got
|
||||
if (mSinglePixel) {
|
||||
target->FillRect(ToRect(aRegion.Intersect(available).Rect()),
|
||||
ColorPattern(mSinglePixelColor),
|
||||
DrawOptions(1.0f, CompositionOp::OP_SOURCE));
|
||||
} else {
|
||||
SurfacePattern pattern(aSurface,
|
||||
aRegion.GetExtendMode(),
|
||||
Matrix::Translation(mDecoded.x, mDecoded.y));
|
||||
target->FillRect(ToRect(aRegion.Intersect(available).Rect()), pattern);
|
||||
}
|
||||
SurfacePattern pattern(aSurface,
|
||||
aRegion.GetExtendMode(),
|
||||
Matrix::Translation(mDecoded.x, mDecoded.y));
|
||||
target->FillRect(ToRect(aRegion.Intersect(available).Rect()), pattern);
|
||||
|
||||
RefPtr<SourceSurface> newsurf = target->Snapshot();
|
||||
return SurfaceWithFormat(new gfxSurfaceDrawable(newsurf, size),
|
||||
return SurfaceWithFormat(new gfxSurfaceDrawable(newsurf, mImageSize),
|
||||
target->GetFormat());
|
||||
}
|
||||
|
||||
// Not tiling, and we have a surface, so we can account for
|
||||
// padding and/or a partial decode just by twiddling parameters.
|
||||
gfxPoint paddingTopLeft(aPadding.left, aPadding.top);
|
||||
aRegion = aRegion.Intersect(available) - paddingTopLeft;
|
||||
aContext->Multiply(gfxMatrix::Translation(paddingTopLeft));
|
||||
aImageRect = gfxRect(0, 0, mFrameRect.width, mFrameRect.height);
|
||||
|
||||
// a partial decode just by twiddling parameters.
|
||||
aRegion = aRegion.Intersect(available);
|
||||
IntSize availableSize(mDecoded.width, mDecoded.height);
|
||||
|
||||
return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, availableSize),
|
||||
mFormat);
|
||||
}
|
||||
|
@ -631,31 +585,20 @@ bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion,
|
|||
NS_ASSERTION(!aRegion.IsRestricted() ||
|
||||
!aRegion.Rect().Intersect(aRegion.Restriction()).IsEmpty(),
|
||||
"We must be allowed to sample *some* source pixels!");
|
||||
NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!");
|
||||
MOZ_ASSERT(mFrameRect.IsEqualEdges(IntRect(IntPoint(), mImageSize)),
|
||||
"Directly drawing an image with a non-trivial frame rect!");
|
||||
|
||||
if (mPalettedImageData) {
|
||||
MOZ_ASSERT_UNREACHABLE("Directly drawing a paletted image!");
|
||||
return false;
|
||||
}
|
||||
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
nsIntMargin padding(mFrameRect.y,
|
||||
mImageSize.width - mFrameRect.XMost(),
|
||||
mImageSize.height - mFrameRect.YMost(),
|
||||
mFrameRect.x);
|
||||
|
||||
bool doPadding = padding != nsIntMargin(0,0,0,0);
|
||||
bool doPartialDecode = !AreAllPixelsWritten();
|
||||
|
||||
if (mSinglePixel && !doPadding && !doPartialDecode) {
|
||||
if (mSinglePixelColor.a == 0.0) {
|
||||
return true;
|
||||
}
|
||||
RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
|
||||
dt->FillRect(ToRect(aRegion.Rect()),
|
||||
ColorPattern(mSinglePixelColor),
|
||||
DrawOptions(1.0f, aContext->CurrentOp()));
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<SourceSurface> surf = GetSurfaceInternal();
|
||||
if (!surf && !mSinglePixel) {
|
||||
if (!surf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -664,16 +607,8 @@ bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion,
|
|||
!(aImageFlags & imgIContainer::FLAG_CLAMP);
|
||||
|
||||
ImageRegion region(aRegion);
|
||||
// SurfaceForDrawing changes the current transform, and we need it to still
|
||||
// be changed when we call gfxUtils::DrawPixelSnapped. We still need to
|
||||
// restore it before returning though.
|
||||
// XXXjwatt In general having functions require someone further up the stack
|
||||
// to undo transform changes that they make is bad practice. We should
|
||||
// change how this code works.
|
||||
gfxContextMatrixAutoSaveRestore autoSR(aContext);
|
||||
SurfaceWithFormat surfaceResult =
|
||||
SurfaceForDrawing(doPadding, doPartialDecode, doTile, aContext,
|
||||
padding, imageRect, region, surf);
|
||||
SurfaceForDrawing(doPartialDecode, doTile, region, surf);
|
||||
|
||||
if (surfaceResult.IsValid()) {
|
||||
gfxUtils::DrawPixelSnapped(aContext, surfaceResult.mDrawable,
|
||||
|
@ -921,20 +856,6 @@ imgFrame::SetOptimizable()
|
|||
mOptimizable = true;
|
||||
}
|
||||
|
||||
Color
|
||||
imgFrame::SinglePixelColor() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mSinglePixelColor;
|
||||
}
|
||||
|
||||
bool
|
||||
imgFrame::IsSinglePixel() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mSinglePixel;
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface>
|
||||
imgFrame::GetSurface()
|
||||
{
|
||||
|
|
|
@ -321,7 +321,6 @@ public:
|
|||
IntSize GetImageSize() const { return mImageSize; }
|
||||
IntRect GetRect() const { return mFrameRect; }
|
||||
IntSize GetSize() const { return mFrameRect.Size(); }
|
||||
bool NeedsPadding() const { return mFrameRect.TopLeft() != IntPoint(0, 0); }
|
||||
void GetImageData(uint8_t** aData, uint32_t* length) const;
|
||||
uint8_t* GetImageData() const;
|
||||
|
||||
|
@ -337,9 +336,6 @@ public:
|
|||
|
||||
void SetOptimizable();
|
||||
|
||||
Color SinglePixelColor() const;
|
||||
bool IsSinglePixel() const;
|
||||
|
||||
already_AddRefed<SourceSurface> GetSurface();
|
||||
|
||||
void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, size_t& aHeapSizeOut,
|
||||
|
@ -379,12 +375,8 @@ private: // methods
|
|||
bool IsValid() { return !!mDrawable; }
|
||||
};
|
||||
|
||||
SurfaceWithFormat SurfaceForDrawing(bool aDoPadding,
|
||||
bool aDoPartialDecode,
|
||||
SurfaceWithFormat SurfaceForDrawing(bool aDoPartialDecode,
|
||||
bool aDoTile,
|
||||
gfxContext* aContext,
|
||||
const nsIntMargin& aPadding,
|
||||
gfxRect& aImageRect,
|
||||
ImageRegion& aRegion,
|
||||
SourceSurface* aSurface);
|
||||
|
||||
|
@ -445,10 +437,6 @@ private: // data
|
|||
// Main-thread-only mutable data.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Note that the data stored in gfx::Color is *non-alpha-premultiplied*.
|
||||
Color mSinglePixelColor;
|
||||
|
||||
bool mSinglePixel;
|
||||
bool mCompositingFailed;
|
||||
};
|
||||
|
||||
|
|
|
@ -345,84 +345,6 @@ AnnotateSystemError()
|
|||
nsPrintfCString("%lld", error));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AnnotateProcessInformation(base::ProcessId aPid)
|
||||
{
|
||||
#ifdef XP_WIN
|
||||
ScopedProcessHandle processHandle(
|
||||
OpenProcess(READ_CONTROL|PROCESS_QUERY_INFORMATION, FALSE, aPid));
|
||||
if (!processHandle) {
|
||||
CrashReporter::AnnotateCrashReport(
|
||||
NS_LITERAL_CSTRING("IPCExtraSystemError"),
|
||||
nsPrintfCString("Failed to get information of process %d, error(%d)",
|
||||
aPid,
|
||||
::GetLastError()));
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD exitCode = 0;
|
||||
if (!::GetExitCodeProcess(processHandle, &exitCode)) {
|
||||
CrashReporter::AnnotateCrashReport(
|
||||
NS_LITERAL_CSTRING("IPCExtraSystemError"),
|
||||
nsPrintfCString("Failed to get exit information of process %d, error(%d)",
|
||||
aPid,
|
||||
::GetLastError()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (exitCode != STILL_ACTIVE) {
|
||||
CrashReporter::AnnotateCrashReport(
|
||||
NS_LITERAL_CSTRING("IPCExtraSystemError"),
|
||||
nsPrintfCString("Process %d is not alive. Exit code: %d",
|
||||
aPid,
|
||||
exitCode));
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedPSecurityDescriptor secDesc(nullptr);
|
||||
PSID ownerSid = nullptr;
|
||||
DWORD rv = ::GetSecurityInfo(processHandle,
|
||||
SE_KERNEL_OBJECT,
|
||||
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
|
||||
&ownerSid,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&secDesc.rwget());
|
||||
if (rv != ERROR_SUCCESS) {
|
||||
// GetSecurityInfo() failed.
|
||||
CrashReporter::AnnotateCrashReport(
|
||||
NS_LITERAL_CSTRING("IPCExtraSystemError"),
|
||||
nsPrintfCString("Failed to get security information of process %d,"
|
||||
" error(%d)",
|
||||
aPid,
|
||||
rv));
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedLPTStr ownerSidStr(nullptr);
|
||||
nsString annotation{};
|
||||
annotation.AppendLiteral("Owner: ");
|
||||
if (::ConvertSidToStringSid(ownerSid, &ownerSidStr.rwget())) {
|
||||
annotation.Append(ownerSidStr.get());
|
||||
}
|
||||
|
||||
ScopedLPTStr secDescStr(nullptr);
|
||||
annotation.AppendLiteral(", Security Descriptor: ");
|
||||
if (::ConvertSecurityDescriptorToStringSecurityDescriptor(secDesc,
|
||||
SDDL_REVISION_1,
|
||||
DACL_SECURITY_INFORMATION,
|
||||
&secDescStr.rwget(),
|
||||
nullptr)) {
|
||||
annotation.Append(secDescStr.get());
|
||||
}
|
||||
|
||||
CrashReporter::AnnotateCrashReport(
|
||||
NS_LITERAL_CSTRING("IPCExtraSystemError"),
|
||||
NS_ConvertUTF16toUTF8(annotation));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
|
|
|
@ -439,10 +439,8 @@ DuplicateHandle(HANDLE aSourceHandle,
|
|||
*/
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
void AnnotateSystemError();
|
||||
void AnnotateProcessInformation(base::ProcessId aPid);
|
||||
#else
|
||||
#define AnnotateSystemError() do { } while (0)
|
||||
#define AnnotateProcessInformation(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
|
|
@ -99,6 +99,10 @@ struct ReturnStopIteration
|
|||
{
|
||||
};
|
||||
|
||||
struct ReturnDeadCPOW
|
||||
{
|
||||
};
|
||||
|
||||
struct ReturnException
|
||||
{
|
||||
JSVariant exn;
|
||||
|
@ -113,6 +117,7 @@ union ReturnStatus
|
|||
{
|
||||
ReturnSuccess;
|
||||
ReturnStopIteration;
|
||||
ReturnDeadCPOW;
|
||||
ReturnException;
|
||||
ReturnObjectOpResult;
|
||||
};
|
||||
|
|
|
@ -73,6 +73,15 @@ WrapperAnswer::ok(ReturnStatus* rs, const JS::ObjectOpResult& result)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WrapperAnswer::deadCPOW(AutoJSAPI& jsapi, ReturnStatus* rs)
|
||||
{
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS_ClearPendingException(cx);
|
||||
*rs = ReturnStatus(ReturnDeadCPOW());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WrapperAnswer::RecvPreventExtensions(const ObjectId& objId, ReturnStatus* rs)
|
||||
{
|
||||
|
@ -83,7 +92,7 @@ WrapperAnswer::RecvPreventExtensions(const ObjectId& objId, ReturnStatus* rs)
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
ObjectOpResult success;
|
||||
if (!JS_PreventExtensions(cx, obj, success))
|
||||
|
@ -115,7 +124,7 @@ WrapperAnswer::RecvGetPropertyDescriptor(const ObjectId& objId, const JSIDVarian
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
LOG("%s.getPropertyDescriptor(%s)", ReceiverObj(objId), Identifier(idVar));
|
||||
|
||||
|
@ -145,7 +154,7 @@ WrapperAnswer::RecvGetOwnPropertyDescriptor(const ObjectId& objId, const JSIDVar
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
LOG("%s.getOwnPropertyDescriptor(%s)", ReceiverObj(objId), Identifier(idVar));
|
||||
|
||||
|
@ -174,7 +183,7 @@ WrapperAnswer::RecvDefineProperty(const ObjectId& objId, const JSIDVariant& idVa
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
LOG("define %s[%s]", ReceiverObj(objId), Identifier(idVar));
|
||||
|
||||
|
@ -202,7 +211,7 @@ WrapperAnswer::RecvDelete(const ObjectId& objId, const JSIDVariant& idVar, Retur
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
LOG("delete %s[%s]", ReceiverObj(objId), Identifier(idVar));
|
||||
|
||||
|
@ -228,7 +237,7 @@ WrapperAnswer::RecvHas(const ObjectId& objId, const JSIDVariant& idVar, ReturnSt
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
LOG("%s.has(%s)", ReceiverObj(objId), Identifier(idVar));
|
||||
|
||||
|
@ -253,7 +262,7 @@ WrapperAnswer::RecvHasOwn(const ObjectId& objId, const JSIDVariant& idVar, Retur
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
LOG("%s.hasOwn(%s)", ReceiverObj(objId), Identifier(idVar));
|
||||
|
||||
|
@ -281,7 +290,7 @@ WrapperAnswer::RecvGet(const ObjectId& objId, const JSVariant& receiverVar,
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(aes, rs);
|
||||
return deadCPOW(aes, rs);
|
||||
|
||||
RootedValue receiver(cx);
|
||||
if (!fromVariant(cx, receiverVar, &receiver))
|
||||
|
@ -314,7 +323,7 @@ WrapperAnswer::RecvSet(const ObjectId& objId, const JSIDVariant& idVar, const JS
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(aes, rs);
|
||||
return deadCPOW(aes, rs);
|
||||
|
||||
LOG("set %s[%s] = %s", ReceiverObj(objId), Identifier(idVar), InVariant(value));
|
||||
|
||||
|
@ -348,7 +357,7 @@ WrapperAnswer::RecvIsExtensible(const ObjectId& objId, ReturnStatus* rs, bool* r
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
LOG("%s.isExtensible()", ReceiverObj(objId));
|
||||
|
||||
|
@ -378,7 +387,7 @@ WrapperAnswer::RecvCallOrConstruct(const ObjectId& objId,
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(aes, rs);
|
||||
return deadCPOW(aes, rs);
|
||||
|
||||
MOZ_ASSERT(argv.Length() >= 2);
|
||||
|
||||
|
@ -473,7 +482,7 @@ WrapperAnswer::RecvHasInstance(const ObjectId& objId, const JSVariant& vVar, Ret
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
LOG("%s.hasInstance(%s)", ReceiverObj(objId), InVariant(vVar));
|
||||
|
||||
|
@ -500,7 +509,7 @@ WrapperAnswer::RecvGetBuiltinClass(const ObjectId& objId, ReturnStatus* rs,
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
LOG("%s.getBuiltinClass()", ReceiverObj(objId));
|
||||
|
||||
|
@ -525,7 +534,7 @@ WrapperAnswer::RecvIsArray(const ObjectId& objId, ReturnStatus* rs,
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
LOG("%s.isArray()", ReceiverObj(objId));
|
||||
|
||||
|
@ -570,7 +579,7 @@ WrapperAnswer::RecvGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectO
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
JS::RootedObject proto(cx);
|
||||
if (!JS_GetPrototype(cx, obj, &proto))
|
||||
|
@ -598,7 +607,7 @@ WrapperAnswer::RecvGetPrototypeIfOrdinary(const ObjectId& objId, ReturnStatus* r
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
JS::RootedObject proto(cx);
|
||||
if (!JS_GetPrototypeIfOrdinary(cx, obj, isOrdinary, &proto))
|
||||
|
@ -623,7 +632,7 @@ WrapperAnswer::RecvRegExpToShared(const ObjectId& objId, ReturnStatus* rs,
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
RootedString sourceJSStr(cx, JS_GetRegExpSource(cx, obj));
|
||||
if (!sourceJSStr)
|
||||
|
@ -649,7 +658,7 @@ WrapperAnswer::RecvGetPropertyKeys(const ObjectId& objId, const uint32_t& flags,
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
LOG("%s.getPropertyKeys()", ReceiverObj(objId));
|
||||
|
||||
|
@ -681,7 +690,7 @@ WrapperAnswer::RecvInstanceOf(const ObjectId& objId, const JSIID& iid, ReturnSta
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
LOG("%s.instanceOf()", ReceiverObj(objId));
|
||||
|
||||
|
@ -707,7 +716,7 @@ WrapperAnswer::RecvDOMInstanceOf(const ObjectId& objId, const int& prototypeID,
|
|||
|
||||
RootedObject obj(cx, findObjectById(cx, objId));
|
||||
if (!obj)
|
||||
return fail(jsapi, rs);
|
||||
return deadCPOW(jsapi, rs);
|
||||
|
||||
LOG("%s.domInstanceOf()", ReceiverObj(objId));
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ class WrapperAnswer : public virtual JavaScriptShared
|
|||
bool fail(dom::AutoJSAPI& jsapi, ReturnStatus* rs);
|
||||
bool ok(ReturnStatus* rs);
|
||||
bool ok(ReturnStatus* rs, const JS::ObjectOpResult& result);
|
||||
bool deadCPOW(dom::AutoJSAPI& jsapi, ReturnStatus* rs);
|
||||
};
|
||||
|
||||
} // namespace jsipc
|
||||
|
|
|
@ -1076,6 +1076,11 @@ WrapperOwner::ok(JSContext* cx, const ReturnStatus& status)
|
|||
if (status.type() == ReturnStatus::TReturnStopIteration)
|
||||
return JS_ThrowStopIteration(cx);
|
||||
|
||||
if (status.type() == ReturnStatus::TReturnDeadCPOW) {
|
||||
JS_ReportError(cx, "operation not possible on dead CPOW");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue exn(cx);
|
||||
if (!fromVariant(cx, status.get_ReturnException().exn(), &exn))
|
||||
return false;
|
||||
|
|
|
@ -2554,44 +2554,90 @@ class BaseCompiler
|
|||
{
|
||||
AnyReg src;
|
||||
RegI32 dest;
|
||||
bool isAsmJS;
|
||||
bool isUnsigned;
|
||||
|
||||
public:
|
||||
OutOfLineTruncateF32OrF64ToI32(AnyReg src, RegI32 dest)
|
||||
OutOfLineTruncateF32OrF64ToI32(AnyReg src, RegI32 dest, bool isAsmJS, bool isUnsigned)
|
||||
: src(src),
|
||||
dest(dest)
|
||||
{}
|
||||
dest(dest),
|
||||
isAsmJS(isAsmJS),
|
||||
isUnsigned(isUnsigned)
|
||||
{
|
||||
MOZ_ASSERT_IF(isAsmJS, !isUnsigned);
|
||||
}
|
||||
|
||||
virtual void generate(MacroAssembler& masm) {
|
||||
// isWasm must be true (for now), see bug 1279876 for related issues.
|
||||
bool isWasm = true;
|
||||
bool isFloat = src.tag == AnyReg::F32;
|
||||
FloatRegister fsrc = isFloat ? src.f32().reg : src.f64().reg;
|
||||
saveVolatileReturnGPR(masm);
|
||||
masm.outOfLineTruncateSlow(fsrc, dest.reg, isFloat, isWasm);
|
||||
restoreVolatileReturnGPR(masm);
|
||||
masm.jump(rejoin());
|
||||
if (isAsmJS) {
|
||||
saveVolatileReturnGPR(masm);
|
||||
masm.outOfLineTruncateSlow(fsrc, dest.reg, isFloat, /* isAsmJS */ true);
|
||||
restoreVolatileReturnGPR(masm);
|
||||
masm.jump(rejoin());
|
||||
} else {
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
if (isFloat)
|
||||
masm.outOfLineWasmTruncateFloat32ToInt32(fsrc, isUnsigned, rejoin());
|
||||
else
|
||||
masm.outOfLineWasmTruncateDoubleToInt32(fsrc, isUnsigned, rejoin());
|
||||
#else
|
||||
MOZ_CRASH("BaseCompiler platform hook: OutOfLineTruncateF32OrF64ToI32 wasm");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MOZ_MUST_USE
|
||||
bool truncateF32ToI32(RegF32 src, RegI32 dest) {
|
||||
OutOfLineCode* ool =
|
||||
addOutOfLineCode(new (alloc_) OutOfLineTruncateF32OrF64ToI32(AnyReg(src),
|
||||
dest));
|
||||
if (!ool)
|
||||
return false;
|
||||
masm.branchTruncateFloat32ToInt32(src.reg, dest.reg, ool->entry());
|
||||
bool truncateF32ToI32(RegF32 src, RegI32 dest, bool isUnsigned) {
|
||||
OutOfLineCode* ool;
|
||||
if (isCompilingAsmJS()) {
|
||||
ool = new(alloc_) OutOfLineTruncateF32OrF64ToI32(AnyReg(src), dest, true, false);
|
||||
ool = addOutOfLineCode(ool);
|
||||
if (!ool)
|
||||
return false;
|
||||
masm.branchTruncateFloat32ToInt32(src.reg, dest.reg, ool->entry());
|
||||
} else {
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
ool = new(alloc_) OutOfLineTruncateF32OrF64ToI32(AnyReg(src), dest, false, isUnsigned);
|
||||
ool = addOutOfLineCode(ool);
|
||||
if (!ool)
|
||||
return false;
|
||||
if (isUnsigned)
|
||||
masm.wasmTruncateFloat32ToUInt32(src.reg, dest.reg, ool->entry());
|
||||
else
|
||||
masm.wasmTruncateFloat32ToInt32(src.reg, dest.reg, ool->entry());
|
||||
#else
|
||||
MOZ_CRASH("BaseCompiler platform hook: truncateF32ToI32 wasm");
|
||||
#endif
|
||||
}
|
||||
masm.bind(ool->rejoin());
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_MUST_USE
|
||||
bool truncateF64ToI32(RegF64 src, RegI32 dest) {
|
||||
OutOfLineCode* ool =
|
||||
addOutOfLineCode(new (alloc_) OutOfLineTruncateF32OrF64ToI32(AnyReg(src),
|
||||
dest));
|
||||
if (!ool)
|
||||
return false;
|
||||
masm.branchTruncateDoubleToInt32(src.reg, dest.reg, ool->entry());
|
||||
bool truncateF64ToI32(RegF64 src, RegI32 dest, bool isUnsigned) {
|
||||
OutOfLineCode* ool;
|
||||
if (isCompilingAsmJS()) {
|
||||
ool = new(alloc_) OutOfLineTruncateF32OrF64ToI32(AnyReg(src), dest, true, false);
|
||||
ool = addOutOfLineCode(ool);
|
||||
if (!ool)
|
||||
return false;
|
||||
masm.branchTruncateDoubleToInt32(src.reg, dest.reg, ool->entry());
|
||||
} else {
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
ool = new(alloc_) OutOfLineTruncateF32OrF64ToI32(AnyReg(src), dest, false, isUnsigned);
|
||||
ool = addOutOfLineCode(ool);
|
||||
if (!ool)
|
||||
return false;
|
||||
if (isUnsigned)
|
||||
masm.wasmTruncateDoubleToUInt32(src.reg, dest.reg, ool->entry());
|
||||
else
|
||||
masm.wasmTruncateDoubleToInt32(src.reg, dest.reg, ool->entry());
|
||||
#else
|
||||
MOZ_CRASH("BaseCompiler platform hook: truncateF32ToI32 wasm");
|
||||
#endif
|
||||
}
|
||||
masm.bind(ool->rejoin());
|
||||
return true;
|
||||
}
|
||||
|
@ -2601,6 +2647,7 @@ class BaseCompiler
|
|||
{
|
||||
AnyReg src;
|
||||
bool isUnsigned;
|
||||
|
||||
public:
|
||||
OutOfLineTruncateCheckF32OrF64ToI64(AnyReg src, bool isUnsigned)
|
||||
: src(src),
|
||||
|
@ -2608,10 +2655,12 @@ class BaseCompiler
|
|||
{}
|
||||
|
||||
virtual void generate(MacroAssembler& masm) {
|
||||
bool isFloat = src.tag == AnyReg::F32;
|
||||
FloatRegister fsrc = isFloat ? src.f32().reg : src.f64().reg;
|
||||
masm.outOfLineWasmTruncateCheck(fsrc, isFloat ? MIRType::Float32 : MIRType::Double,
|
||||
MIRType::Int64, isUnsigned, rejoin());
|
||||
if (src.tag == AnyReg::F32)
|
||||
masm.outOfLineWasmTruncateFloat32ToInt64(src.f32().reg, isUnsigned, rejoin());
|
||||
else if (src.tag == AnyReg::F64)
|
||||
masm.outOfLineWasmTruncateDoubleToInt64(src.f64().reg, isUnsigned, rejoin());
|
||||
else
|
||||
MOZ_CRASH("unexpected type");
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
@ -4276,7 +4325,7 @@ BaseCompiler::emitTruncateF32ToI32()
|
|||
{
|
||||
RegF32 r0 = popF32();
|
||||
RegI32 i0 = needI32();
|
||||
if (!truncateF32ToI32(r0, i0))
|
||||
if (!truncateF32ToI32(r0, i0, isUnsigned))
|
||||
return false;
|
||||
freeF32(r0);
|
||||
pushI32(i0);
|
||||
|
@ -4309,7 +4358,7 @@ BaseCompiler::emitTruncateF64ToI32()
|
|||
{
|
||||
RegF64 r0 = popF64();
|
||||
RegI32 i0 = needI32();
|
||||
if (!truncateF64ToI32(r0, i0))
|
||||
if (!truncateF64ToI32(r0, i0, isUnsigned))
|
||||
return false;
|
||||
freeF64(r0);
|
||||
pushI32(i0);
|
||||
|
|
|
@ -96,6 +96,8 @@ class GeneratedSourceMap
|
|||
void setTotalLines(uint32_t val) { totalLines_ = val; }
|
||||
};
|
||||
|
||||
typedef UniquePtr<GeneratedSourceMap> UniqueGeneratedSourceMap;
|
||||
|
||||
// Translate the given binary representation of a wasm module into the module's textual
|
||||
// representation.
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#ifdef JS_ION_PERF
|
||||
# include "jit/PerfSpewer.h"
|
||||
#endif
|
||||
#include "vm/StringBuffer.h"
|
||||
#ifdef MOZ_VTUNE
|
||||
# include "vtune/VTuneWrapper.h"
|
||||
#endif
|
||||
|
@ -79,8 +80,8 @@ static void
|
|||
StaticallyLink(CodeSegment& cs, const LinkData& linkData, ExclusiveContext* cx)
|
||||
{
|
||||
for (LinkData::InternalLink link : linkData.internalLinks) {
|
||||
uint8_t* patchAt = cs.code() + link.patchAtOffset;
|
||||
void* target = cs.code() + link.targetOffset;
|
||||
uint8_t* patchAt = cs.base() + link.patchAtOffset;
|
||||
void* target = cs.base() + link.targetOffset;
|
||||
if (link.isRawPointerPatch())
|
||||
*(void**)(patchAt) = target;
|
||||
else
|
||||
|
@ -90,7 +91,7 @@ StaticallyLink(CodeSegment& cs, const LinkData& linkData, ExclusiveContext* cx)
|
|||
for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) {
|
||||
const Uint32Vector& offsets = linkData.symbolicLinks[imm];
|
||||
for (size_t i = 0; i < offsets.length(); i++) {
|
||||
uint8_t* patchAt = cs.code() + offsets[i];
|
||||
uint8_t* patchAt = cs.base() + offsets[i];
|
||||
void* target = AddressOf(imm, cx);
|
||||
Assembler::PatchDataWithValueCheck(CodeLocationLabel(patchAt),
|
||||
PatchedImmPtr(target),
|
||||
|
@ -108,13 +109,13 @@ static void
|
|||
SpecializeToMemory(CodeSegment& cs, const Metadata& metadata, HandleWasmMemoryObject memory)
|
||||
{
|
||||
for (const BoundsCheck& check : metadata.boundsChecks)
|
||||
Assembler::UpdateBoundsCheck(check.patchAt(cs.code()), memory->buffer().byteLength());
|
||||
Assembler::UpdateBoundsCheck(check.patchAt(cs.base()), memory->buffer().byteLength());
|
||||
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
uint8_t* base = memory->buffer().dataPointerEither().unwrap();
|
||||
for (const MemoryAccess& access : metadata.memoryAccesses) {
|
||||
// Patch memory pointer immediate.
|
||||
void* addr = access.patchMemoryPtrImmAt(cs.code());
|
||||
void* addr = access.patchMemoryPtrImmAt(cs.base());
|
||||
uint32_t disp = reinterpret_cast<uint32_t>(X86Encoding::GetPointer(addr));
|
||||
MOZ_ASSERT(disp <= INT32_MAX);
|
||||
X86Encoding::SetPointer(addr, (void*)(base + disp));
|
||||
|
@ -140,8 +141,8 @@ SendCodeRangesToProfiler(JSContext* cx, CodeSegment& cs, const Bytes& bytecode,
|
|||
if (!codeRange.isFunction())
|
||||
continue;
|
||||
|
||||
uintptr_t start = uintptr_t(cs.code() + codeRange.begin());
|
||||
uintptr_t end = uintptr_t(cs.code() + codeRange.end());
|
||||
uintptr_t start = uintptr_t(cs.base() + codeRange.begin());
|
||||
uintptr_t end = uintptr_t(cs.base() + codeRange.end());
|
||||
uintptr_t size = end - start;
|
||||
|
||||
TwoByteName name(cx);
|
||||
|
@ -207,26 +208,28 @@ CodeSegment::create(JSContext* cx,
|
|||
if (!cs->bytes_)
|
||||
return nullptr;
|
||||
|
||||
uint8_t* codeBase = cs->base();
|
||||
|
||||
cs->functionCodeLength_ = linkData.functionCodeLength;
|
||||
cs->codeLength_ = bytecode.length();
|
||||
cs->globalDataLength_ = linkData.globalDataLength;
|
||||
cs->interruptCode_ = cs->code() + linkData.interruptOffset;
|
||||
cs->outOfBoundsCode_ = cs->code() + linkData.outOfBoundsOffset;
|
||||
cs->unalignedAccessCode_ = cs->code() + linkData.unalignedAccessOffset;
|
||||
cs->badIndirectCallCode_ = cs->code() + linkData.badIndirectCallOffset;
|
||||
cs->interruptCode_ = codeBase + linkData.interruptOffset;
|
||||
cs->outOfBoundsCode_ = codeBase + linkData.outOfBoundsOffset;
|
||||
cs->unalignedAccessCode_ = codeBase + linkData.unalignedAccessOffset;
|
||||
cs->badIndirectCallCode_ = codeBase + linkData.badIndirectCallOffset;
|
||||
|
||||
{
|
||||
JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
|
||||
AutoFlushICache afc("CodeSegment::create");
|
||||
AutoFlushICache::setRange(uintptr_t(cs->code()), cs->codeLength());
|
||||
AutoFlushICache::setRange(uintptr_t(codeBase), cs->codeLength());
|
||||
|
||||
memcpy(cs->code(), bytecode.begin(), bytecode.length());
|
||||
memcpy(codeBase, bytecode.begin(), bytecode.length());
|
||||
StaticallyLink(*cs, linkData, cx);
|
||||
if (memory)
|
||||
SpecializeToMemory(*cs, metadata, memory);
|
||||
}
|
||||
|
||||
if (!ExecutableAllocator::makeExecutable(cs->code(), cs->codeLength())) {
|
||||
if (!ExecutableAllocator::makeExecutable(codeBase, cs->codeLength())) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -551,3 +554,269 @@ Metadata::getFuncName(JSContext* cx, const Bytes* maybeBytecode, uint32_t funcIn
|
|||
CopyAndInflateChars(name->begin(), chars.get(), name->length());
|
||||
return true;
|
||||
}
|
||||
|
||||
Code::Code(UniqueCodeSegment segment,
|
||||
const Metadata& metadata,
|
||||
const ShareableBytes* maybeBytecode)
|
||||
: segment_(Move(segment)),
|
||||
metadata_(&metadata),
|
||||
maybeBytecode_(maybeBytecode),
|
||||
profilingEnabled_(false)
|
||||
{}
|
||||
|
||||
struct CallSiteRetAddrOffset
|
||||
{
|
||||
const CallSiteVector& callSites;
|
||||
explicit CallSiteRetAddrOffset(const CallSiteVector& callSites) : callSites(callSites) {}
|
||||
uint32_t operator[](size_t index) const {
|
||||
return callSites[index].returnAddressOffset();
|
||||
}
|
||||
};
|
||||
|
||||
const CallSite*
|
||||
Code::lookupCallSite(void* returnAddress) const
|
||||
{
|
||||
uint32_t target = ((uint8_t*)returnAddress) - segment_->base();
|
||||
size_t lowerBound = 0;
|
||||
size_t upperBound = metadata_->callSites.length();
|
||||
|
||||
size_t match;
|
||||
if (!BinarySearch(CallSiteRetAddrOffset(metadata_->callSites), lowerBound, upperBound, target, &match))
|
||||
return nullptr;
|
||||
|
||||
return &metadata_->callSites[match];
|
||||
}
|
||||
|
||||
const CodeRange*
|
||||
Code::lookupRange(void* pc) const
|
||||
{
|
||||
CodeRange::PC target((uint8_t*)pc - segment_->base());
|
||||
size_t lowerBound = 0;
|
||||
size_t upperBound = metadata_->codeRanges.length();
|
||||
|
||||
size_t match;
|
||||
if (!BinarySearch(metadata_->codeRanges, lowerBound, upperBound, target, &match))
|
||||
return nullptr;
|
||||
|
||||
return &metadata_->codeRanges[match];
|
||||
}
|
||||
|
||||
#ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS
|
||||
struct MemoryAccessOffset
|
||||
{
|
||||
const MemoryAccessVector& accesses;
|
||||
explicit MemoryAccessOffset(const MemoryAccessVector& accesses) : accesses(accesses) {}
|
||||
uintptr_t operator[](size_t index) const {
|
||||
return accesses[index].insnOffset();
|
||||
}
|
||||
};
|
||||
|
||||
const MemoryAccess*
|
||||
Code::lookupMemoryAccess(void* pc) const
|
||||
{
|
||||
MOZ_ASSERT(segment_->containsFunctionPC(pc));
|
||||
|
||||
uint32_t target = ((uint8_t*)pc) - segment_->base();
|
||||
size_t lowerBound = 0;
|
||||
size_t upperBound = metadata_->memoryAccesses.length();
|
||||
|
||||
size_t match;
|
||||
if (!BinarySearch(MemoryAccessOffset(metadata_->memoryAccesses), lowerBound, upperBound, target, &match))
|
||||
return nullptr;
|
||||
|
||||
return &metadata_->memoryAccesses[match];
|
||||
}
|
||||
#endif // ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
|
||||
|
||||
bool
|
||||
Code::getFuncName(JSContext* cx, uint32_t funcIndex, TwoByteName* name) const
|
||||
{
|
||||
const Bytes* maybeBytecode = maybeBytecode_ ? &maybeBytecode_.get()->bytes : nullptr;
|
||||
return metadata_->getFuncName(cx, maybeBytecode, funcIndex, name);
|
||||
}
|
||||
|
||||
JSAtom*
|
||||
Code::getFuncAtom(JSContext* cx, uint32_t funcIndex) const
|
||||
{
|
||||
TwoByteName name(cx);
|
||||
if (!getFuncName(cx, funcIndex, &name))
|
||||
return nullptr;
|
||||
|
||||
return AtomizeChars(cx, name.begin(), name.length());
|
||||
}
|
||||
|
||||
const char experimentalWarning[] =
|
||||
"Temporary\n"
|
||||
".--. .--. ____ .-'''-. ,---. ,---.\n"
|
||||
"| |_ | | .' __ `. / _ \\| \\ / |\n"
|
||||
"| _( )_ | |/ ' \\ \\ (`' )/`--'| , \\/ , |\n"
|
||||
"|(_ o _) | ||___| / |(_ o _). | |\\_ /| |\n"
|
||||
"| (_,_) \\ | | _.-` | (_,_). '. | _( )_/ | |\n"
|
||||
"| |/ \\| |.' _ |.---. \\ :| (_ o _) | |\n"
|
||||
"| ' /\\ ` || _( )_ |\\ `-' || (_,_) | |\n"
|
||||
"| / \\ |\\ (_ o _) / \\ / | | | |\n"
|
||||
"`---' `---` '.(_,_).' `-...-' '--' '--'\n"
|
||||
"text support (Work In Progress):\n\n";
|
||||
|
||||
const size_t experimentalWarningLinesCount = 12;
|
||||
|
||||
const char enabledMessage[] =
|
||||
"Restart with developer tools open to view WebAssembly source";
|
||||
|
||||
struct LineComparator
|
||||
{
|
||||
const uint32_t lineno;
|
||||
explicit LineComparator(uint32_t lineno) : lineno(lineno) {}
|
||||
|
||||
int operator()(const ExprLoc& loc) const {
|
||||
return lineno == loc.lineno ? 0 : lineno < loc.lineno ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
JSString*
|
||||
Code::createText(JSContext* cx)
|
||||
{
|
||||
StringBuffer buffer(cx);
|
||||
if (maybeBytecode_) {
|
||||
const Bytes& bytes = maybeBytecode_->bytes;
|
||||
if (!buffer.append(experimentalWarning))
|
||||
return nullptr;
|
||||
|
||||
maybeSourceMap_.reset(cx->runtime()->new_<GeneratedSourceMap>(cx));
|
||||
if (!maybeSourceMap_)
|
||||
return nullptr;
|
||||
|
||||
if (!BinaryToExperimentalText(cx, bytes.begin(), bytes.length(), buffer,
|
||||
ExperimentalTextFormatting(), maybeSourceMap_.get())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// Checking source map invariant: expression and function locations must be sorted
|
||||
// by line number.
|
||||
uint32_t lastLineno = 0;
|
||||
for (const ExprLoc& loc : maybeSourceMap_->exprlocs()) {
|
||||
MOZ_ASSERT(lastLineno <= loc.lineno);
|
||||
lastLineno = loc.lineno;
|
||||
}
|
||||
lastLineno = 0;
|
||||
for (const FunctionLoc& loc : maybeSourceMap_->functionlocs()) {
|
||||
MOZ_ASSERT(lastLineno <= loc.startLineno);
|
||||
MOZ_ASSERT(loc.startLineno <= loc.endLineno);
|
||||
lastLineno = loc.endLineno + 1;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if (!buffer.append(enabledMessage))
|
||||
return nullptr;
|
||||
}
|
||||
return buffer.finishString();
|
||||
}
|
||||
|
||||
bool
|
||||
Code::getLineOffsets(size_t lineno, Vector<uint32_t>& offsets) const
|
||||
{
|
||||
// TODO Ensure text was generated?
|
||||
if (!maybeSourceMap_)
|
||||
return false;
|
||||
|
||||
if (lineno < experimentalWarningLinesCount)
|
||||
return true;
|
||||
|
||||
lineno -= experimentalWarningLinesCount;
|
||||
|
||||
ExprLocVector& exprlocs = maybeSourceMap_->exprlocs();
|
||||
|
||||
// Binary search for the expression with the specified line number and
|
||||
// rewind to the first expression, if more than one expression on the same line.
|
||||
size_t match;
|
||||
if (!BinarySearchIf(exprlocs, 0, exprlocs.length(), LineComparator(lineno), &match))
|
||||
return true;
|
||||
|
||||
while (match > 0 && exprlocs[match - 1].lineno == lineno)
|
||||
match--;
|
||||
|
||||
// Return all expression offsets that were printed on the specified line.
|
||||
for (size_t i = match; i < exprlocs.length() && exprlocs[i].lineno == lineno; i++) {
|
||||
if (!offsets.append(exprlocs[i].offset))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Code::ensureProfilingState(JSContext* cx, bool newProfilingEnabled)
|
||||
{
|
||||
if (profilingEnabled_ == newProfilingEnabled)
|
||||
return true;
|
||||
|
||||
// When enabled, generate profiling labels for every name in funcNames_
|
||||
// that is the name of some Function CodeRange. This involves malloc() so
|
||||
// do it now since, once we start sampling, we'll be in a signal-handing
|
||||
// context where we cannot malloc.
|
||||
if (newProfilingEnabled) {
|
||||
for (const CodeRange& codeRange : metadata_->codeRanges) {
|
||||
if (!codeRange.isFunction())
|
||||
continue;
|
||||
|
||||
TwoByteName name(cx);
|
||||
if (!getFuncName(cx, codeRange.funcIndex(), &name))
|
||||
return false;
|
||||
if (!name.append('\0'))
|
||||
return false;
|
||||
|
||||
UniqueChars label(JS_smprintf("%hs (%s:%u)",
|
||||
name.begin(),
|
||||
metadata_->filename.get(),
|
||||
codeRange.funcLineOrBytecode()));
|
||||
if (!label) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (codeRange.funcIndex() >= funcLabels_.length()) {
|
||||
if (!funcLabels_.resize(codeRange.funcIndex() + 1))
|
||||
return false;
|
||||
}
|
||||
funcLabels_[codeRange.funcIndex()] = Move(label);
|
||||
}
|
||||
} else {
|
||||
funcLabels_.clear();
|
||||
}
|
||||
|
||||
// Only mutate the code after the fallible operations are complete to avoid
|
||||
// the need to rollback.
|
||||
profilingEnabled_ = newProfilingEnabled;
|
||||
|
||||
{
|
||||
AutoWritableJitCode awjc(cx->runtime(), segment_->base(), segment_->codeLength());
|
||||
AutoFlushICache afc("Code::ensureProfilingState");
|
||||
AutoFlushICache::setRange(uintptr_t(segment_->base()), segment_->codeLength());
|
||||
|
||||
for (const CallSite& callSite : metadata_->callSites)
|
||||
ToggleProfiling(*this, callSite, newProfilingEnabled);
|
||||
for (const CallThunk& callThunk : metadata_->callThunks)
|
||||
ToggleProfiling(*this, callThunk, newProfilingEnabled);
|
||||
for (const CodeRange& codeRange : metadata_->codeRanges)
|
||||
ToggleProfiling(*this, codeRange, newProfilingEnabled);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Code::addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
||||
Metadata::SeenSet* seenMetadata,
|
||||
ShareableBytes::SeenSet* seenBytes,
|
||||
size_t* code,
|
||||
size_t* data) const
|
||||
{
|
||||
*code += segment_->codeLength();
|
||||
*data += mallocSizeOf(this) +
|
||||
segment_->globalDataLength() +
|
||||
metadata_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenMetadata);
|
||||
|
||||
if (maybeBytecode_)
|
||||
*data += maybeBytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#ifndef wasm_code_h
|
||||
#define wasm_code_h
|
||||
|
||||
#include "asmjs/WasmBinaryToExperimentalText.h"
|
||||
#include "asmjs/WasmTypes.h"
|
||||
|
||||
namespace js {
|
||||
|
@ -31,6 +32,9 @@ struct LinkData;
|
|||
struct Metadata;
|
||||
|
||||
// A wasm CodeSegment owns the allocated executable code for a wasm module.
|
||||
// This allocation also currently includes the global data segment, which allows
|
||||
// RIP-relative access to global data on some architectures, but this will
|
||||
// change in the future to give global data its own allocation.
|
||||
|
||||
class CodeSegment;
|
||||
typedef UniquePtr<CodeSegment> UniqueCodeSegment;
|
||||
|
@ -73,7 +77,7 @@ class CodeSegment
|
|||
HandleWasmMemoryObject memory);
|
||||
~CodeSegment();
|
||||
|
||||
uint8_t* code() const { return bytes_; }
|
||||
uint8_t* base() const { return bytes_; }
|
||||
uint8_t* globalData() const { return bytes_ + codeLength_; }
|
||||
uint32_t codeLength() const { return codeLength_; }
|
||||
uint32_t globalDataLength() const { return globalDataLength_; }
|
||||
|
@ -90,11 +94,11 @@ class CodeSegment
|
|||
// function code which, in turn, simplifies reasoning about how stubs
|
||||
// enter/exit.
|
||||
|
||||
bool containsFunctionPC(void* pc) const {
|
||||
return pc >= code() && pc < (code() + functionCodeLength_);
|
||||
bool containsFunctionPC(const void* pc) const {
|
||||
return pc >= base() && pc < (base() + functionCodeLength_);
|
||||
}
|
||||
bool containsCodePC(void* pc) const {
|
||||
return pc >= code() && pc < (code() + codeLength_);
|
||||
bool containsCodePC(const void* pc) const {
|
||||
return pc >= base() && pc < (base() + codeLength_);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -512,6 +516,72 @@ struct Metadata : ShareableBase<Metadata>, MetadataCacheablePod
|
|||
typedef RefPtr<Metadata> MutableMetadata;
|
||||
typedef RefPtr<const Metadata> SharedMetadata;
|
||||
|
||||
// Code objects own executable code and the metadata that describes it. At the
|
||||
// moment, Code objects are owned uniquely by instances since CodeSegments are
|
||||
// not shareable. However, once this restriction is removed, a single Code
|
||||
// object will be shared between a module and all its instances.
|
||||
|
||||
class Code
|
||||
{
|
||||
const UniqueCodeSegment segment_;
|
||||
const SharedMetadata metadata_;
|
||||
const SharedBytes maybeBytecode_;
|
||||
UniqueGeneratedSourceMap maybeSourceMap_;
|
||||
CacheableCharsVector funcLabels_;
|
||||
bool profilingEnabled_;
|
||||
|
||||
public:
|
||||
Code(UniqueCodeSegment segment,
|
||||
const Metadata& metadata,
|
||||
const ShareableBytes* maybeBytecode);
|
||||
|
||||
const CodeSegment& segment() const { return *segment_; }
|
||||
const Metadata& metadata() const { return *metadata_; }
|
||||
|
||||
// Frame iterator support:
|
||||
|
||||
const CallSite* lookupCallSite(void* returnAddress) const;
|
||||
const CodeRange* lookupRange(void* pc) const;
|
||||
#ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS
|
||||
const MemoryAccess* lookupMemoryAccess(void* pc) const;
|
||||
#endif
|
||||
|
||||
// Return the name associated with a given function index, or generate one
|
||||
// if none was given by the module.
|
||||
|
||||
bool getFuncName(JSContext* cx, uint32_t funcIndex, TwoByteName* name) const;
|
||||
JSAtom* getFuncAtom(JSContext* cx, uint32_t funcIndex) const;
|
||||
|
||||
// If the source bytecode was saved when this Code was constructed, this
|
||||
// method will render the binary as text. Otherwise, a diagnostic string
|
||||
// will be returned.
|
||||
|
||||
JSString* createText(JSContext* cx);
|
||||
bool getLineOffsets(size_t lineno, Vector<uint32_t>& offsets) const;
|
||||
|
||||
// Each Code has a profiling mode that is updated to match the runtime's
|
||||
// profiling mode when there are no other activations of the code live on
|
||||
// the stack. Once in profiling mode, ProfilingFrameIterator can be used to
|
||||
// asynchronously walk the stack. Otherwise, the ProfilingFrameIterator will
|
||||
// skip any activations of this code.
|
||||
|
||||
MOZ_MUST_USE bool ensureProfilingState(JSContext* cx, bool enabled);
|
||||
bool profilingEnabled() const { return profilingEnabled_; }
|
||||
const char* profilingLabel(uint32_t funcIndex) const { return funcLabels_[funcIndex].get(); }
|
||||
|
||||
// about:memory reporting:
|
||||
|
||||
void addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
||||
Metadata::SeenSet* seenMetadata,
|
||||
ShareableBytes::SeenSet* seenBytes,
|
||||
size_t* code,
|
||||
size_t* data) const;
|
||||
|
||||
WASM_DECLARE_SERIALIZABLE(Code);
|
||||
};
|
||||
|
||||
typedef UniquePtr<Code> UniqueCode;
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2016 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "asmjs/WasmCompartment.h"
|
||||
|
||||
#include "jscompartment.h"
|
||||
|
||||
#include "asmjs/WasmInstance.h"
|
||||
|
||||
#include "vm/Debugger-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace wasm;
|
||||
|
||||
Compartment::Compartment(Zone* zone)
|
||||
: mutatingInstances_(false),
|
||||
instanceObjects_(zone, InstanceObjectSet()),
|
||||
activationCount_(0),
|
||||
profilingEnabled_(false)
|
||||
{}
|
||||
|
||||
Compartment::~Compartment()
|
||||
{
|
||||
MOZ_ASSERT(activationCount_ == 0);
|
||||
MOZ_ASSERT(!instanceObjects_.initialized() || instanceObjects_.empty());
|
||||
MOZ_ASSERT(instances_.empty());
|
||||
MOZ_ASSERT(!mutatingInstances_);
|
||||
}
|
||||
|
||||
struct InstanceComparator
|
||||
{
|
||||
const Instance& target;
|
||||
explicit InstanceComparator(const Instance& target) : target(target) {}
|
||||
|
||||
int operator()(const Instance* instance) const {
|
||||
if (instance == &target)
|
||||
return 0;
|
||||
MOZ_ASSERT(!target.codeSegment().containsCodePC(instance->codeBase()));
|
||||
MOZ_ASSERT(!instance->codeSegment().containsCodePC(target.codeBase()));
|
||||
return target.codeBase() < instance->codeBase() ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
Compartment::trace(JSTracer* trc)
|
||||
{
|
||||
// A WasmInstanceObject that was initially reachable when called can become
|
||||
// unreachable while executing on the stack. Since wasm does not otherwise
|
||||
// scan the stack during GC to identify live instances, we mark all instance
|
||||
// objects live if there is any running wasm in the compartment.
|
||||
if (activationCount_)
|
||||
instanceObjects_.get().trace(trc);
|
||||
}
|
||||
|
||||
bool
|
||||
Compartment::registerInstance(JSContext* cx, HandleWasmInstanceObject instanceObj)
|
||||
{
|
||||
Instance& instance = instanceObj->instance();
|
||||
MOZ_ASSERT(this == &instance.compartment()->wasm);
|
||||
|
||||
if (!instance.ensureProfilingState(cx, profilingEnabled_))
|
||||
return false;
|
||||
|
||||
if (!instanceObjects_.initialized() && !instanceObjects_.init()) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!instanceObjects_.putNew(instanceObj)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t index;
|
||||
if (BinarySearchIf(instances_, 0, instances_.length(), InstanceComparator(instance), &index))
|
||||
MOZ_CRASH("duplicate registration");
|
||||
|
||||
{
|
||||
AutoMutateInstances guard(*this);
|
||||
if (!instances_.insert(instances_.begin() + index, &instance)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Debugger::onNewWasmInstance(cx, instanceObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Compartment::unregisterInstance(Instance& instance)
|
||||
{
|
||||
size_t index;
|
||||
if (!BinarySearchIf(instances_, 0, instances_.length(), InstanceComparator(instance), &index))
|
||||
return;
|
||||
|
||||
AutoMutateInstances guard(*this);
|
||||
instances_.erase(instances_.begin() + index);
|
||||
}
|
||||
|
||||
struct PCComparator
|
||||
{
|
||||
const void* pc;
|
||||
explicit PCComparator(const void* pc) : pc(pc) {}
|
||||
|
||||
int operator()(const Instance* instance) const {
|
||||
if (instance->codeSegment().containsCodePC(pc))
|
||||
return 0;
|
||||
return pc < instance->codeBase() ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
Code*
|
||||
Compartment::lookupCode(const void* pc) const
|
||||
{
|
||||
Instance* instance = lookupInstanceDeprecated(pc);
|
||||
return instance ? &instance->code() : nullptr;
|
||||
}
|
||||
|
||||
Instance*
|
||||
Compartment::lookupInstanceDeprecated(const void* pc) const
|
||||
{
|
||||
// See profilingEnabled().
|
||||
MOZ_ASSERT(!mutatingInstances_);
|
||||
|
||||
size_t index;
|
||||
if (!BinarySearchIf(instances_, 0, instances_.length(), PCComparator(pc), &index))
|
||||
return nullptr;
|
||||
|
||||
return instances_[index];
|
||||
}
|
||||
|
||||
bool
|
||||
Compartment::ensureProfilingState(JSContext* cx)
|
||||
{
|
||||
bool newProfilingEnabled = cx->spsProfiler.enabled();
|
||||
if (profilingEnabled_ == newProfilingEnabled)
|
||||
return true;
|
||||
|
||||
// Since one Instance can call another Instance in the same compartment
|
||||
// directly without calling through Instance::callExport(), when profiling
|
||||
// is enabled, enable it for the entire compartment at once. It is only safe
|
||||
// to enable profiling when the wasm is not on the stack, so delay enabling
|
||||
// profiling until there are no live WasmActivations in this compartment.
|
||||
|
||||
if (activationCount_ > 0)
|
||||
return true;
|
||||
|
||||
for (Instance* instance : instances_) {
|
||||
if (!instance->ensureProfilingState(cx, newProfilingEnabled))
|
||||
return false;
|
||||
}
|
||||
|
||||
profilingEnabled_ = newProfilingEnabled;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Compartment::profilingEnabled() const
|
||||
{
|
||||
// Profiling can asynchronously interrupt the mutation of the instances_
|
||||
// vector which is used by lookupCode() during stack-walking. To handle
|
||||
// this rare case, disable profiling during mutation.
|
||||
return profilingEnabled_ && !mutatingInstances_;
|
||||
}
|
||||
|
||||
void
|
||||
Compartment::addSizeOfExcludingThis(MallocSizeOf mallocSizeOf, size_t* compartmentTables)
|
||||
{
|
||||
*compartmentTables += instanceObjects_.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2016 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef wasm_compartment_h
|
||||
#define wasm_compartment_h
|
||||
|
||||
#include "asmjs/WasmJS.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class WasmActivation;
|
||||
|
||||
namespace wasm {
|
||||
|
||||
class Code;
|
||||
|
||||
// wasm::Compartment lives in JSCompartment and contains the wasm-related
|
||||
// per-compartment state. wasm::Compartment tracks every live instance in the
|
||||
// compartment and must be notified, via registerInstance(), of any new
|
||||
// WasmInstanceObject.
|
||||
|
||||
class Compartment
|
||||
{
|
||||
using InstanceObjectSet = GCHashSet<ReadBarriered<WasmInstanceObject*>,
|
||||
MovableCellHasher<ReadBarriered<WasmInstanceObject*>>,
|
||||
SystemAllocPolicy>;
|
||||
using WeakInstanceObjectSet = JS::WeakCache<InstanceObjectSet>;
|
||||
using InstanceVector = Vector<Instance*, 0, SystemAllocPolicy>;
|
||||
|
||||
InstanceVector instances_;
|
||||
volatile bool mutatingInstances_;
|
||||
WeakInstanceObjectSet instanceObjects_;
|
||||
size_t activationCount_;
|
||||
bool profilingEnabled_;
|
||||
|
||||
friend class js::WasmActivation;
|
||||
|
||||
struct AutoMutateInstances {
|
||||
Compartment &c;
|
||||
explicit AutoMutateInstances(Compartment& c) : c(c) {
|
||||
MOZ_ASSERT(!c.mutatingInstances_);
|
||||
c.mutatingInstances_ = true;
|
||||
}
|
||||
~AutoMutateInstances() {
|
||||
MOZ_ASSERT(c.mutatingInstances_);
|
||||
c.mutatingInstances_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
explicit Compartment(Zone* zone);
|
||||
~Compartment();
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
// Before a WasmInstanceObject can be considered fully constructed and
|
||||
// valid, it must be registered with the Compartment. If this method fails,
|
||||
// an error has been reported and the instance object must be abandoned.
|
||||
// After a successful registration, an Instance must call
|
||||
// unregisterInstance() before being destroyed.
|
||||
|
||||
bool registerInstance(JSContext* cx, HandleWasmInstanceObject instanceObj);
|
||||
void unregisterInstance(Instance& instance);
|
||||
|
||||
// Return a weak set of all live instances in the compartment. Accessing
|
||||
// objects of the set will trigger a read-barrier which will mark the
|
||||
// object.
|
||||
|
||||
const WeakInstanceObjectSet& instanceObjects() const { return instanceObjects_; }
|
||||
|
||||
// This methods returns the wasm::Code containing the given pc, if any
|
||||
// exists in the compartment.
|
||||
|
||||
Code* lookupCode(const void* pc) const;
|
||||
|
||||
// Currently, there is one Code per Instance so it is also possible to
|
||||
// lookup a Instance given a pc. However, the goal is to share one Code
|
||||
// between multiple Instances at which point in time this method will be
|
||||
// removed.
|
||||
|
||||
Instance* lookupInstanceDeprecated(const void* pc) const;
|
||||
|
||||
// To ensure profiling is enabled (so that wasm frames are not lost in
|
||||
// profiling callstacks), ensureProfilingState must be called before calling
|
||||
// the first wasm function in a compartment.
|
||||
|
||||
bool ensureProfilingState(JSContext* cx);
|
||||
bool profilingEnabled() const;
|
||||
|
||||
// about:memory reporting
|
||||
|
||||
void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t* compartmentTables);
|
||||
};
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
#endif // wasm_compartment_h
|
|
@ -45,8 +45,8 @@ CallerFPFromFP(void* fp)
|
|||
}
|
||||
|
||||
FrameIterator::FrameIterator()
|
||||
: cx_(nullptr),
|
||||
instance_(nullptr),
|
||||
: activation_(nullptr),
|
||||
code_(nullptr),
|
||||
callsite_(nullptr),
|
||||
codeRange_(nullptr),
|
||||
fp_(nullptr),
|
||||
|
@ -56,8 +56,8 @@ FrameIterator::FrameIterator()
|
|||
}
|
||||
|
||||
FrameIterator::FrameIterator(const WasmActivation& activation)
|
||||
: cx_(activation.cx()),
|
||||
instance_(&activation.instance()),
|
||||
: activation_(&activation),
|
||||
code_(nullptr),
|
||||
callsite_(nullptr),
|
||||
codeRange_(nullptr),
|
||||
fp_(activation.fp()),
|
||||
|
@ -69,10 +69,15 @@ FrameIterator::FrameIterator(const WasmActivation& activation)
|
|||
}
|
||||
|
||||
void* pc = activation.resumePC();
|
||||
if (!pc)
|
||||
if (!pc) {
|
||||
MOZ_ASSERT(done());
|
||||
return;
|
||||
}
|
||||
|
||||
const CodeRange* codeRange = instance_->lookupCodeRange(pc);
|
||||
code_ = activation_->compartment()->wasm.lookupCode(pc);
|
||||
MOZ_ASSERT(code_);
|
||||
|
||||
const CodeRange* codeRange = code_->lookupRange(pc);
|
||||
MOZ_ASSERT(codeRange);
|
||||
|
||||
if (codeRange->kind() == CodeRange::Function)
|
||||
|
@ -86,9 +91,7 @@ FrameIterator::FrameIterator(const WasmActivation& activation)
|
|||
bool
|
||||
FrameIterator::done() const
|
||||
{
|
||||
return !fp_ &&
|
||||
!codeRange_ &&
|
||||
!missingFrameMessage_;
|
||||
return !codeRange_ && !missingFrameMessage_;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -98,7 +101,7 @@ FrameIterator::operator++()
|
|||
if (fp_) {
|
||||
DebugOnly<uint8_t*> oldfp = fp_;
|
||||
fp_ += callsite_->stackDepth();
|
||||
MOZ_ASSERT_IF(instance_->profilingEnabled(), fp_ == CallerFPFromFP(oldfp));
|
||||
MOZ_ASSERT_IF(code_->profilingEnabled(), fp_ == CallerFPFromFP(oldfp));
|
||||
settle();
|
||||
} else if (codeRange_) {
|
||||
MOZ_ASSERT(codeRange_);
|
||||
|
@ -115,17 +118,20 @@ FrameIterator::settle()
|
|||
{
|
||||
void* returnAddress = ReturnAddressFromFP(fp_);
|
||||
|
||||
const CodeRange* codeRange = instance_->lookupCodeRange(returnAddress);
|
||||
MOZ_ASSERT(codeRange);
|
||||
codeRange_ = codeRange;
|
||||
code_ = activation_->compartment()->wasm.lookupCode(returnAddress);
|
||||
MOZ_ASSERT(code_);
|
||||
|
||||
switch (codeRange->kind()) {
|
||||
codeRange_ = code_->lookupRange(returnAddress);
|
||||
MOZ_ASSERT(codeRange_);
|
||||
|
||||
switch (codeRange_->kind()) {
|
||||
case CodeRange::Function:
|
||||
callsite_ = instance_->lookupCallSite(returnAddress);
|
||||
callsite_ = code_->lookupCallSite(returnAddress);
|
||||
MOZ_ASSERT(callsite_);
|
||||
break;
|
||||
case CodeRange::Entry:
|
||||
fp_ = nullptr;
|
||||
code_ = nullptr;
|
||||
codeRange_ = nullptr;
|
||||
MOZ_ASSERT(done());
|
||||
break;
|
||||
|
@ -137,20 +143,41 @@ FrameIterator::settle()
|
|||
}
|
||||
}
|
||||
|
||||
const char*
|
||||
FrameIterator::filename() const
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
return code_->metadata().filename.get();
|
||||
}
|
||||
|
||||
const char16_t*
|
||||
FrameIterator::displayURL() const
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
return code_->metadata().displayURL();
|
||||
}
|
||||
|
||||
bool
|
||||
FrameIterator::mutedErrors() const
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
return code_->metadata().mutedErrors();
|
||||
}
|
||||
|
||||
JSAtom*
|
||||
FrameIterator::functionDisplayAtom() const
|
||||
{
|
||||
MOZ_ASSERT(!done());
|
||||
|
||||
UniqueChars owner;
|
||||
JSContext* cx = activation_->cx();
|
||||
|
||||
if (missingFrameMessage_) {
|
||||
const char* msg = "asm.js/wasm frames may be missing; enable the profiler before running "
|
||||
"to see all frames";
|
||||
JSAtom* atom = Atomize(cx_, msg, strlen(msg));
|
||||
JSAtom* atom = Atomize(cx, msg, strlen(msg));
|
||||
if (!atom) {
|
||||
cx_->clearPendingException();
|
||||
return cx_->names().empty;
|
||||
cx->clearPendingException();
|
||||
return cx->names().empty;
|
||||
}
|
||||
|
||||
return atom;
|
||||
|
@ -158,10 +185,10 @@ FrameIterator::functionDisplayAtom() const
|
|||
|
||||
MOZ_ASSERT(codeRange_);
|
||||
|
||||
JSAtom* atom = instance_->getFuncAtom(cx_, codeRange_->funcIndex());
|
||||
JSAtom* atom = code_->getFuncAtom(cx, codeRange_->funcIndex());
|
||||
if (!atom) {
|
||||
cx_->clearPendingException();
|
||||
return cx_->names().empty;
|
||||
cx->clearPendingException();
|
||||
return cx->names().empty;
|
||||
}
|
||||
|
||||
return atom;
|
||||
|
@ -172,8 +199,7 @@ FrameIterator::lineOrBytecode() const
|
|||
{
|
||||
MOZ_ASSERT(!done());
|
||||
return callsite_ ? callsite_->lineOrBytecode()
|
||||
: codeRange_ ? codeRange_->funcLineOrBytecode()
|
||||
: 0;
|
||||
: (codeRange_ ? codeRange_->funcLineOrBytecode() : 0);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -187,19 +213,19 @@ FrameIterator::lineOrBytecode() const
|
|||
static const unsigned PushedRetAddr = 0;
|
||||
static const unsigned PostStorePrePopFP = 0;
|
||||
# endif
|
||||
static const unsigned PushedFP = 13;
|
||||
static const unsigned StoredFP = 20;
|
||||
static const unsigned PushedFP = 20;
|
||||
static const unsigned StoredFP = 27;
|
||||
#elif defined(JS_CODEGEN_X86)
|
||||
# if defined(DEBUG)
|
||||
static const unsigned PushedRetAddr = 0;
|
||||
static const unsigned PostStorePrePopFP = 0;
|
||||
# endif
|
||||
static const unsigned PushedFP = 8;
|
||||
static const unsigned StoredFP = 11;
|
||||
static const unsigned PushedFP = 14;
|
||||
static const unsigned StoredFP = 17;
|
||||
#elif defined(JS_CODEGEN_ARM)
|
||||
static const unsigned PushedRetAddr = 4;
|
||||
static const unsigned PushedFP = 16;
|
||||
static const unsigned StoredFP = 20;
|
||||
static const unsigned PushedFP = 20;
|
||||
static const unsigned StoredFP = 24;
|
||||
static const unsigned PostStorePrePopFP = 4;
|
||||
#elif defined(JS_CODEGEN_ARM64)
|
||||
static const unsigned PushedRetAddr = 0;
|
||||
|
@ -208,8 +234,8 @@ static const unsigned StoredFP = 0;
|
|||
static const unsigned PostStorePrePopFP = 0;
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
static const unsigned PushedRetAddr = 8;
|
||||
static const unsigned PushedFP = 24;
|
||||
static const unsigned StoredFP = 28;
|
||||
static const unsigned PushedFP = 28;
|
||||
static const unsigned StoredFP = 32;
|
||||
static const unsigned PostStorePrePopFP = 4;
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
# if defined(DEBUG)
|
||||
|
@ -250,7 +276,7 @@ GenerateProfilingPrologue(MacroAssembler& masm, unsigned framePushed, ExitReason
|
|||
// randomly inserted between two instructions.
|
||||
{
|
||||
#if defined(JS_CODEGEN_ARM)
|
||||
AutoForbidPools afp(&masm, /* number of instructions in scope = */ 5);
|
||||
AutoForbidPools afp(&masm, /* number of instructions in scope = */ 6);
|
||||
#endif
|
||||
|
||||
offsets->begin = masm.currentOffset();
|
||||
|
@ -431,7 +457,8 @@ wasm::GenerateExitEpilogue(MacroAssembler& masm, unsigned framePushed, ExitReaso
|
|||
// ProfilingFrameIterator
|
||||
|
||||
ProfilingFrameIterator::ProfilingFrameIterator()
|
||||
: instance_(nullptr),
|
||||
: activation_(nullptr),
|
||||
code_(nullptr),
|
||||
codeRange_(nullptr),
|
||||
callerFP_(nullptr),
|
||||
callerPC_(nullptr),
|
||||
|
@ -442,7 +469,8 @@ ProfilingFrameIterator::ProfilingFrameIterator()
|
|||
}
|
||||
|
||||
ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation)
|
||||
: instance_(&activation.instance()),
|
||||
: activation_(&activation),
|
||||
code_(nullptr),
|
||||
codeRange_(nullptr),
|
||||
callerFP_(nullptr),
|
||||
callerPC_(nullptr),
|
||||
|
@ -454,35 +482,41 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation)
|
|||
// happens if profiling is enabled while the instance is on the stack (in
|
||||
// which case profiling will be enabled when the instance becomes inactive
|
||||
// and gets called again).
|
||||
if (!instance_->profilingEnabled()) {
|
||||
if (!activation_->compartment()->wasm.profilingEnabled()) {
|
||||
MOZ_ASSERT(done());
|
||||
return;
|
||||
}
|
||||
|
||||
initFromFP(activation);
|
||||
initFromFP();
|
||||
}
|
||||
|
||||
static inline void
|
||||
AssertMatchesCallSite(const Instance& instance, void* callerPC, void* callerFP, void* fp)
|
||||
AssertMatchesCallSite(const WasmActivation& activation, void* callerPC, void* callerFP, void* fp)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
const CodeRange* callerCodeRange = instance.lookupCodeRange(callerPC);
|
||||
Code* code = activation.compartment()->wasm.lookupCode(callerPC);
|
||||
MOZ_ASSERT(code);
|
||||
|
||||
const CodeRange* callerCodeRange = code->lookupRange(callerPC);
|
||||
MOZ_ASSERT(callerCodeRange);
|
||||
|
||||
if (callerCodeRange->kind() == CodeRange::Entry) {
|
||||
MOZ_ASSERT(callerFP == nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
const CallSite* callsite = instance.lookupCallSite(callerPC);
|
||||
const CallSite* callsite = code->lookupCallSite(callerPC);
|
||||
MOZ_ASSERT(callsite);
|
||||
|
||||
MOZ_ASSERT(callerFP == (uint8_t*)fp + callsite->stackDepth());
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ProfilingFrameIterator::initFromFP(const WasmActivation& activation)
|
||||
ProfilingFrameIterator::initFromFP()
|
||||
{
|
||||
uint8_t* fp = activation.fp();
|
||||
uint8_t* fp = activation_->fp();
|
||||
stackAddress_ = fp;
|
||||
|
||||
// If a signal was handled while entering an activation, the frame will
|
||||
// still be null.
|
||||
|
@ -491,6 +525,14 @@ ProfilingFrameIterator::initFromFP(const WasmActivation& activation)
|
|||
return;
|
||||
}
|
||||
|
||||
void* pc = ReturnAddressFromFP(fp);
|
||||
|
||||
code_ = activation_->compartment()->wasm.lookupCode(pc);
|
||||
MOZ_ASSERT(code_);
|
||||
|
||||
codeRange_ = code_->lookupRange(pc);
|
||||
MOZ_ASSERT(codeRange_);
|
||||
|
||||
// Since we don't have the pc for fp, start unwinding at the caller of fp
|
||||
// (ReturnAddressFromFP(fp)). This means that the innermost frame is
|
||||
// skipped. This is fine because:
|
||||
|
@ -499,13 +541,7 @@ ProfilingFrameIterator::initFromFP(const WasmActivation& activation)
|
|||
// - for Math and other builtin calls as well as interrupts, we note the absence
|
||||
// of an exit reason and inject a fake "builtin" frame; and
|
||||
// - for async interrupts, we just accept that we'll lose the innermost frame.
|
||||
void* pc = ReturnAddressFromFP(fp);
|
||||
const CodeRange* codeRange = instance_->lookupCodeRange(pc);
|
||||
MOZ_ASSERT(codeRange);
|
||||
codeRange_ = codeRange;
|
||||
stackAddress_ = fp;
|
||||
|
||||
switch (codeRange->kind()) {
|
||||
switch (codeRange_->kind()) {
|
||||
case CodeRange::Entry:
|
||||
callerPC_ = nullptr;
|
||||
callerFP_ = nullptr;
|
||||
|
@ -514,7 +550,7 @@ ProfilingFrameIterator::initFromFP(const WasmActivation& activation)
|
|||
fp = CallerFPFromFP(fp);
|
||||
callerPC_ = ReturnAddressFromFP(fp);
|
||||
callerFP_ = CallerFPFromFP(fp);
|
||||
AssertMatchesCallSite(*instance_, callerPC_, callerFP_, fp);
|
||||
AssertMatchesCallSite(*activation_, callerPC_, callerFP_, fp);
|
||||
break;
|
||||
case CodeRange::ImportJitExit:
|
||||
case CodeRange::ImportInterpExit:
|
||||
|
@ -525,7 +561,7 @@ ProfilingFrameIterator::initFromFP(const WasmActivation& activation)
|
|||
|
||||
// The iterator inserts a pretend innermost frame for non-None ExitReasons.
|
||||
// This allows the variety of exit reasons to show up in the callstack.
|
||||
exitReason_ = activation.exitReason();
|
||||
exitReason_ = activation_->exitReason();
|
||||
|
||||
// In the case of calls to builtins or asynchronous interrupts, no exit path
|
||||
// is taken so the exitReason is None. Coerce these to the Native exit
|
||||
|
@ -551,7 +587,8 @@ InThunk(const CodeRange& codeRange, uint32_t offsetInModule)
|
|||
|
||||
ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
||||
const RegisterState& state)
|
||||
: instance_(&activation.instance()),
|
||||
: activation_(&activation),
|
||||
code_(nullptr),
|
||||
codeRange_(nullptr),
|
||||
callerFP_(nullptr),
|
||||
callerPC_(nullptr),
|
||||
|
@ -563,22 +600,23 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
|||
// happens if profiling is enabled while the instance is on the stack (in
|
||||
// which case profiling will be enabled when the instance becomes inactive
|
||||
// and gets called again).
|
||||
if (!instance_->profilingEnabled()) {
|
||||
if (!activation_->compartment()->wasm.profilingEnabled()) {
|
||||
MOZ_ASSERT(done());
|
||||
return;
|
||||
}
|
||||
|
||||
// If pc isn't in the instance's code, we must have exited the code via an
|
||||
// exit trampoline or signal handler.
|
||||
if (!instance_->codeSegment().containsCodePC(state.pc)) {
|
||||
initFromFP(activation);
|
||||
code_ = activation_->compartment()->wasm.lookupCode(state.pc);
|
||||
if (!code_) {
|
||||
initFromFP();
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: fp may be null while entering and leaving the activation.
|
||||
uint8_t* fp = activation.fp();
|
||||
|
||||
const CodeRange* codeRange = instance_->lookupCodeRange(state.pc);
|
||||
const CodeRange* codeRange = code_->lookupRange(state.pc);
|
||||
switch (codeRange->kind()) {
|
||||
case CodeRange::Function:
|
||||
case CodeRange::CallThunk:
|
||||
|
@ -591,8 +629,7 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
|||
// while pc is in the prologue/epilogue would skip the second-to-
|
||||
// innermost call. To avoid this problem, we use the static structure of
|
||||
// the code in the prologue and epilogue to do the Right Thing.
|
||||
MOZ_ASSERT(instance_->codeSegment().containsCodePC(state.pc));
|
||||
uint32_t offsetInModule = (uint8_t*)state.pc - instance_->codeSegment().code();
|
||||
uint32_t offsetInModule = (uint8_t*)state.pc - code_->segment().base();
|
||||
MOZ_ASSERT(offsetInModule >= codeRange->begin());
|
||||
MOZ_ASSERT(offsetInModule < codeRange->end());
|
||||
uint32_t offsetInCodeRange = offsetInModule - codeRange->begin();
|
||||
|
@ -603,13 +640,13 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
|||
// still in lr and fp still holds the caller's fp.
|
||||
callerPC_ = state.lr;
|
||||
callerFP_ = fp;
|
||||
AssertMatchesCallSite(*instance_, callerPC_, callerFP_, sp - 2);
|
||||
AssertMatchesCallSite(*activation_, callerPC_, callerFP_, sp - 2);
|
||||
} else if (offsetInModule == codeRange->profilingReturn() - PostStorePrePopFP) {
|
||||
// Second-to-last instruction of the ARM/MIPS function; fp points to
|
||||
// the caller's fp; have not yet popped AsmJSFrame.
|
||||
callerPC_ = ReturnAddressFromFP(sp);
|
||||
callerFP_ = CallerFPFromFP(sp);
|
||||
AssertMatchesCallSite(*instance_, callerPC_, callerFP_, sp);
|
||||
AssertMatchesCallSite(*activation_, callerPC_, callerFP_, sp);
|
||||
} else
|
||||
#endif
|
||||
if (offsetInCodeRange < PushedFP || offsetInModule == codeRange->profilingReturn() ||
|
||||
|
@ -619,19 +656,19 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
|||
// still points to the caller's fp.
|
||||
callerPC_ = *sp;
|
||||
callerFP_ = fp;
|
||||
AssertMatchesCallSite(*instance_, callerPC_, callerFP_, sp - 1);
|
||||
AssertMatchesCallSite(*activation_, callerPC_, callerFP_, sp - 1);
|
||||
} else if (offsetInCodeRange < StoredFP) {
|
||||
// The full AsmJSFrame has been pushed; fp still points to the
|
||||
// caller's frame.
|
||||
MOZ_ASSERT(fp == CallerFPFromFP(sp));
|
||||
callerPC_ = ReturnAddressFromFP(sp);
|
||||
callerFP_ = CallerFPFromFP(sp);
|
||||
AssertMatchesCallSite(*instance_, callerPC_, callerFP_, sp);
|
||||
AssertMatchesCallSite(*activation_, callerPC_, callerFP_, sp);
|
||||
} else {
|
||||
// Not in the prologue/epilogue.
|
||||
callerPC_ = ReturnAddressFromFP(fp);
|
||||
callerFP_ = CallerFPFromFP(fp);
|
||||
AssertMatchesCallSite(*instance_, callerPC_, callerFP_, fp);
|
||||
AssertMatchesCallSite(*activation_, callerPC_, callerFP_, fp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -658,7 +695,7 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
|
|||
// skipped frames. Thus, we use simply unwind based on fp.
|
||||
callerPC_ = ReturnAddressFromFP(fp);
|
||||
callerFP_ = CallerFPFromFP(fp);
|
||||
AssertMatchesCallSite(*instance_, callerPC_, callerFP_, fp);
|
||||
AssertMatchesCallSite(*activation_, callerPC_, callerFP_, fp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -685,11 +722,13 @@ ProfilingFrameIterator::operator++()
|
|||
return;
|
||||
}
|
||||
|
||||
const CodeRange* codeRange = instance_->lookupCodeRange(callerPC_);
|
||||
MOZ_ASSERT(codeRange);
|
||||
codeRange_ = codeRange;
|
||||
code_ = activation_->compartment()->wasm.lookupCode(callerPC_);
|
||||
MOZ_ASSERT(code_);
|
||||
|
||||
switch (codeRange->kind()) {
|
||||
codeRange_ = code_->lookupRange(callerPC_);
|
||||
MOZ_ASSERT(codeRange_);
|
||||
|
||||
switch (codeRange_->kind()) {
|
||||
case CodeRange::Entry:
|
||||
MOZ_ASSERT(callerFP_ == nullptr);
|
||||
callerPC_ = nullptr;
|
||||
|
@ -701,7 +740,7 @@ ProfilingFrameIterator::operator++()
|
|||
case CodeRange::CallThunk:
|
||||
stackAddress_ = callerFP_;
|
||||
callerPC_ = ReturnAddressFromFP(callerFP_);
|
||||
AssertMatchesCallSite(*instance_, callerPC_, CallerFPFromFP(callerFP_), callerFP_);
|
||||
AssertMatchesCallSite(*activation_, callerPC_, CallerFPFromFP(callerFP_), callerFP_);
|
||||
callerFP_ = CallerFPFromFP(callerFP_);
|
||||
break;
|
||||
}
|
||||
|
@ -735,7 +774,7 @@ ProfilingFrameIterator::label() const
|
|||
}
|
||||
|
||||
switch (codeRange_->kind()) {
|
||||
case CodeRange::Function: return instance_->profilingLabel(codeRange_->funcIndex());
|
||||
case CodeRange::Function: return code_->profilingLabel(codeRange_->funcIndex());
|
||||
case CodeRange::Entry: return "entry trampoline (in asm.js)";
|
||||
case CodeRange::ImportJitExit: return importJitDescription;
|
||||
case CodeRange::ImportInterpExit: return importInterpDescription;
|
||||
|
@ -750,12 +789,12 @@ ProfilingFrameIterator::label() const
|
|||
// Runtime patching to enable/disable profiling
|
||||
|
||||
void
|
||||
wasm::ToggleProfiling(const Instance& instance, const CallSite& callSite, bool enabled)
|
||||
wasm::ToggleProfiling(const Code& code, const CallSite& callSite, bool enabled)
|
||||
{
|
||||
if (callSite.kind() != CallSite::Relative)
|
||||
return;
|
||||
|
||||
uint8_t* callerRetAddr = instance.codeSegment().code() + callSite.returnAddressOffset();
|
||||
uint8_t* callerRetAddr = code.segment().base() + callSite.returnAddressOffset();
|
||||
|
||||
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
void* callee = X86Encoding::GetRel32Target(callerRetAddr);
|
||||
|
@ -782,12 +821,12 @@ wasm::ToggleProfiling(const Instance& instance, const CallSite& callSite, bool e
|
|||
# error "Missing architecture"
|
||||
#endif
|
||||
|
||||
const CodeRange* codeRange = instance.lookupCodeRange(callee);
|
||||
const CodeRange* codeRange = code.lookupRange(callee);
|
||||
if (!codeRange->isFunction())
|
||||
return;
|
||||
|
||||
uint8_t* from = instance.codeSegment().code() + codeRange->funcNonProfilingEntry();
|
||||
uint8_t* to = instance.codeSegment().code() + codeRange->funcProfilingEntry();
|
||||
uint8_t* from = code.segment().base() + codeRange->funcNonProfilingEntry();
|
||||
uint8_t* to = code.segment().base() + codeRange->funcProfilingEntry();
|
||||
if (!enabled)
|
||||
Swap(from, to);
|
||||
|
||||
|
@ -810,24 +849,24 @@ wasm::ToggleProfiling(const Instance& instance, const CallSite& callSite, bool e
|
|||
}
|
||||
|
||||
void
|
||||
wasm::ToggleProfiling(const Instance& instance, const CallThunk& callThunk, bool enabled)
|
||||
wasm::ToggleProfiling(const Code& code, const CallThunk& callThunk, bool enabled)
|
||||
{
|
||||
const CodeRange& cr = instance.metadata().codeRanges[callThunk.u.codeRangeIndex];
|
||||
const CodeRange& cr = code.metadata().codeRanges[callThunk.u.codeRangeIndex];
|
||||
uint32_t calleeOffset = enabled ? cr.funcProfilingEntry() : cr.funcNonProfilingEntry();
|
||||
MacroAssembler::repatchThunk(instance.codeSegment().code(), callThunk.offset, calleeOffset);
|
||||
MacroAssembler::repatchThunk(code.segment().base(), callThunk.offset, calleeOffset);
|
||||
}
|
||||
|
||||
void
|
||||
wasm::ToggleProfiling(const Instance& instance, const CodeRange& codeRange, bool enabled)
|
||||
wasm::ToggleProfiling(const Code& code, const CodeRange& codeRange, bool enabled)
|
||||
{
|
||||
if (!codeRange.isFunction())
|
||||
return;
|
||||
|
||||
uint8_t* code = instance.codeSegment().code();
|
||||
uint8_t* profilingEntry = code + codeRange.funcProfilingEntry();
|
||||
uint8_t* tableProfilingJump = code + codeRange.funcTableProfilingJump();
|
||||
uint8_t* profilingJump = code + codeRange.funcProfilingJump();
|
||||
uint8_t* profilingEpilogue = code + codeRange.funcProfilingEpilogue();
|
||||
uint8_t* codeBase = code.segment().base();
|
||||
uint8_t* profilingEntry = codeBase + codeRange.funcProfilingEntry();
|
||||
uint8_t* tableProfilingJump = codeBase + codeRange.funcTableProfilingJump();
|
||||
uint8_t* profilingJump = codeBase + codeRange.funcProfilingJump();
|
||||
uint8_t* profilingEpilogue = codeBase + codeRange.funcProfilingEpilogue();
|
||||
|
||||
if (enabled) {
|
||||
MacroAssembler::patchNopToNearJump(tableProfilingJump, profilingEntry);
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace jit { class MacroAssembler; class Label; }
|
|||
namespace wasm {
|
||||
|
||||
class CallSite;
|
||||
class Code;
|
||||
class CodeRange;
|
||||
class Instance;
|
||||
class SigIdDesc;
|
||||
|
@ -49,8 +50,8 @@ struct ProfilingOffsets;
|
|||
// function stack frame.
|
||||
class FrameIterator
|
||||
{
|
||||
JSContext* cx_;
|
||||
const Instance* instance_;
|
||||
const WasmActivation* activation_;
|
||||
const Code* code_;
|
||||
const CallSite* callsite_;
|
||||
const CodeRange* codeRange_;
|
||||
uint8_t* fp_;
|
||||
|
@ -63,6 +64,9 @@ class FrameIterator
|
|||
explicit FrameIterator(const WasmActivation& activation);
|
||||
void operator++();
|
||||
bool done() const;
|
||||
const char* filename() const;
|
||||
const char16_t* displayURL() const;
|
||||
bool mutedErrors() const;
|
||||
JSAtom* functionDisplayAtom() const;
|
||||
unsigned lineOrBytecode() const;
|
||||
};
|
||||
|
@ -82,14 +86,15 @@ enum class ExitReason : uint32_t
|
|||
// module is not in profiling mode, the activation is skipped.
|
||||
class ProfilingFrameIterator
|
||||
{
|
||||
const Instance* instance_;
|
||||
const WasmActivation* activation_;
|
||||
const Code* code_;
|
||||
const CodeRange* codeRange_;
|
||||
uint8_t* callerFP_;
|
||||
void* callerPC_;
|
||||
void* stackAddress_;
|
||||
ExitReason exitReason_;
|
||||
|
||||
void initFromFP(const WasmActivation& activation);
|
||||
void initFromFP();
|
||||
|
||||
public:
|
||||
ProfilingFrameIterator();
|
||||
|
@ -120,13 +125,13 @@ GenerateFunctionEpilogue(jit::MacroAssembler& masm, unsigned framePushed, FuncOf
|
|||
// Runtime patching to enable/disable profiling
|
||||
|
||||
void
|
||||
ToggleProfiling(const Instance& instance, const CallSite& callSite, bool enabled);
|
||||
ToggleProfiling(const Code& code, const CallSite& callSite, bool enabled);
|
||||
|
||||
void
|
||||
ToggleProfiling(const Instance& instance, const CallThunk& callThunk, bool enabled);
|
||||
ToggleProfiling(const Code& code, const CallThunk& callThunk, bool enabled);
|
||||
|
||||
void
|
||||
ToggleProfiling(const Instance& instance, const CodeRange& codeRange, bool enabled);
|
||||
ToggleProfiling(const Code& code, const CodeRange& codeRange, bool enabled);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
|
|
@ -76,17 +76,17 @@ ModuleGenerator::~ModuleGenerator()
|
|||
if (outstanding_) {
|
||||
AutoLockHelperThreadState lock;
|
||||
while (true) {
|
||||
IonCompileTaskPtrVector& worklist = HelperThreadState().wasmWorklist();
|
||||
IonCompileTaskPtrVector& worklist = HelperThreadState().wasmWorklist(lock);
|
||||
MOZ_ASSERT(outstanding_ >= worklist.length());
|
||||
outstanding_ -= worklist.length();
|
||||
worklist.clear();
|
||||
|
||||
IonCompileTaskPtrVector& finished = HelperThreadState().wasmFinishedList();
|
||||
IonCompileTaskPtrVector& finished = HelperThreadState().wasmFinishedList(lock);
|
||||
MOZ_ASSERT(outstanding_ >= finished.length());
|
||||
outstanding_ -= finished.length();
|
||||
finished.clear();
|
||||
|
||||
uint32_t numFailed = HelperThreadState().harvestFailedWasmJobs();
|
||||
uint32_t numFailed = HelperThreadState().harvestFailedWasmJobs(lock);
|
||||
MOZ_ASSERT(outstanding_ >= numFailed);
|
||||
outstanding_ -= numFailed;
|
||||
|
||||
|
@ -199,12 +199,12 @@ ModuleGenerator::finishOutstandingTask()
|
|||
while (true) {
|
||||
MOZ_ASSERT(outstanding_ > 0);
|
||||
|
||||
if (HelperThreadState().wasmFailed())
|
||||
if (HelperThreadState().wasmFailed(lock))
|
||||
return false;
|
||||
|
||||
if (!HelperThreadState().wasmFinishedList().empty()) {
|
||||
if (!HelperThreadState().wasmFinishedList(lock).empty()) {
|
||||
outstanding_--;
|
||||
task = HelperThreadState().wasmFinishedList().popCopy();
|
||||
task = HelperThreadState().wasmFinishedList(lock).popCopy();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -790,9 +790,9 @@ ModuleGenerator::startFuncDefs()
|
|||
#ifdef DEBUG
|
||||
{
|
||||
AutoLockHelperThreadState lock;
|
||||
MOZ_ASSERT(!HelperThreadState().wasmFailed());
|
||||
MOZ_ASSERT(HelperThreadState().wasmWorklist().empty());
|
||||
MOZ_ASSERT(HelperThreadState().wasmFinishedList().empty());
|
||||
MOZ_ASSERT(!HelperThreadState().wasmFailed(lock));
|
||||
MOZ_ASSERT(HelperThreadState().wasmWorklist(lock).empty());
|
||||
MOZ_ASSERT(HelperThreadState().wasmFinishedList(lock).empty());
|
||||
}
|
||||
#endif
|
||||
parallel_ = true;
|
||||
|
|
|
@ -18,18 +18,9 @@
|
|||
|
||||
#include "asmjs/WasmInstance.h"
|
||||
|
||||
#include "mozilla/BinarySearch.h"
|
||||
|
||||
#include "jsprf.h"
|
||||
|
||||
#include "asmjs/WasmBinaryToExperimentalText.h"
|
||||
#include "asmjs/WasmJS.h"
|
||||
#include "asmjs/WasmModule.h"
|
||||
#include "builtin/SIMD.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/JitCommon.h"
|
||||
#include "jit/JitCompartment.h"
|
||||
#include "vm/StringBuffer.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
|
@ -94,120 +85,50 @@ class SigIdSet
|
|||
|
||||
ExclusiveData<SigIdSet> sigIdSet;
|
||||
|
||||
JSContext**
|
||||
Instance::addressOfContextPtr() const
|
||||
{
|
||||
return (JSContext**)(codeSegment().globalData() + ContextPtrGlobalDataOffset);
|
||||
}
|
||||
|
||||
Instance**
|
||||
Instance::addressOfInstancePtr() const
|
||||
{
|
||||
return (Instance**)(codeSegment().globalData() + InstancePtrGlobalDataOffset);
|
||||
}
|
||||
|
||||
uint8_t**
|
||||
Instance::addressOfMemoryBase() const
|
||||
{
|
||||
return (uint8_t**)(codeSegment_->globalData() + HeapGlobalDataOffset);
|
||||
return (uint8_t**)(codeSegment().globalData() + HeapGlobalDataOffset);
|
||||
}
|
||||
|
||||
void**
|
||||
Instance::addressOfTableBase(size_t tableIndex) const
|
||||
{
|
||||
MOZ_ASSERT(metadata_->tables[tableIndex].globalDataOffset >= InitialGlobalDataBytes);
|
||||
return (void**)(codeSegment_->globalData() + metadata_->tables[tableIndex].globalDataOffset);
|
||||
MOZ_ASSERT(metadata().tables[tableIndex].globalDataOffset >= InitialGlobalDataBytes);
|
||||
return (void**)(codeSegment().globalData() + metadata().tables[tableIndex].globalDataOffset);
|
||||
}
|
||||
|
||||
const void**
|
||||
Instance::addressOfSigId(const SigIdDesc& sigId) const
|
||||
{
|
||||
MOZ_ASSERT(sigId.globalDataOffset() >= InitialGlobalDataBytes);
|
||||
return (const void**)(codeSegment_->globalData() + sigId.globalDataOffset());
|
||||
return (const void**)(codeSegment().globalData() + sigId.globalDataOffset());
|
||||
}
|
||||
|
||||
FuncImportExit&
|
||||
Instance::funcImportToExit(const FuncImport& fi)
|
||||
{
|
||||
MOZ_ASSERT(fi.exitGlobalDataOffset() >= InitialGlobalDataBytes);
|
||||
return *(FuncImportExit*)(codeSegment_->globalData() + fi.exitGlobalDataOffset());
|
||||
}
|
||||
|
||||
WasmActivation*&
|
||||
Instance::activation()
|
||||
{
|
||||
return *(WasmActivation**)(codeSegment_->globalData() + ActivationGlobalDataOffset);
|
||||
}
|
||||
|
||||
bool
|
||||
Instance::toggleProfiling(JSContext* cx)
|
||||
{
|
||||
profilingEnabled_ = !profilingEnabled_;
|
||||
|
||||
{
|
||||
AutoWritableJitCode awjc(cx->runtime(), codeSegment_->code(), codeSegment_->codeLength());
|
||||
AutoFlushICache afc("Instance::toggleProfiling");
|
||||
AutoFlushICache::setRange(uintptr_t(codeSegment_->code()), codeSegment_->codeLength());
|
||||
|
||||
for (const CallSite& callSite : metadata_->callSites)
|
||||
ToggleProfiling(*this, callSite, profilingEnabled_);
|
||||
for (const CallThunk& callThunk : metadata_->callThunks)
|
||||
ToggleProfiling(*this, callThunk, profilingEnabled_);
|
||||
for (const CodeRange& codeRange : metadata_->codeRanges)
|
||||
ToggleProfiling(*this, codeRange, profilingEnabled_);
|
||||
}
|
||||
|
||||
// When enabled, generate profiling labels for every name in funcNames_
|
||||
// that is the name of some Function CodeRange. This involves malloc() so
|
||||
// do it now since, once we start sampling, we'll be in a signal-handing
|
||||
// context where we cannot malloc.
|
||||
if (profilingEnabled_) {
|
||||
for (const CodeRange& codeRange : metadata_->codeRanges) {
|
||||
if (!codeRange.isFunction())
|
||||
continue;
|
||||
|
||||
TwoByteName name(cx);
|
||||
if (!getFuncName(cx, codeRange.funcIndex(), &name))
|
||||
return false;
|
||||
if (!name.append('\0'))
|
||||
return false;
|
||||
|
||||
UniqueChars label(JS_smprintf("%hs (%s:%u)",
|
||||
name.begin(),
|
||||
metadata_->filename.get(),
|
||||
codeRange.funcLineOrBytecode()));
|
||||
if (!label) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (codeRange.funcIndex() >= funcLabels_.length()) {
|
||||
if (!funcLabels_.resize(codeRange.funcIndex() + 1))
|
||||
return false;
|
||||
}
|
||||
funcLabels_[codeRange.funcIndex()] = Move(label);
|
||||
}
|
||||
} else {
|
||||
funcLabels_.clear();
|
||||
}
|
||||
|
||||
// Typed-function tables' elements point directly to either the profiling or
|
||||
// non-profiling prologue and must therefore be updated when the profiling
|
||||
// mode is toggled.
|
||||
|
||||
for (const SharedTable& table : tables_) {
|
||||
if (!table->isTypedFunction())
|
||||
continue;
|
||||
|
||||
void** array = table->array();
|
||||
uint32_t length = table->length();
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
const CodeRange* codeRange = lookupCodeRange(array[i]);
|
||||
void* from = codeSegment_->code() + codeRange->funcNonProfilingEntry();
|
||||
void* to = codeSegment_->code() + codeRange->funcProfilingEntry();
|
||||
if (!profilingEnabled_)
|
||||
Swap(from, to);
|
||||
MOZ_ASSERT(array[i] == from);
|
||||
array[i] = to;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return *(FuncImportExit*)(codeSegment().globalData() + fi.exitGlobalDataOffset());
|
||||
}
|
||||
|
||||
bool
|
||||
Instance::callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
|
||||
MutableHandleValue rval)
|
||||
{
|
||||
const FuncImport& fi = metadata_->funcImports[funcImportIndex];
|
||||
const FuncImport& fi = metadata().funcImports[funcImportIndex];
|
||||
|
||||
InvokeArgs args(cx);
|
||||
if (!args.init(argc))
|
||||
|
@ -260,7 +181,7 @@ Instance::callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, con
|
|||
return true;
|
||||
|
||||
// The exit may already have become optimized.
|
||||
void* jitExitCode = codeSegment_->code() + fi.jitExitCodeOffset();
|
||||
void* jitExitCode = codeBase() + fi.jitExitCodeOffset();
|
||||
if (exit.code == jitExitCode)
|
||||
return true;
|
||||
|
||||
|
@ -324,84 +245,75 @@ Instance::callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, con
|
|||
}
|
||||
|
||||
/* static */ int32_t
|
||||
Instance::callImport_void(int32_t funcImportIndex, int32_t argc, uint64_t* argv)
|
||||
Instance::callImport_void(Instance* instance, int32_t funcImportIndex, int32_t argc, uint64_t* argv)
|
||||
{
|
||||
WasmActivation* activation = JSRuntime::innermostWasmActivation();
|
||||
JSContext* cx = activation->cx();
|
||||
|
||||
JSContext* cx = instance->cx();
|
||||
RootedValue rval(cx);
|
||||
return activation->instance().callImport(cx, funcImportIndex, argc, argv, &rval);
|
||||
return instance->callImport(cx, funcImportIndex, argc, argv, &rval);
|
||||
}
|
||||
|
||||
/* static */ int32_t
|
||||
Instance::callImport_i32(int32_t funcImportIndex, int32_t argc, uint64_t* argv)
|
||||
Instance::callImport_i32(Instance* instance, int32_t funcImportIndex, int32_t argc, uint64_t* argv)
|
||||
{
|
||||
WasmActivation* activation = JSRuntime::innermostWasmActivation();
|
||||
JSContext* cx = activation->cx();
|
||||
|
||||
JSContext* cx = instance->cx();
|
||||
RootedValue rval(cx);
|
||||
if (!activation->instance().callImport(cx, funcImportIndex, argc, argv, &rval))
|
||||
if (!instance->callImport(cx, funcImportIndex, argc, argv, &rval))
|
||||
return false;
|
||||
|
||||
return ToInt32(cx, rval, (int32_t*)argv);
|
||||
}
|
||||
|
||||
/* static */ int32_t
|
||||
Instance::callImport_i64(int32_t funcImportIndex, int32_t argc, uint64_t* argv)
|
||||
Instance::callImport_i64(Instance* instance, int32_t funcImportIndex, int32_t argc, uint64_t* argv)
|
||||
{
|
||||
WasmActivation* activation = JSRuntime::innermostWasmActivation();
|
||||
JSContext* cx = activation->cx();
|
||||
|
||||
JSContext* cx = instance->cx();
|
||||
RootedValue rval(cx);
|
||||
if (!activation->instance().callImport(cx, funcImportIndex, argc, argv, &rval))
|
||||
if (!instance->callImport(cx, funcImportIndex, argc, argv, &rval))
|
||||
return false;
|
||||
|
||||
return ReadI64Object(cx, rval, (int64_t*)argv);
|
||||
}
|
||||
|
||||
/* static */ int32_t
|
||||
Instance::callImport_f64(int32_t funcImportIndex, int32_t argc, uint64_t* argv)
|
||||
Instance::callImport_f64(Instance* instance, int32_t funcImportIndex, int32_t argc, uint64_t* argv)
|
||||
{
|
||||
WasmActivation* activation = JSRuntime::innermostWasmActivation();
|
||||
JSContext* cx = activation->cx();
|
||||
|
||||
JSContext* cx = instance->cx();
|
||||
RootedValue rval(cx);
|
||||
if (!activation->instance().callImport(cx, funcImportIndex, argc, argv, &rval))
|
||||
if (!instance->callImport(cx, funcImportIndex, argc, argv, &rval))
|
||||
return false;
|
||||
|
||||
return ToNumber(cx, rval, (double*)argv);
|
||||
}
|
||||
|
||||
Instance::Instance(JSContext* cx,
|
||||
UniqueCodeSegment codeSegment,
|
||||
const Metadata& metadata,
|
||||
const ShareableBytes* maybeBytecode,
|
||||
UniqueCode code,
|
||||
HandleWasmMemoryObject memory,
|
||||
SharedTableVector&& tables,
|
||||
Handle<FunctionVector> funcImports,
|
||||
const ValVector& globalImports)
|
||||
: codeSegment_(Move(codeSegment)),
|
||||
metadata_(&metadata),
|
||||
maybeBytecode_(maybeBytecode),
|
||||
: compartment_(cx->compartment()),
|
||||
code_(Move(code)),
|
||||
memory_(memory),
|
||||
tables_(Move(tables)),
|
||||
profilingEnabled_(false)
|
||||
tables_(Move(tables))
|
||||
{
|
||||
MOZ_ASSERT(funcImports.length() == metadata.funcImports.length());
|
||||
MOZ_ASSERT(tables_.length() == metadata.tables.length());
|
||||
MOZ_ASSERT(funcImports.length() == metadata().funcImports.length());
|
||||
MOZ_ASSERT(tables_.length() == metadata().tables.length());
|
||||
|
||||
for (size_t i = 0; i < metadata.funcImports.length(); i++) {
|
||||
const FuncImport& fi = metadata.funcImports[i];
|
||||
*addressOfContextPtr() = cx;
|
||||
*addressOfInstancePtr() = this;
|
||||
|
||||
for (size_t i = 0; i < metadata().funcImports.length(); i++) {
|
||||
const FuncImport& fi = metadata().funcImports[i];
|
||||
FuncImportExit& exit = funcImportToExit(fi);
|
||||
exit.code = codeSegment_->code() + fi.interpExitCodeOffset();
|
||||
exit.code = codeBase() + fi.interpExitCodeOffset();
|
||||
exit.fun = funcImports[i];
|
||||
exit.baselineScript = nullptr;
|
||||
}
|
||||
|
||||
uint8_t* globalData = codeSegment_->globalData();
|
||||
uint8_t* globalData = code_->segment().globalData();
|
||||
|
||||
for (size_t i = 0; i < metadata.globals.length(); i++) {
|
||||
const GlobalDesc& global = metadata.globals[i];
|
||||
for (size_t i = 0; i < metadata().globals.length(); i++) {
|
||||
const GlobalDesc& global = metadata().globals[i];
|
||||
if (global.isConstant())
|
||||
continue;
|
||||
|
||||
|
@ -419,7 +331,7 @@ Instance::Instance(JSContext* cx,
|
|||
break;
|
||||
}
|
||||
case InitExpr::Kind::GetGlobal: {
|
||||
const GlobalDesc& imported = metadata.globals[init.globalIndex()];
|
||||
const GlobalDesc& imported = metadata().globals[init.globalIndex()];
|
||||
globalImports[imported.importIndex()].writePayload(globalAddr);
|
||||
break;
|
||||
}
|
||||
|
@ -444,13 +356,13 @@ Instance::Instance(JSContext* cx,
|
|||
bool
|
||||
Instance::init(JSContext* cx)
|
||||
{
|
||||
if (!metadata_->sigIds.empty()) {
|
||||
if (!metadata().sigIds.empty()) {
|
||||
ExclusiveData<SigIdSet>::Guard lockedSigIdSet = sigIdSet.lock();
|
||||
|
||||
if (!lockedSigIdSet->ensureInitialized(cx))
|
||||
return false;
|
||||
|
||||
for (const SigWithId& sig : metadata_->sigIds) {
|
||||
for (const SigWithId& sig : metadata().sigIds) {
|
||||
const void* sigId;
|
||||
if (!lockedSigIdSet->allocateSigId(cx, sig, &sigId))
|
||||
return false;
|
||||
|
@ -464,16 +376,18 @@ Instance::init(JSContext* cx)
|
|||
|
||||
Instance::~Instance()
|
||||
{
|
||||
for (unsigned i = 0; i < metadata_->funcImports.length(); i++) {
|
||||
FuncImportExit& exit = funcImportToExit(metadata_->funcImports[i]);
|
||||
compartment_->wasm.unregisterInstance(*this);
|
||||
|
||||
for (unsigned i = 0; i < metadata().funcImports.length(); i++) {
|
||||
FuncImportExit& exit = funcImportToExit(metadata().funcImports[i]);
|
||||
if (exit.baselineScript)
|
||||
exit.baselineScript->removeDependentWasmImport(*this, i);
|
||||
}
|
||||
|
||||
if (!metadata_->sigIds.empty()) {
|
||||
if (!metadata().sigIds.empty()) {
|
||||
ExclusiveData<SigIdSet>::Guard lockedSigIdSet = sigIdSet.lock();
|
||||
|
||||
for (const SigWithId& sig : metadata_->sigIds) {
|
||||
for (const SigWithId& sig : metadata().sigIds) {
|
||||
if (const void* sigId = *addressOfSigId(sig.id))
|
||||
lockedSigIdSet->deallocateSigId(sig, sigId);
|
||||
}
|
||||
|
@ -483,7 +397,7 @@ Instance::~Instance()
|
|||
void
|
||||
Instance::trace(JSTracer* trc)
|
||||
{
|
||||
for (const FuncImport& fi : metadata_->funcImports)
|
||||
for (const FuncImport& fi : metadata().funcImports)
|
||||
TraceNullableEdge(trc, &funcImportToExit(fi).fun, "wasm function import");
|
||||
TraceNullableEdge(trc, &memory_, "wasm buffer");
|
||||
}
|
||||
|
@ -491,7 +405,7 @@ Instance::trace(JSTracer* trc)
|
|||
SharedMem<uint8_t*>
|
||||
Instance::memoryBase() const
|
||||
{
|
||||
MOZ_ASSERT(metadata_->usesMemory());
|
||||
MOZ_ASSERT(metadata().usesMemory());
|
||||
MOZ_ASSERT(*addressOfMemoryBase() == memory_->buffer().dataPointerEither());
|
||||
return memory_->buffer().dataPointerEither();
|
||||
}
|
||||
|
@ -512,16 +426,10 @@ Instance::updateStackLimit(JSContext* cx)
|
|||
bool
|
||||
Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args)
|
||||
{
|
||||
const FuncExport& func = metadata_->lookupFuncExport(funcIndex);
|
||||
if (!cx->compartment()->wasm.ensureProfilingState(cx))
|
||||
return false;
|
||||
|
||||
// Enable/disable profiling in the Module to match the current global
|
||||
// profiling state. Don't do this if the Module is already active on the
|
||||
// stack since this would leave the Module in a state where profiling is
|
||||
// enabled but the stack isn't unwindable.
|
||||
if (profilingEnabled() != cx->runtime()->spsProfiler.enabled() && !activation()) {
|
||||
if (!toggleProfiling(cx))
|
||||
return false;
|
||||
}
|
||||
const FuncExport& func = metadata().lookupFuncExport(funcIndex);
|
||||
|
||||
// The calling convention for an external call into wasm is to pass an
|
||||
// array of 16-byte values where each value contains either a coerced int32
|
||||
|
@ -619,12 +527,12 @@ Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args)
|
|||
// the optimized wasm-to-Ion FFI call path (which we want to be very
|
||||
// fast) can avoid doing so. The JitActivation is marked as inactive so
|
||||
// stack iteration will skip over it.
|
||||
WasmActivation activation(cx, *this);
|
||||
WasmActivation activation(cx);
|
||||
JitActivation jitActivation(cx, /* active */ false);
|
||||
|
||||
// Call the per-exported-function trampoline created by GenerateEntry.
|
||||
auto funcPtr = JS_DATA_TO_FUNC_PTR(ExportFuncPtr, codeSegment_->code() + func.entryOffset());
|
||||
if (!CALL_GENERATED_3(funcPtr, exportArgs.begin(), codeSegment_->globalData(), tlsData()))
|
||||
auto funcPtr = JS_DATA_TO_FUNC_PTR(ExportFuncPtr, codeBase() + func.entryOffset());
|
||||
if (!CALL_GENERATED_3(funcPtr, exportArgs.begin(), codeSegment().globalData(), tlsData()))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -705,189 +613,53 @@ Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args)
|
|||
return true;
|
||||
}
|
||||
|
||||
const char experimentalWarning[] =
|
||||
"Temporary\n"
|
||||
".--. .--. ____ .-'''-. ,---. ,---.\n"
|
||||
"| |_ | | .' __ `. / _ \\| \\ / |\n"
|
||||
"| _( )_ | |/ ' \\ \\ (`' )/`--'| , \\/ , |\n"
|
||||
"|(_ o _) | ||___| / |(_ o _). | |\\_ /| |\n"
|
||||
"| (_,_) \\ | | _.-` | (_,_). '. | _( )_/ | |\n"
|
||||
"| |/ \\| |.' _ |.---. \\ :| (_ o _) | |\n"
|
||||
"| ' /\\ ` || _( )_ |\\ `-' || (_,_) | |\n"
|
||||
"| / \\ |\\ (_ o _) / \\ / | | | |\n"
|
||||
"`---' `---` '.(_,_).' `-...-' '--' '--'\n"
|
||||
"text support (Work In Progress):\n\n";
|
||||
|
||||
const size_t experimentalWarningLinesCount = 12;
|
||||
|
||||
const char enabledMessage[] =
|
||||
"Restart with developer tools open to view WebAssembly source";
|
||||
|
||||
JSString*
|
||||
Instance::createText(JSContext* cx)
|
||||
{
|
||||
StringBuffer buffer(cx);
|
||||
if (maybeBytecode_) {
|
||||
const Bytes& bytes = maybeBytecode_->bytes;
|
||||
if (!buffer.append(experimentalWarning))
|
||||
return nullptr;
|
||||
maybeSourceMap_.reset(cx->runtime()->new_<GeneratedSourceMap>(cx));
|
||||
if (!maybeSourceMap_)
|
||||
return nullptr;
|
||||
if (!BinaryToExperimentalText(cx, bytes.begin(), bytes.length(), buffer,
|
||||
ExperimentalTextFormatting(), maybeSourceMap_.get()))
|
||||
return nullptr;
|
||||
#if DEBUG
|
||||
// Checking source map invariant: expression and function locations must be sorted
|
||||
// by line number.
|
||||
uint32_t lastLineno = 0;
|
||||
for (size_t i = 0; i < maybeSourceMap_->exprlocs().length(); i++) {
|
||||
MOZ_ASSERT(lastLineno <= maybeSourceMap_->exprlocs()[i].lineno);
|
||||
lastLineno = maybeSourceMap_->exprlocs()[i].lineno;
|
||||
}
|
||||
lastLineno = 0;
|
||||
for (size_t i = 0; i < maybeSourceMap_->functionlocs().length(); i++) {
|
||||
MOZ_ASSERT(lastLineno <= maybeSourceMap_->functionlocs()[i].startLineno &&
|
||||
maybeSourceMap_->functionlocs()[i].startLineno <=
|
||||
maybeSourceMap_->functionlocs()[i].endLineno);
|
||||
lastLineno = maybeSourceMap_->functionlocs()[i].endLineno + 1;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if (!buffer.append(enabledMessage))
|
||||
return nullptr;
|
||||
}
|
||||
return buffer.finishString();
|
||||
}
|
||||
|
||||
struct GeneratedSourceMapLinenoComparator
|
||||
{
|
||||
int operator()(const ExprLoc& loc) const {
|
||||
return lineno == loc.lineno ? 0 : lineno < loc.lineno ? -1 : 1;
|
||||
}
|
||||
explicit GeneratedSourceMapLinenoComparator(uint32_t lineno_) : lineno(lineno_) {}
|
||||
const uint32_t lineno;
|
||||
};
|
||||
|
||||
bool
|
||||
Instance::getLineOffsets(size_t lineno, Vector<uint32_t>& offsets)
|
||||
{
|
||||
// TODO Ensure text was generated?
|
||||
if (!maybeSourceMap_)
|
||||
return false;
|
||||
|
||||
if (lineno < experimentalWarningLinesCount)
|
||||
return true;
|
||||
lineno -= experimentalWarningLinesCount;
|
||||
|
||||
ExprLocVector& exprlocs = maybeSourceMap_->exprlocs();
|
||||
|
||||
// Binary search for the expression with the specified line number and
|
||||
// rewind to the first expression, if more than one expression on the same line.
|
||||
size_t match;
|
||||
if (!BinarySearchIf(exprlocs, 0, exprlocs.length(),
|
||||
GeneratedSourceMapLinenoComparator(lineno), &match))
|
||||
return true;
|
||||
while (match > 0 && exprlocs[match - 1].lineno == lineno)
|
||||
match--;
|
||||
// Returning all expression offsets that were printed on the specified line.
|
||||
for (size_t i = match; i < exprlocs.length() && exprlocs[i].lineno == lineno; i++) {
|
||||
if (!offsets.append(exprlocs[i].offset))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Instance::getFuncName(JSContext* cx, uint32_t funcIndex, TwoByteName* name) const
|
||||
{
|
||||
const Bytes* maybeBytecode = maybeBytecode_ ? &maybeBytecode_.get()->bytes : nullptr;
|
||||
return metadata_->getFuncName(cx, maybeBytecode, funcIndex, name);
|
||||
}
|
||||
|
||||
JSAtom*
|
||||
Instance::getFuncAtom(JSContext* cx, uint32_t funcIndex) const
|
||||
{
|
||||
TwoByteName name(cx);
|
||||
if (!getFuncName(cx, funcIndex, &name))
|
||||
return nullptr;
|
||||
|
||||
return AtomizeChars(cx, name.begin(), name.length());
|
||||
}
|
||||
|
||||
void
|
||||
Instance::deoptimizeImportExit(uint32_t funcImportIndex)
|
||||
{
|
||||
const FuncImport& fi = metadata_->funcImports[funcImportIndex];
|
||||
const FuncImport& fi = metadata().funcImports[funcImportIndex];
|
||||
FuncImportExit& exit = funcImportToExit(fi);
|
||||
exit.code = codeSegment_->code() + fi.interpExitCodeOffset();
|
||||
exit.code = codeBase() + fi.interpExitCodeOffset();
|
||||
exit.baselineScript = nullptr;
|
||||
}
|
||||
|
||||
struct CallSiteRetAddrOffset
|
||||
bool
|
||||
Instance::ensureProfilingState(JSContext* cx, bool newProfilingEnabled)
|
||||
{
|
||||
const CallSiteVector& callSites;
|
||||
explicit CallSiteRetAddrOffset(const CallSiteVector& callSites) : callSites(callSites) {}
|
||||
uint32_t operator[](size_t index) const {
|
||||
return callSites[index].returnAddressOffset();
|
||||
if (code_->profilingEnabled() == newProfilingEnabled)
|
||||
return true;
|
||||
|
||||
if (!code_->ensureProfilingState(cx, newProfilingEnabled))
|
||||
return false;
|
||||
|
||||
// Typed-function tables' elements point directly to either the profiling or
|
||||
// non-profiling prologue and must therefore be updated when the profiling
|
||||
// mode is toggled.
|
||||
|
||||
for (const SharedTable& table : tables_) {
|
||||
if (!table->isTypedFunction() || !table->initialized())
|
||||
continue;
|
||||
|
||||
// This logic will have to be somewhat generalized if wasm can create
|
||||
// typed function tables since a single table can contain elements from
|
||||
// multiple instances.
|
||||
MOZ_ASSERT(metadata().kind == ModuleKind::AsmJS);
|
||||
|
||||
void** array = table->array();
|
||||
uint32_t length = table->length();
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
const CodeRange* codeRange = code_->lookupRange(array[i]);
|
||||
void* from = codeBase() + codeRange->funcNonProfilingEntry();
|
||||
void* to = codeBase() + codeRange->funcProfilingEntry();
|
||||
if (!newProfilingEnabled)
|
||||
Swap(from, to);
|
||||
MOZ_ASSERT(array[i] == from);
|
||||
array[i] = to;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const CallSite*
|
||||
Instance::lookupCallSite(void* returnAddress) const
|
||||
{
|
||||
uint32_t target = ((uint8_t*)returnAddress) - codeSegment_->code();
|
||||
size_t lowerBound = 0;
|
||||
size_t upperBound = metadata_->callSites.length();
|
||||
|
||||
size_t match;
|
||||
if (!BinarySearch(CallSiteRetAddrOffset(metadata_->callSites), lowerBound, upperBound, target, &match))
|
||||
return nullptr;
|
||||
|
||||
return &metadata_->callSites[match];
|
||||
return true;
|
||||
}
|
||||
|
||||
const CodeRange*
|
||||
Instance::lookupCodeRange(void* pc) const
|
||||
{
|
||||
CodeRange::PC target((uint8_t*)pc - codeSegment_->code());
|
||||
size_t lowerBound = 0;
|
||||
size_t upperBound = metadata_->codeRanges.length();
|
||||
|
||||
size_t match;
|
||||
if (!BinarySearch(metadata_->codeRanges, lowerBound, upperBound, target, &match))
|
||||
return nullptr;
|
||||
|
||||
return &metadata_->codeRanges[match];
|
||||
}
|
||||
|
||||
#ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS
|
||||
struct MemoryAccessOffset
|
||||
{
|
||||
const MemoryAccessVector& accesses;
|
||||
explicit MemoryAccessOffset(const MemoryAccessVector& accesses) : accesses(accesses) {}
|
||||
uintptr_t operator[](size_t index) const {
|
||||
return accesses[index].insnOffset();
|
||||
}
|
||||
};
|
||||
|
||||
const MemoryAccess*
|
||||
Instance::lookupMemoryAccess(void* pc) const
|
||||
{
|
||||
MOZ_ASSERT(codeSegment_->containsFunctionPC(pc));
|
||||
|
||||
uint32_t target = ((uint8_t*)pc) - codeSegment_->code();
|
||||
size_t lowerBound = 0;
|
||||
size_t upperBound = metadata_->memoryAccesses.length();
|
||||
|
||||
size_t match;
|
||||
if (!BinarySearch(MemoryAccessOffset(metadata_->memoryAccesses), lowerBound, upperBound, target, &match))
|
||||
return nullptr;
|
||||
|
||||
return &metadata_->memoryAccesses[match];
|
||||
}
|
||||
#endif // ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
|
||||
|
||||
void
|
||||
Instance::addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
||||
Metadata::SeenSet* seenMetadata,
|
||||
|
@ -896,14 +668,10 @@ Instance::addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
|||
size_t* code,
|
||||
size_t* data) const
|
||||
{
|
||||
*code += codeSegment_->codeLength();
|
||||
*data += mallocSizeOf(this) +
|
||||
codeSegment_->globalDataLength() +
|
||||
metadata_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenMetadata);
|
||||
*data += mallocSizeOf(this);
|
||||
|
||||
code_->addSizeOfMisc(mallocSizeOf, seenMetadata, seenBytes, code, data);
|
||||
|
||||
for (const SharedTable& table : tables_)
|
||||
*data += table->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenTables);
|
||||
|
||||
if (maybeBytecode_)
|
||||
*data += maybeBytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
|
||||
}
|
||||
|
|
|
@ -30,8 +30,6 @@ class WasmInstanceObject;
|
|||
|
||||
namespace wasm {
|
||||
|
||||
class GeneratedSourceMap;
|
||||
|
||||
// Instance represents a wasm instance and provides all the support for runtime
|
||||
// execution of code in the instance. Instances share various immutable data
|
||||
// structures with the Module from which they were instantiated and other
|
||||
|
@ -41,52 +39,40 @@ class GeneratedSourceMap;
|
|||
|
||||
class Instance
|
||||
{
|
||||
const UniqueCodeSegment codeSegment_;
|
||||
const SharedMetadata metadata_;
|
||||
const SharedBytes maybeBytecode_;
|
||||
JSCompartment* const compartment_;
|
||||
const UniqueCode code_;
|
||||
GCPtrWasmMemoryObject memory_;
|
||||
SharedTableVector tables_;
|
||||
|
||||
bool profilingEnabled_;
|
||||
CacheableCharsVector funcLabels_;
|
||||
|
||||
|
||||
UniquePtr<GeneratedSourceMap> maybeSourceMap_;
|
||||
|
||||
// Thread-local data for code running in this instance.
|
||||
// When threading is supported, we need a TlsData object per thread per
|
||||
// instance.
|
||||
TlsData tlsData_;
|
||||
|
||||
// Internal helpers:
|
||||
JSContext** addressOfContextPtr() const;
|
||||
Instance** addressOfInstancePtr() const;
|
||||
uint8_t** addressOfMemoryBase() const;
|
||||
void** addressOfTableBase(size_t tableIndex) const;
|
||||
const void** addressOfSigId(const SigIdDesc& sigId) const;
|
||||
FuncImportExit& funcImportToExit(const FuncImport& fi);
|
||||
MOZ_MUST_USE bool toggleProfiling(JSContext* cx);
|
||||
|
||||
// Get this instance's TLS data pointer for the current thread.
|
||||
TlsData* tlsData() { return &tlsData_; }
|
||||
|
||||
// An instance keeps track of its innermost WasmActivation. A WasmActivation
|
||||
// is pushed for the duration of each call of an export.
|
||||
friend class js::WasmActivation;
|
||||
WasmActivation*& activation();
|
||||
|
||||
// Import call slow paths which are called directly from wasm code.
|
||||
friend void* AddressOf(SymbolicAddress, ExclusiveContext*);
|
||||
static int32_t callImport_void(Instance*, int32_t, int32_t, uint64_t*);
|
||||
static int32_t callImport_i32(Instance*, int32_t, int32_t, uint64_t*);
|
||||
static int32_t callImport_i64(Instance*, int32_t, int32_t, uint64_t*);
|
||||
static int32_t callImport_f64(Instance*, int32_t, int32_t, uint64_t*);
|
||||
|
||||
bool callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
|
||||
MutableHandleValue rval);
|
||||
static int32_t callImport_void(int32_t importIndex, int32_t argc, uint64_t* argv);
|
||||
static int32_t callImport_i32(int32_t importIndex, int32_t argc, uint64_t* argv);
|
||||
static int32_t callImport_i64(int32_t importIndex, int32_t argc, uint64_t* argv);
|
||||
static int32_t callImport_f64(int32_t importIndex, int32_t argc, uint64_t* argv);
|
||||
|
||||
public:
|
||||
Instance(JSContext* cx,
|
||||
UniqueCodeSegment codeSegment,
|
||||
const Metadata& metadata,
|
||||
const ShareableBytes* maybeBytecode,
|
||||
UniqueCode code,
|
||||
HandleWasmMemoryObject memory,
|
||||
SharedTableVector&& tables,
|
||||
Handle<FunctionVector> funcImports,
|
||||
|
@ -95,8 +81,13 @@ class Instance
|
|||
bool init(JSContext* cx);
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
const CodeSegment& codeSegment() const { return *codeSegment_; }
|
||||
const Metadata& metadata() const { return *metadata_; }
|
||||
JSContext* cx() const { return *addressOfContextPtr(); }
|
||||
JSCompartment* compartment() const { return compartment_; }
|
||||
Code& code() { return *code_; }
|
||||
const Code& code() const { return *code_; }
|
||||
const CodeSegment& codeSegment() const { return code_->segment(); }
|
||||
uint8_t* codeBase() const { return code_->segment().base(); }
|
||||
const Metadata& metadata() const { return code_->metadata(); }
|
||||
const SharedTableVector& tables() const { return tables_; }
|
||||
SharedMem<uint8_t*> memoryBase() const;
|
||||
size_t memoryLength() const;
|
||||
|
@ -106,31 +97,6 @@ class Instance
|
|||
|
||||
MOZ_MUST_USE bool callExport(JSContext* cx, uint32_t funcIndex, CallArgs args);
|
||||
|
||||
// An instance has a profiling mode that is updated to match the runtime's
|
||||
// profiling mode when calling an instance's exports when there are no other
|
||||
// activations of the instance live on the stack. Once in profiling mode,
|
||||
// ProfilingFrameIterator can be used to asynchronously walk the stack.
|
||||
// Otherwise, the ProfilingFrameIterator will skip any activations of this
|
||||
// instance.
|
||||
|
||||
bool profilingEnabled() const { return profilingEnabled_; }
|
||||
const char* profilingLabel(uint32_t funcIndex) const { return funcLabels_[funcIndex].get(); }
|
||||
|
||||
// If the source binary was saved (by passing the bytecode to the
|
||||
// constructor), this method will render the binary as text. Otherwise, a
|
||||
// diagnostic string will be returned.
|
||||
|
||||
// Text format support functions:
|
||||
|
||||
JSString* createText(JSContext* cx);
|
||||
bool getLineOffsets(size_t lineno, Vector<uint32_t>& offsets);
|
||||
|
||||
// Return the name associated with a given function index, or generate one
|
||||
// if none was given by the module.
|
||||
|
||||
bool getFuncName(JSContext* cx, uint32_t funcIndex, TwoByteName* name) const;
|
||||
JSAtom* getFuncAtom(JSContext* cx, uint32_t funcIndex) const;
|
||||
|
||||
// Initially, calls to imports in wasm code call out through the generic
|
||||
// callImport method. If the imported callee gets JIT compiled and the types
|
||||
// match up, callImport will patch the code to instead call through a thunk
|
||||
|
@ -139,13 +105,9 @@ class Instance
|
|||
|
||||
void deoptimizeImportExit(uint32_t funcImportIndex);
|
||||
|
||||
// Stack frame iterator support:
|
||||
// See Code::ensureProfilingState comment.
|
||||
|
||||
const CallSite* lookupCallSite(void* returnAddress) const;
|
||||
const CodeRange* lookupCodeRange(void* pc) const;
|
||||
#ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS
|
||||
const MemoryAccess* lookupMemoryAccess(void* pc) const;
|
||||
#endif
|
||||
MOZ_MUST_USE bool ensureProfilingState(JSContext* cx, bool enabled);
|
||||
|
||||
// Update the instance's copy of the stack limit.
|
||||
void updateStackLimit(JSContext*);
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче