Merge m-c to fx-team. a=merge

This commit is contained in:
Ryan VanderMeulen 2014-07-03 23:27:34 -04:00
commit 0cd1d876ea
94 changed files with 1234 additions and 716 deletions

View File

@ -22,6 +22,5 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 800200: Removing the old JavaScript debugging API, js/jsd. I'm advised
that our build system doesn't cope well with deletions, and that a spoonful
of clobber helps the medicine go down (in a most delightful way).
Bustage pile-up on inbound that appears to need clobbering to go away for good.

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7a32f4ce7f922ed174946cfe322f3d6df40f18ea"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b597b86274ab109d7ef530bc0c4b6adccddae4e4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="7a32f4ce7f922ed174946cfe322f3d6df40f18ea"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b597b86274ab109d7ef530bc0c4b6adccddae4e4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="7a32f4ce7f922ed174946cfe322f3d6df40f18ea"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b597b86274ab109d7ef530bc0c4b6adccddae4e4"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7a32f4ce7f922ed174946cfe322f3d6df40f18ea"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b597b86274ab109d7ef530bc0c4b6adccddae4e4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="7a32f4ce7f922ed174946cfe322f3d6df40f18ea"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b597b86274ab109d7ef530bc0c4b6adccddae4e4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "dd04ca4b91cdb034a4b90a924b14cd066677b05b",
"revision": "92bddc1a99aa2d80d58fc28749fecedbf5c692f1",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7a32f4ce7f922ed174946cfe322f3d6df40f18ea"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b597b86274ab109d7ef530bc0c4b6adccddae4e4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7a32f4ce7f922ed174946cfe322f3d6df40f18ea"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b597b86274ab109d7ef530bc0c4b6adccddae4e4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="7a32f4ce7f922ed174946cfe322f3d6df40f18ea"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="b597b86274ab109d7ef530bc0c4b6adccddae4e4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7a32f4ce7f922ed174946cfe322f3d6df40f18ea"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b597b86274ab109d7ef530bc0c4b6adccddae4e4"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="230f11aff069d90d20fc2dc63b48e9ae3d4bdcd1"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -165,7 +165,7 @@ OBJDIR_TARGETS = install export libs clean realclean distclean maybe_clobber_pro
# The default rule is build
build::
$(MAKE) -f $(TOPSRCDIR)/client.mk $(if $(MOZ_PGO),profiledbuild,realbuild)
$(MAKE) -f $(TOPSRCDIR)/client.mk $(if $(MOZ_PGO),profiledbuild,realbuild) CREATE_MOZCONFIG_JSON=
# Define mkdir
include $(TOPSRCDIR)/config/makefiles/makeutils.mk
@ -218,12 +218,12 @@ everything: clean build
# is usable in multi-pass builds, where you might not have a runnable
# application until all the build passes and postflight scripts have run.
profiledbuild::
$(MAKE) -f $(TOPSRCDIR)/client.mk realbuild MOZ_PROFILE_GENERATE=1 MOZ_PGO_INSTRUMENTED=1
$(MAKE) -f $(TOPSRCDIR)/client.mk realbuild MOZ_PROFILE_GENERATE=1 MOZ_PGO_INSTRUMENTED=1 CREATE_MOZCONFIG_JSON=
$(MAKE) -C $(OBJDIR) package MOZ_PGO_INSTRUMENTED=1 MOZ_INTERNAL_SIGNING_FORMAT= MOZ_EXTERNAL_SIGNING_FORMAT=
rm -f $(OBJDIR)/jarlog/en-US.log
MOZ_PGO_INSTRUMENTED=1 JARLOG_FILE=jarlog/en-US.log EXTRA_TEST_ARGS=10 $(MAKE) -C $(OBJDIR) pgo-profile-run
$(MAKE) -f $(TOPSRCDIR)/client.mk maybe_clobber_profiledbuild
$(MAKE) -f $(TOPSRCDIR)/client.mk realbuild MOZ_PROFILE_USE=1
$(MAKE) -f $(TOPSRCDIR)/client.mk maybe_clobber_profiledbuild CREATE_MOZCONFIG_JSON=
$(MAKE) -f $(TOPSRCDIR)/client.mk realbuild MOZ_PROFILE_USE=1 CREATE_MOZCONFIG_JSON=
#####################################################
# Build date unification
@ -333,9 +333,14 @@ configure-preqs = \
$(OBJDIR)/.mozconfig.json \
$(NULL)
CREATE_MOZCONFIG_JSON := $(shell $(TOPSRCDIR)/mach environment --format=json -o $(OBJDIR)/.mozconfig.json)
$(OBJDIR)/.mozconfig.json: $(call mkdir_deps,$(OBJDIR))
@$(TOPSRCDIR)/mach environment --format=json -o $@
CREATE_MOZCONFIG_JSON = $(shell $(TOPSRCDIR)/mach environment --format=json -o $(OBJDIR)/.mozconfig.json)
# Force CREATE_MOZCONFIG_JSON above to be resolved, without side effects in
# case the result is non empty, and allowing an override on the make command
# line not running the command (using := $(shell) still runs the shell command).
ifneq (,$(CREATE_MOZCONFIG_JSON))
endif
$(OBJDIR)/.mozconfig.json: $(call mkdir_deps,$(OBJDIR)) ;
save-mozconfig: $(FOUND_MOZCONFIG)
-cp $(FOUND_MOZCONFIG) $(OBJDIR)/.mozconfig

View File

@ -895,7 +895,7 @@ nsScriptLoader::AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest)
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
JS::CompileOptions options(cx);
FillCompileOptionsForRequest(aRequest, global, &options);
FillCompileOptionsForRequest(jsapi, aRequest, global, &options);
if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptTextLength)) {
return NS_ERROR_FAILURE;
@ -1066,7 +1066,8 @@ nsScriptLoader::GetScriptGlobalObject()
}
void
nsScriptLoader::FillCompileOptionsForRequest(nsScriptLoadRequest *aRequest,
nsScriptLoader::FillCompileOptionsForRequest(const AutoJSAPI &jsapi,
nsScriptLoadRequest *aRequest,
JS::Handle<JSObject *> aScopeChain,
JS::CompileOptions *aOptions)
{
@ -1085,15 +1086,9 @@ nsScriptLoader::FillCompileOptionsForRequest(nsScriptLoadRequest *aRequest,
aOptions->setOriginPrincipals(nsJSPrincipals::get(aRequest->mOriginPrincipal));
}
AutoJSContext cx;
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> elementVal(cx);
MOZ_ASSERT(aRequest->mElement);
// XXXbz this is super-fragile, because the code that _uses_ the
// JS::CompileOptions is nowhere near us, but we have to coordinate
// compartments with it... and in particular, it will compile in the
// compartment of aScopeChain, so we want to wrap into that compartment as
// well.
JSAutoCompartment ac(cx, aScopeChain);
if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, aRequest->mElement,
&elementVal,
/* aAllowWrapping = */ true))) {
@ -1165,7 +1160,7 @@ nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest,
}
JS::CompileOptions options(entryScript.cx());
FillCompileOptionsForRequest(aRequest, global, &options);
FillCompileOptionsForRequest(entryScript, aRequest, global, &options);
rv = nsJSUtils::EvaluateString(entryScript.cx(), aSrcBuf, global, options,
aOffThreadToken);
}

View File

@ -25,6 +25,12 @@ namespace JS {
class SourceBufferHolder;
}
namespace mozilla {
namespace dom {
class AutoJSAPI;
}
}
//////////////////////////////////////////////////////////////
// Script loader implementation
//////////////////////////////////////////////////////////////
@ -312,7 +318,8 @@ private:
void **aOffThreadToken);
already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalObject();
void FillCompileOptionsForRequest(nsScriptLoadRequest *aRequest,
void FillCompileOptionsForRequest(const mozilla::dom::AutoJSAPI &jsapi,
nsScriptLoadRequest *aRequest,
JS::Handle<JSObject *> aScopeChain,
JS::CompileOptions *aOptions);

View File

@ -31,6 +31,7 @@
#include "gfxContext.h"
#include "gfxPattern.h"
#include "gfxPrefs.h"
#include "gfxUtils.h"
#include "CanvasUtils.h"
@ -437,6 +438,11 @@ WebGLContext::SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions)
// enforce that if stencil is specified, we also give back depth
newOpts.depth |= newOpts.stencil;
// Don't do antialiasing if we've disabled MSAA.
if (!gfxPrefs::MSAALevel()) {
newOpts.antialias = false;
}
#if 0
GenerateWarning("aaHint: %d stencil: %d depth: %d alpha: %d premult: %d preserve: %d\n",
newOpts.antialias ? 1 : 0,

View File

@ -119,10 +119,6 @@ bool WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
gl->IsSupported(GLFeature::texture_half_float);
case WebGLExtensionID::OES_texture_half_float_linear:
return gl->IsSupported(GLFeature::texture_half_float_linear);
case WebGLExtensionID::WEBGL_color_buffer_float:
return WebGLExtensionColorBufferFloat::IsSupported(this);
case WebGLExtensionID::EXT_color_buffer_half_float:
return WebGLExtensionColorBufferHalfFloat::IsSupported(this);
case WebGLExtensionID::OES_vertex_array_object:
return WebGLExtensionVertexArray::IsSupported(this);
case WebGLExtensionID::EXT_texture_filter_anisotropic:
@ -168,6 +164,10 @@ bool WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
switch (ext) {
case WebGLExtensionID::EXT_blend_minmax:
return WebGLExtensionBlendMinMax::IsSupported(this);
case WebGLExtensionID::EXT_color_buffer_half_float:
return WebGLExtensionColorBufferHalfFloat::IsSupported(this);
case WebGLExtensionID::WEBGL_color_buffer_float:
return WebGLExtensionColorBufferFloat::IsSupported(this);
default:
// For warnings-as-errors.
break;

View File

@ -416,7 +416,7 @@ skip-if = true # bug 493692
skip-if = true # bug 1021673
[test_seek_out_of_range.html]
[test_seek.html]
skip-if = true # Intermittent test failures in bug 1023564 and bug 981153
skip-if = buildapp == 'b2g' || toolkit == 'android' # Intermittent test failures in bug 1023564 and bug 981153
[test_seek2.html]
[test_seekable1.html]
[test_seekable2.html]

View File

@ -68,16 +68,10 @@ SVGPathElement::PathLength()
}
float
SVGPathElement::GetTotalLength(ErrorResult& rv)
SVGPathElement::GetTotalLength()
{
RefPtr<Path> flat = GetPathForLengthOrPositionMeasuring();
if (!flat) {
rv.Throw(NS_ERROR_FAILURE);
return 0.f;
}
return flat->ComputeLength();
return flat ? flat->ComputeLength() : 0.f;
}
already_AddRefed<nsISVGPoint>
@ -353,7 +347,11 @@ SVGPathElement::GetPathLengthScale(PathLengthScaleForType aFor)
float authorsPathLengthEstimate = mPathLength.GetAnimValue();
if (authorsPathLengthEstimate > 0) {
RefPtr<Path> path = GetPathForLengthOrPositionMeasuring();
if (!path) {
// The path is empty or invalid so its length must be zero and
// we know that 0 / authorsPathLengthEstimate = 0.
return 0.0;
}
if (aFor == eForTextPath) {
// For textPath, a transform on the referenced path affects the
// textPath layout, so when calculating the actual path length
@ -365,10 +363,7 @@ SVGPathElement::GetPathLengthScale(PathLengthScaleForType aFor)
path = builder->Finish();
}
}
if (path) {
return path->ComputeLength() / authorsPathLengthEstimate;
}
return path->ComputeLength() / authorsPathLengthEstimate;
}
}
return 1.0;

View File

@ -88,7 +88,7 @@ public:
// WebIDL
already_AddRefed<SVGAnimatedNumber> PathLength();
float GetTotalLength(ErrorResult& rv);
float GetTotalLength();
already_AddRefed<nsISVGPoint> GetPointAtLength(float distance, ErrorResult& rv);
uint32_t GetPathSegAtLength(float distance);
already_AddRefed<DOMSVGPathSegClosePath> CreateSVGPathSegClosePath();

View File

@ -32,6 +32,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1024926
is(path.getTotalLength(), 150, "Unexpected path length");
while (path.pathSegList.numberOfItems > 0) {
path.pathSegList.removeItem(0);
}
is(path.getTotalLength(), 0, "Unexpected path length");
SimpleTest.finish();
</script>

View File

@ -69,3 +69,5 @@ if (f3Main() !== lastSum)
if (!this.jsFuns)
postMessage("ok");
else
complete();

View File

@ -17,10 +17,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=929236
<script>
var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
ok(jsFuns.isAsmJSCompilationAvailable(), "asm.js compilation is available");
// generate a big ol asm.js module and compile it async so that we can hit
// the asm.js cache.
var code = "function f() { 'use asm';\n";
for (var i = 0; i < 5000; i++)
@ -56,8 +52,18 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=929236
}
}
SimpleTest.waitForExplicitFinish();
evalAsync(code);
function runTest() {
// generate a big ol asm.js module and compile it async so that we can hit
// the asm.js cache.
SimpleTest.waitForExplicitFinish();
evalAsync(code);
}
if (!jsFuns.isAsmJSCompilationAvailable()) {
ok(true, "isAsmJSCompilationAvailable is false, skipping this test!");
} else {
runTest();
}
</script>
</body>

View File

@ -17,7 +17,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=944821
<script>
var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
ok(jsFuns.isAsmJSCompilationAvailable(), "compilation is available");
var assertCacheHit = false;
// generate four slightly different big asm.js modules and compile them async
// so that we can hit the asm.js cache.
@ -50,11 +51,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=944821
document.body.appendChild(script);
}
for (var i = 0; i < N; i++)
evalAsync(codes[i]);
var finishedCount = 0;
var assertCacheHit = false;
function finishedEvalAsync() {
finishedCount++;
@ -69,7 +66,18 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=944821
}
}
SimpleTest.waitForExplicitFinish();
function runTest() {
for (var i = 0; i < N; i++)
evalAsync(codes[i]);
SimpleTest.waitForExplicitFinish();
}
if (!jsFuns.isAsmJSCompilationAvailable()) {
ok(true, "isAsmJSCompilationAvailable is false, skipping this test!");
} else {
runTest();
}
</script>
</body>

View File

@ -17,19 +17,31 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=854209
<script>
var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
ok(jsFuns.isAsmJSCompilationAvailable(), "asm.js compilation is available");
</script>
<script src="http://mochi.test:8888/tests/dom/asmjscache/test/file_slow.js"></script>
<script>
var w = new Worker('http://mochi.test:8888/tests/dom/asmjscache/test/file_slow.js');
w.onmessage = function(e) {
ok(e.data === "ok", "Worker asm.js tests");
SimpleTest.finish();
var completed = 0;
function complete() {
if (++completed == 2)
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
if (!jsFuns.isAsmJSCompilationAvailable()) {
ok(true, "isAsmJSCompilationAvailable is false, skipping this test!");
} else {
var script = document.createElement("script");
script.src = "http://mochi.test:8888/tests/dom/asmjscache/test/file_slow.js";
document.body.appendChild(script);
var w = new Worker('http://mochi.test:8888/tests/dom/asmjscache/test/file_slow.js');
w.onmessage = function(e) {
ok(e.data === "ok", "Worker asm.js tests");
complete();
}
SimpleTest.waitForExplicitFinish();
}
</script>
<script>
</script>
</body>

View File

@ -17,50 +17,57 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=941830
<script>
var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
ok(jsFuns.isAsmJSCompilationAvailable());
var asmjsCode = "function f() { 'use asm';";
for (var i = 0; i < 5000; i++)
asmjsCode += "function g" + i + "() { return " + i + "}";
asmjsCode += "return g42 }";
ok(asmjsCode.length > 100000, "code is long enough to definitely hit the cache");
function runTest() {
var asmjsCode = "function f() { 'use asm';";
for (var i = 0; i < 5000; i++)
asmjsCode += "function g" + i + "() { return " + i + "}";
asmjsCode += "return g42 }";
ok(asmjsCode.length > 100000, "code is long enough to definitely hit the cache");
var workerCode = asmjsCode;
workerCode += "if (f()() !== 42) postMessage('fail'); else postMessage('ok');";
workerCode = 'var code = "' + workerCode + '"; eval(code); eval(code)';
var workerBlob = new Blob([workerCode], {type:"application/javascript"});
var workerCode = asmjsCode;
workerCode += "if (f()() !== 42) postMessage('fail'); else postMessage('ok');";
workerCode = 'var code = "' + workerCode + '"; eval(code); eval(code)';
var workerBlob = new Blob([workerCode], {type:"application/javascript"});
var mainCode = asmjsCode;
mainCode += "ok(jsFuns.isAsmJSModuleLoadedFromCache(f), 'f is a cache hit')\n";
mainCode += "var g42 = f();\n";
mainCode += "ok(jsFuns.isAsmJSFunction(g42), 'g42 is an asm.js function');\n";
mainCode += "ok(g42() === 42, 'g42 returns the correct result');\n";
mainCode += "SimpleTest.finish();\n";
var mainBlob = new Blob([mainCode], {type:"application/javascript"});
var mainCode = asmjsCode;
mainCode += "ok(jsFuns.isAsmJSModuleLoadedFromCache(f), 'f is a cache hit')\n";
mainCode += "var g42 = f();\n";
mainCode += "ok(jsFuns.isAsmJSFunction(g42), 'g42 is an asm.js function');\n";
mainCode += "ok(g42() === 42, 'g42 returns the correct result');\n";
mainCode += "SimpleTest.finish();\n";
var mainBlob = new Blob([mainCode], {type:"application/javascript"});
var w = new Worker(URL.createObjectURL(workerBlob));
var w = new Worker(URL.createObjectURL(workerBlob));
var received = 0;
w.onmessage = function(e) {
switch (received) {
case 0:
ok(e.data === "ok", "Received first message");
received = 1;
break;
case 1:
ok(e.data === "ok", "Received second message");
received = 2;
var received = 0;
w.onmessage = function(e) {
switch (received) {
case 0:
ok(e.data === "ok", "Received first message");
received = 1;
break;
case 1:
ok(e.data === "ok", "Received second message");
received = 2;
var script = document.createElement('script');
script.src = URL.createObjectURL(mainBlob);
document.body.appendChild(script);
break;
default:
throw "Huh?";
var script = document.createElement('script');
script.src = URL.createObjectURL(mainBlob);
document.body.appendChild(script);
break;
default:
throw "Huh?";
}
}
SimpleTest.waitForExplicitFinish();
}
SimpleTest.waitForExplicitFinish();
if (!jsFuns.isAsmJSCompilationAvailable()) {
ok(true, "isAsmJSCompilationAvailable is false, skipping this test!");
} else {
runTest();
}
</script>
</body>

View File

@ -101,6 +101,11 @@ void
CustomEvent::GetDetail(JSContext* aCx,
JS::MutableHandle<JS::Value> aRetval)
{
if (!mDetail) {
aRetval.setNull();
return;
}
VariantToJsval(aCx, mDetail, aRetval);
}

View File

@ -0,0 +1,5 @@
<script>
document.createEvent('CustomEvent').detail;
</script>

View File

@ -5,6 +5,7 @@ load 422009-1.xhtml
load 457776-1.html
load 496308-1.html
load 682637-1.html
load 1033343.html
load eventctor-nulldictionary.html
load eventctor-nullstorage.html
load recursive-onload.html

View File

@ -42,6 +42,14 @@ var _fromByTestLists =
toComp: "8px"}),
new AnimTestcaseFromBy("1px", "10px", { midComp: "6px", toComp: "11px"}),
],
lengthPxSVG: [
new AnimTestcaseFromBy("0px", "8px", { fromComp: "0",
midComp: "4",
toComp: "8"}),
new AnimTestcaseFromBy("1px", "10px", { fromComp: "1",
midComp: "6",
toComp: "11"}),
],
opacity: [
new AnimTestcaseFromBy("1", "-1", { midComp: "0.5", toComp: "0"}),
new AnimTestcaseFromBy("0.4", "-0.6", { midComp: "0.1", toComp: "0"}),
@ -137,5 +145,5 @@ var gFromByBundles =
]),
new TestcaseBundle(gPropList.stroke_width,
[].concat(_fromByTestLists.lengthNoUnitsSVG,
_fromByTestLists.lengthPx))
_fromByTestLists.lengthPxSVG))
];

View File

@ -103,6 +103,20 @@ var _fromToTestLists = {
new AnimTestcaseFromTo("10px", "20px", { midComp: "15px"}),
new AnimTestcaseFromTo("41px", "1px", { midComp: "21px"}),
],
lengthPxSVG: [
new AnimTestcaseFromTo("0px", "12px", { fromComp: "0",
midComp: "6",
toComp: "12"}),
new AnimTestcaseFromTo("16px", "0px", { fromComp: "16",
midComp: "8",
toComp: "0"}),
new AnimTestcaseFromTo("10px", "20px", { fromComp: "10",
midComp: "15",
toComp: "20"}),
new AnimTestcaseFromTo("41px", "1px", { fromComp: "41",
midComp: "21",
toComp: "1"}),
],
lengthPctSVG: [
new AnimTestcaseFromTo("20.5%", "0.5%", { midComp: "10.5%" }),
],
@ -111,6 +125,14 @@ var _fromToTestLists = {
"need support for interpolating between " +
"px and percent values"),
],
lengthPxNoUnitsSVG: [
new AnimTestcaseFromTo("10", "20px", { fromComp: "10",
midComp: "15",
toComp: "20"}),
new AnimTestcaseFromTo("10px", "20", { fromComp: "10",
midComp: "15",
toComp: "20"}),
],
opacity: [
new AnimTestcaseFromTo("1", "0", { midComp: "0.5" }),
new AnimTestcaseFromTo("0.2", "0.12", { midComp: "0.16" }),
@ -385,9 +407,10 @@ var gFromToBundles = [
])),
new TestcaseBundle(gPropList.stroke_dashoffset,
[].concat(_fromToTestLists.lengthNoUnitsSVG,
_fromToTestLists.lengthPx,
_fromToTestLists.lengthPxSVG,
_fromToTestLists.lengthPxPctSVG,
_fromToTestLists.lengthPctSVG)),
_fromToTestLists.lengthPctSVG,
_fromToTestLists.lengthPxNoUnitsSVG)),
new TestcaseBundle(gPropList.stroke_linecap, [
new AnimTestcaseFromTo("butt", "round"),
new AnimTestcaseFromTo("round", "square"),
@ -403,11 +426,12 @@ var gFromToBundles = [
new TestcaseBundle(gPropList.stroke_opacity, _fromToTestLists.opacity),
new TestcaseBundle(gPropList.stroke_width,
[].concat(_fromToTestLists.lengthNoUnitsSVG,
_fromToTestLists.lengthPx,
_fromToTestLists.lengthPxSVG,
_fromToTestLists.lengthPxPctSVG,
_fromToTestLists.lengthPctSVG, [
_fromToTestLists.lengthPctSVG,
_fromToTestLists.lengthPxNoUnitsSVG, [
new AnimTestcaseFromTo("inherit", "7px",
{ fromComp: "1px", midComp: "4px"}),
{ fromComp: "1", midComp: "4", toComp: "7" }),
])),
new TestcaseBundle(gPropList.text_anchor, [
new AnimTestcaseFromTo("start", "middle"),

View File

@ -92,15 +92,6 @@ var _pacedTestLists =
comp1: "8"
}),
],
lengthPx : [
new AnimTestcasePaced("0px; 2px; 6px",
{ comp0: "0px",
comp1_6: "1px",
comp1_3: "2px",
comp2_3: "4px",
comp1: "6px"
}),
],
lengthPx : [
new AnimTestcasePaced("0px; 2px; 6px",
{ comp0: "0px",
@ -117,6 +108,22 @@ var _pacedTestLists =
comp1: "8px"
}),
],
lengthPxSVG : [
new AnimTestcasePaced("0px; 2px; 6px",
{ comp0: "0",
comp1_6: "1",
comp1_3: "2",
comp2_3: "4",
comp1: "6"
}),
new AnimTestcasePaced("10px; 12px; 8px",
{ comp0: "10",
comp1_6: "11",
comp1_3: "12",
comp2_3: "10",
comp1: "8"
}),
],
lengthPctSVG : [
new AnimTestcasePaced("5%; 6%; 4%",
{ comp0: "5%",
@ -291,12 +298,12 @@ var gPacedBundles =
])),
new TestcaseBundle(gPropList.stroke_dashoffset,
[].concat(_pacedTestLists.lengthNoUnitsSVG,
_pacedTestLists.lengthPx,
_pacedTestLists.lengthPxSVG,
_pacedTestLists.lengthPctSVG,
_pacedTestLists.lengthPxPctSVG)),
new TestcaseBundle(gPropList.stroke_width,
[].concat(_pacedTestLists.lengthNoUnitsSVG,
_pacedTestLists.lengthPx,
_pacedTestLists.lengthPxSVG,
_pacedTestLists.lengthPctSVG,
_pacedTestLists.lengthPxPctSVG)),
// XXXdholbert TODO: test 'stroke-dasharray' once we support animating it

View File

@ -58,16 +58,16 @@ function main()
verifyStyle(rect, "stroke-width", "5px");
svg.setCurrentTime(1);
verifyStyle(text, "font-size", "20px");
verifyStyle(rect, "stroke-width", "20px");
verifyStyle(rect, "stroke-width", "20");
svg.setCurrentTime(1.5);
verifyStyle(text, "font-size", "30px");
verifyStyle(rect, "stroke-width", "30px");
verifyStyle(rect, "stroke-width", "30");
svg.setCurrentTime(2);
verifyStyle(text, "font-size", "40px");
verifyStyle(rect, "stroke-width", "40px");
verifyStyle(rect, "stroke-width", "40");
svg.setCurrentTime(3);
verifyStyle(text, "font-size", "40px");
verifyStyle(rect, "stroke-width", "40px");
verifyStyle(rect, "stroke-width", "40");
} catch (e) {
// If anything goes wrong, make sure we restore textZoom before bubbling
// the exception upwards, so that we don't mess up subsequent tests.

View File

@ -13,7 +13,6 @@ interface SVGPathElement : SVGGraphicsElement {
readonly attribute SVGAnimatedNumber pathLength;
[Throws]
float getTotalLength();
[NewObject, Throws]
SVGPoint getPointAtLength(float distance);

View File

@ -525,11 +525,17 @@ public:
// Inversely scale the offset by the resolution (when you're zoomed further in,
// the same swipe should move you a shorter distance).
CSSPoint cssOffset = offset / aFrameMetrics.GetZoom();
// Ordinarily we might need to do a ScheduleComposite if either of
// the following AdjustDisplacement calls returns true, but this
// is already running as part of a FlingAnimation, so we'll be compositing
// per frame of animation anyway.
CSSPoint overscroll;
aFrameMetrics.ScrollBy(CSSPoint(
mApzc.mX.AdjustDisplacement(cssOffset.x, overscroll.x),
mApzc.mY.AdjustDisplacement(cssOffset.y, overscroll.y)
));
CSSPoint adjustedOffset;
mApzc.mX.AdjustDisplacement(cssOffset.x, adjustedOffset.x, overscroll.x);
mApzc.mY.AdjustDisplacement(cssOffset.y, adjustedOffset.y, overscroll.y);
aFrameMetrics.ScrollBy(adjustedOffset);
// The fling may have caused us to reach the end of our scroll range.
if (!IsZero(overscroll)) {
@ -1613,14 +1619,17 @@ bool AsyncPanZoomController::AttemptScroll(const ScreenPoint& aStartPoint,
// the same swipe should move you a shorter distance).
CSSPoint cssDisplacement = displacement / zoom;
CSSPoint allowedDisplacement(mX.AdjustDisplacement(cssDisplacement.x,
cssOverscroll.x),
mY.AdjustDisplacement(cssDisplacement.y,
cssOverscroll.y));
CSSPoint adjustedDisplacement;
bool xChanged = mX.AdjustDisplacement(cssDisplacement.x, adjustedDisplacement.x, cssOverscroll.x);
bool yChanged = mY.AdjustDisplacement(cssDisplacement.y, adjustedDisplacement.y, cssOverscroll.y);
if (xChanged || yChanged) {
ScheduleComposite();
}
overscroll = cssOverscroll * zoom;
if (!IsZero(allowedDisplacement)) {
ScrollBy(allowedDisplacement);
if (!IsZero(adjustedDisplacement)) {
ScrollBy(adjustedDisplacement);
ScheduleCompositeAndMaybeRepaint();
UpdateSharedCompositorFrameMetrics();
}
@ -1802,8 +1811,17 @@ CalculateDisplayPortSize(const CSSSize& aCompositionSize,
float yMultiplier = fabsf(aVelocity.y) < gfxPrefs::APZMinSkateSpeed()
? gfxPrefs::APZYStationarySizeMultiplier()
: gfxPrefs::APZYSkateSizeMultiplier();
return CSSSize(aCompositionSize.width * xMultiplier,
aCompositionSize.height * yMultiplier);
// Ensure that it is at least as large as the visible area inflated by the
// danger zone. If this is not the case then the "AboutToCheckerboard"
// function in TiledContentClient.cpp will return true even in the stable
// state.
float xSize = std::max(aCompositionSize.width * xMultiplier,
aCompositionSize.width + (2 * gfxPrefs::APZDangerZoneX()));
float ySize = std::max(aCompositionSize.height * yMultiplier,
aCompositionSize.height + (2 * gfxPrefs::APZDangerZoneY()));
return CSSSize(xSize, ySize);
}
/**

View File

@ -62,24 +62,27 @@ void Axis::StartTouch(int32_t aPos, uint32_t aTimestampMs) {
mAxisLocked = false;
}
float Axis::AdjustDisplacement(float aDisplacement, float& aOverscrollAmountOut) {
bool Axis::AdjustDisplacement(float aDisplacement,
float& aDisplacementOut,
float& aOverscrollAmountOut)
{
if (mAxisLocked) {
aOverscrollAmountOut = 0;
return 0;
aDisplacementOut = 0;
return false;
}
float displacement = aDisplacement;
// First consume any overscroll in the opposite direction along this axis.
float consumedOverscroll = 0;
if (mOverscroll > 0 && aDisplacement < 0) {
float consumedOverscroll = std::min(mOverscroll, -aDisplacement);
mOverscroll -= consumedOverscroll;
displacement += consumedOverscroll;
consumedOverscroll = std::min(mOverscroll, -aDisplacement);
} else if (mOverscroll < 0 && aDisplacement > 0) {
float consumedOverscroll = std::min(-mOverscroll, aDisplacement);
mOverscroll += consumedOverscroll;
displacement -= consumedOverscroll;
consumedOverscroll = 0 - std::min(-mOverscroll, aDisplacement);
}
mOverscroll -= consumedOverscroll;
displacement += consumedOverscroll;
// Split the requested displacement into an allowed displacement that does
// not overscroll, and an overscroll amount.
@ -90,7 +93,8 @@ float Axis::AdjustDisplacement(float aDisplacement, float& aOverscrollAmountOut)
aOverscrollAmountOut = DisplacementWillOverscrollAmount(displacement);
displacement -= aOverscrollAmountOut;
}
return displacement;
aDisplacementOut = displacement;
return fabsf(consumedOverscroll) > EPSILON;
}
float Axis::ApplyResistance(float aRequestedOverscroll) const {

View File

@ -83,9 +83,13 @@ public:
* to prevent the viewport from overscrolling the page rect), and axis locking
* (which might prevent any displacement from happening). If overscroll
* ocurred, its amount is written to |aOverscrollAmountOut|.
* The adjusted displacement is returned.
* The |aDisplacementOut| parameter is set to the adjusted
* displacement, and the function returns true iff internal overscroll amounts
* were changed.
*/
float AdjustDisplacement(float aDisplacement, float& aOverscrollAmountOut);
bool AdjustDisplacement(float aDisplacement,
float& aDisplacementOut,
float& aOverscrollAmountOut);
/**
* Overscrolls this axis by the requested amount in the requested direction.

View File

@ -192,6 +192,7 @@ SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
if (aLowPrecision && !mLastProgressiveUpdateWasLowPrecision) {
// Skip low precision rendering until we're at risk of checkerboarding.
if (!mProgressiveUpdateWasInDanger) {
TILING_PRLOG(("TILING: Aborting low-precision rendering because not at risk of checkerboarding\n"));
return true;
}
mProgressiveUpdateWasInDanger = false;
@ -201,6 +202,8 @@ SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
// Always abort updates if the resolution has changed. There's no use
// in drawing at the incorrect resolution.
if (!FuzzyEquals(compositorMetrics.GetZoom().scale, contentMetrics.GetZoom().scale)) {
TILING_PRLOG(("TILING: Aborting because resolution changed from %f to %f\n",
contentMetrics.GetZoom().scale, compositorMetrics.GetZoom().scale));
return true;
}
@ -239,6 +242,7 @@ SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
// Abort drawing stale low-precision content if there's a more recent
// display-port in the pipeline.
if (aLowPrecision && !aHasPendingNewThebesContent) {
TILING_PRLOG(("TILING: Aborting low-precision because of new pending content\n"));
return true;
}
@ -272,7 +276,14 @@ SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetric
painted = painted.Intersect(aContentMetrics.mScrollableRect);
showing = showing.Intersect(aContentMetrics.mScrollableRect);
return !painted.Contains(showing);
if (!painted.Contains(showing)) {
TILING_PRLOG_OBJ(("TILING: About to checkerboard; content %s\n", tmpstr.get()), aContentMetrics);
TILING_PRLOG_OBJ(("TILING: About to checkerboard; painted %s\n", tmpstr.get()), painted);
TILING_PRLOG_OBJ(("TILING: About to checkerboard; compositor %s\n", tmpstr.get()), aCompositorMetrics);
TILING_PRLOG_OBJ(("TILING: About to checkerboard; showing %s\n", tmpstr.get()), showing);
return true;
}
return false;
}
ClientTiledLayerBuffer::ClientTiledLayerBuffer(ClientTiledThebesLayer* aThebesLayer,

View File

@ -197,10 +197,40 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy
if (!aCount || !mCurLine)
return;
// This code assumes that mRawBuf == WIN_V3_INTERNAL_BIH_LENGTH
// and that sizeof(mRawBuf) >= BFH_INTERNAL_LENGTH
MOZ_ASSERT(sizeof(mRawBuf) == WIN_V3_INTERNAL_BIH_LENGTH);
MOZ_ASSERT(sizeof(mRawBuf) >= BFH_INTERNAL_LENGTH);
MOZ_ASSERT(OS2_INTERNAL_BIH_LENGTH < WIN_V3_INTERNAL_BIH_LENGTH);
// This code also assumes it's working with a byte array
MOZ_ASSERT(sizeof(mRawBuf[0]) == 1);
if (mPos < BFH_INTERNAL_LENGTH) { /* In BITMAPFILEHEADER */
// BFH_INTERNAL_LENGTH < sizeof(mRawBuf)
// mPos < BFH_INTERNAL_LENGTH
// BFH_INTERNAL_LENGTH - mPos < sizeof(mRawBuf)
// so toCopy <= BFH_INTERNAL_LENGTH
// so toCopy < sizeof(mRawBuf)
// so toCopy > 0 && toCopy <= BFH_INTERNAL_LENGTH
uint32_t toCopy = BFH_INTERNAL_LENGTH - mPos;
if (toCopy > aCount)
toCopy = aCount;
// mRawBuf is a byte array of size WIN_V3_INTERNAL_BIH_LENGTH (verified above)
// mPos is < BFH_INTERNAL_LENGTH
// BFH_INTERNAL_LENGTH < WIN_V3_INTERNAL_BIH_LENGTH
// so mPos < sizeof(mRawBuf)
//
// Therefore this assert should hold
MOZ_ASSERT(mPos < sizeof(mRawBuf));
// toCopy <= BFH_INTERNAL_LENGTH
// mPos >= 0 && mPos < BFH_INTERNAL_LENGTH
// sizeof(mRawBuf) >= BFH_INTERNAL_LENGTH (verified above)
//
// Therefore this assert should hold
MOZ_ASSERT(mPos + toCopy <= sizeof(mRawBuf));
memcpy(mRawBuf + mPos, aBuffer, toCopy);
mPos += toCopy;
aCount -= toCopy;
@ -216,15 +246,74 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy
mLOH = OS2_HEADER_LENGTH;
}
if (mPos >= BFH_INTERNAL_LENGTH && mPos < mLOH) { /* In BITMAPINFOHEADER */
// mLOH == WIN_V3_HEADER_LENGTH || mLOH == OS2_HEADER_LENGTH
// OS2_HEADER_LENGTH < WIN_V3_HEADER_LENGTH
// BFH_INTERNAL_LENGTH < OS2_HEADER_LENGTH
// BFH_INTERNAL_LENGTH < WIN_V3_HEADER_LENGTH
//
// So toCopy is in the range
// 1 to (WIN_V3_HEADER_LENGTH - BFH_INTERNAL_LENGTH)
// or 1 to (OS2_HEADER_LENGTH - BFH_INTERNAL_LENGTH)
//
// But WIN_V3_HEADER_LENGTH = BFH_INTERNAL_LENGTH + WIN_V3_INTERNAL_BIH_LENGTH
// and OS2_HEADER_LENGTH = BFH_INTERNAL_LENGTH + OS2_INTERNAL_BIH_LENGTH
//
// So toCopy is in the range
//
// 1 to WIN_V3_INTERNAL_BIH_LENGTH
// or 1 to OS2_INTERNAL_BIH_LENGTH
// and OS2_INTERNAL_BIH_LENGTH < WIN_V3_INTERNAL_BIH_LENGTH
//
// sizeof(mRawBuf) = WIN_V3_INTERNAL_BIH_LENGTH
// so toCopy <= sizeof(mRawBuf)
uint32_t toCopy = mLOH - mPos;
if (toCopy > aCount)
toCopy = aCount;
memcpy(mRawBuf + (mPos - BFH_INTERNAL_LENGTH), aBuffer, toCopy);
// mPos is in the range
// BFH_INTERNAL_LENGTH to (WIN_V3_HEADER_LENGTH - 1)
//
// offset is then in the range (see toCopy comments for more details)
// 0 to (WIN_V3_INTERNAL_BIH_LENGTH - 1)
//
// sizeof(mRawBuf) is WIN_V3_INTERNAL_BIH_LENGTH so this
// offset stays within bounds and this assert should hold
const uint32_t offset = mPos - BFH_INTERNAL_LENGTH;
MOZ_ASSERT(offset < sizeof(mRawBuf));
// Two cases:
// mPos = BFH_INTERNAL_LENGTH
// mLOH = WIN_V3_HEADER_LENGTH
//
// offset = 0
// toCopy = WIN_V3_INTERNAL_BIH_LENGTH
//
// This will be in the bounds of sizeof(mRawBuf)
//
// Second Case:
// mPos = WIN_V3_HEADER_LENGTH - 1
// mLOH = WIN_V3_HEADER_LENGTH
//
// offset = WIN_V3_INTERNAL_BIH_LENGTH - 1
// toCopy = 1
//
// This will be in the bounds of sizeof(mRawBuf)
//
// As sizeof(mRawBuf) == WIN_V3_INTERNAL_BIH_LENGTH (verified above)
// and WIN_V3_HEADER_LENGTH is the largest range of values. If mLOH
// was equal to OS2_HEADER_LENGTH then the ranges are smaller.
MOZ_ASSERT(offset + toCopy <= sizeof(mRawBuf));
memcpy(mRawBuf + offset, aBuffer, toCopy);
mPos += toCopy;
aCount -= toCopy;
aBuffer += toCopy;
}
// At this point mPos should be >= mLOH unless aBuffer did not have enough
// data. In the latter case aCount should be 0.
MOZ_ASSERT(mPos >= mLOH || aCount == 0);
// HasSize is called to ensure that if at this point mPos == mLOH but
// we have no data left to process, the next time WriteInternal is called
// we won't enter this condition again.
@ -376,12 +465,62 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy
}
}
else if (aCount && mBIH.compression == BI_BITFIELDS && mPos < (WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH)) {
// If compression is used, this is a windows bitmap, hence we can
// use WIN_HEADER_LENGTH instead of mLOH
// If compression is used, this is a windows bitmap (compression can't be used with OS/2 bitmaps),
// hence we can use WIN_V3_HEADER_LENGTH instead of mLOH.
// (verified below)
// If aCount != 0 then mPos should be >= mLOH due to the if statements
// at the beginning of the function
MOZ_ASSERT(mPos >= mLOH);
MOZ_ASSERT(mLOH == WIN_V3_HEADER_LENGTH);
// mLOH == WIN_V3_HEADER_LENGTH (verified above)
// mPos >= mLOH (verified above)
// mPos < WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH
//
// So toCopy is in the range
// 0 to (BITFIELD_LENGTH - 1)
uint32_t toCopy = (WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH) - mPos;
if (toCopy > aCount)
toCopy = aCount;
memcpy(mRawBuf + (mPos - WIN_V3_HEADER_LENGTH), aBuffer, toCopy);
// mPos >= WIN_V3_HEADER_LENGTH
// mPos < WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH
//
// offset is in the range
// 0 to (BITFIELD_LENGTH - 1)
//
// BITFIELD_LENGTH < WIN_V3_INTERNAL_BIH_LENGTH
// and sizeof(mRawBuf) == WIN_V3_INTERNAL_BIH_LENGTH (verified at top of function)
//
// Therefore this assert should hold
const uint32_t offset = mPos - WIN_V3_HEADER_LENGTH;
MOZ_ASSERT(offset < sizeof(mRawBuf));
// Two cases:
// mPos = WIN_V3_HEADER_LENGTH
//
// offset = 0
// toCopy = BITFIELD_LENGTH
//
// This will be in the bounds of sizeof(mRawBuf)
//
// Second case:
//
// mPos = WIN_V3_HEADER_LENGTH + BITFIELD_LENGTH - 1
//
// offset = BITFIELD_LENGTH - 1
// toCopy = 1
//
// This will be in the bounds of sizeof(mRawBuf)
//
// As BITFIELD_LENGTH < WIN_V3_INTERNAL_BIH_LENGTH and
// sizeof(mRawBuf) == WIN_V3_INTERNAL_BIH_LENGTH
//
// Therefore this assert should hold
MOZ_ASSERT(offset + toCopy <= sizeof(mRawBuf));
memcpy(mRawBuf + offset, aBuffer, toCopy);
mPos += toCopy;
aBuffer += toCopy;
aCount -= toCopy;

View File

@ -54,11 +54,11 @@ private:
* the bitmasks from mBitFields */
NS_METHOD CalcBitShift();
uint32_t mPos;
uint32_t mPos; ///< Number of bytes read from aBuffer in WriteInternal()
BMPFILEHEADER mBFH;
BITMAPV5HEADER mBIH;
char mRawBuf[WIN_V3_INTERNAL_BIH_LENGTH];
char mRawBuf[WIN_V3_INTERNAL_BIH_LENGTH]; ///< If this is changed, WriteInternal() MUST be updated
uint32_t mLOH; ///< Length of the header

View File

@ -880,8 +880,23 @@ static bool
SaveStack(JSContext *cx, unsigned argc, jsval *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
unsigned maxFrameCount = 0;
if (args.length() >= 1) {
double d;
if (!ToNumber(cx, args[0], &d))
return false;
if (d < 0) {
js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
JSDVG_SEARCH_STACK, args[0], JS::NullPtr(),
"not a valid maximum frame count", NULL);
return false;
}
maxFrameCount = d;
}
Rooted<JSObject*> stack(cx);
if (!JS::CaptureCurrentStack(cx, &stack))
if (!JS::CaptureCurrentStack(cx, &stack, maxFrameCount))
return false;
args.rval().setObjectOrNull(stack);
return true;

View File

@ -98,7 +98,9 @@ assertThrowsValue(function() { f(8,2.4) }, 2.4+36);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var identity=imp.identity; function g(x) { x=+x; return +identity(x) } return g'), null, imp)(13.37), 13.37);
// Test asm.js => ion paths
setJitCompilerOption("ion.usecount.trigger", 20);
setJitCompilerOption("ion.usecount.trigger", 10);
setJitCompilerOption("baseline.usecount.trigger", 0);
setJitCompilerOption("offthread-compilation.enable", 0);
// In registers on x64 and ARM, on the stack for x86
function ffiIntFew(a,b,c,d) { return d+1 }
@ -109,32 +111,32 @@ for (var i = 0; i < 40; i++)
// Stack and registers for x64 and ARM, stack for x86
function ffiIntMany(a,b,c,d,e,f,g,h,i,j) { return j+1 }
var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function f(i) { i=i|0; return ffi(i|0,(i+1)|0,(i+2)|0,(i+3)|0,(i+4)|0,(i+5)|0,(i+6)|0,(i+7)|0,(i+8)|0,(i+9)|0)|0 } return f'), null, {ffi:ffiIntMany});
for (var i = 0; i < 40; i++)
for (var i = 0; i < 15; i++)
assertEq(f(i), i+10);
// In registers on x64 and ARM, on the stack for x86
function ffiDoubleFew(a,b,c,d) { return d+1 }
var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function f(i) { i=+i; return +ffi(i,i+1.0,i+2.0,i+3.0) } return f'), null, {ffi:ffiDoubleFew});
for (var i = 0; i < 40; i++)
for (var i = 0; i < 15; i++)
assertEq(f(i), i+4);
// Stack and registers for x64 and ARM, stack for x86
function ffiDoubleMany(a,b,c,d,e,f,g,h,i,j) { return j+1 }
var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function f(i) { i=+i; return +ffi(i,i+1.0,i+2.0,i+3.0,i+4.0,i+5.0,i+6.0,i+7.0,i+8.0,i+9.0) } return f'), null, {ffi:ffiDoubleMany});
for (var i = 0; i < 40; i++)
for (var i = 0; i < 15; i++)
assertEq(f(i), i+10);
// Test the throw path
function ffiThrow(n) { if (n == 38) throw 'yolo'; }
function ffiThrow(n) { if (n == 14) throw 'yolo'; }
var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function f(i) { i=i|0; ffi(i >> 0); } return f'), null, {ffi:ffiThrow});
var i = 0;
try {
for (; i < 40; i++)
for (; i < 15; i++)
f(i);
throw 'assume unreachable';
} catch (e) {
assertEq(e, 'yolo');
assertEq(i, 38);
assertEq(i, 14);
}
// OOL conversion paths

View File

@ -17,10 +17,14 @@ function dumpStack()
stack = new Error().stack
}
setJitCompilerOption("ion.usecount.trigger", 10);
setJitCompilerOption("baseline.usecount.trigger", 0);
setJitCompilerOption("offthread-compilation.enable", 0);
var callFFI = asmCompile('global', 'ffis', USE_ASM + "var ffi=ffis.ffi; function f() { return ffi()|0 } return f");
var f = asmLink(callFFI, null, {ffi:dumpStack});
for (var i = 0; i < 5000; i++) {
for (var i = 0; i < 15; i++) {
stack = null;
f();
matchStack(stack, ['dumpStack', 'f']);
@ -42,7 +46,7 @@ matchStack(stack, ["dumpStack", "f", "middle", "f"]);
function returnStackDumper() { return { valueOf:function() { stack = new Error().stack } } }
var f = asmLink(callFFI, null, {ffi:returnStackDumper});
for (var i = 0; i < 5000; i++) {
for (var i = 0; i < 15; i++) {
stack = null;
f();
matchStack(stack, ['valueOf', 'f']);

View File

@ -0,0 +1,42 @@
// Test that we can capture only the N newest frames.
// This is the maxFrameCount argument to JS::CaptureCurrentStack.
load(libdir + 'asserts.js');
function recur(n, limit) {
if (n > 0)
return recur(n - 1, limit);
return saveStack(limit);
}
// Negative values are rejected.
assertThrowsInstanceOf(() => saveStack(-1), TypeError);
// Zero means 'no limit'.
assertEq(saveStack(0).parent, null);
assertEq(recur(0, 0).parent !== null, true);
assertEq(recur(0, 0).parent.parent, null);
assertEq(recur(1, 0).parent.parent.parent, null);
assertEq(recur(2, 0).parent.parent.parent.parent, null);
assertEq(recur(3, 0).parent.parent.parent.parent.parent, null);
// limit of 1
assertEq(saveStack(1).parent, null);
assertEq(recur(0, 1).parent, null);
assertEq(recur(0, 1).parent, null);
assertEq(recur(1, 1).parent, null);
assertEq(recur(2, 1).parent, null);
// limit of 2
assertEq(saveStack(2).parent, null);
assertEq(recur(0, 2).parent !== null, true);
assertEq(recur(0, 2).parent.parent, null);
assertEq(recur(1, 2).parent.parent, null);
assertEq(recur(2, 2).parent.parent, null);
// limit of 3
assertEq(saveStack(3).parent, null);
assertEq(recur(0, 3).parent !== null, true);
assertEq(recur(0, 3).parent.parent, null);
assertEq(recur(1, 3).parent.parent.parent, null);
assertEq(recur(2, 3).parent.parent.parent, null);

View File

@ -16,7 +16,6 @@
#include "jsprf.h"
#include "prmjtime.h"
#include "assembler/assembler/MacroAssembler.h"
#include "frontend/Parser.h"
#include "jit/AsmJSLink.h"
#include "jit/AsmJSModule.h"
@ -1409,9 +1408,6 @@ class MOZ_STACK_CLASS ModuleCompiler
return false;
return exits_.add(p, Move(exitDescriptor), *exitIndex);
}
bool addFunctionName(PropertyName *name, uint32_t *index) {
return module_->addFunctionName(name, index);
}
// Note a constraint on the minimum size of the heap. The heap size is
// constrained when linking to be at least the maximum of all such constraints.
@ -1443,6 +1439,11 @@ class MOZ_STACK_CLASS ModuleCompiler
bool finishGeneratingFunction(Func &func, MIRGenerator &mir, CodeGenerator &codegen) {
JS_ASSERT(func.defined() && func.code()->bound());
uint32_t beginOffset = func.code()->offset();
uint32_t endOffset = masm_.currentOffset();
if (!module_->addFunctionCodeRange(func.name(), beginOffset, endOffset))
return false;
jit::IonScriptCounts *counts = codegen.extractScriptCounts();
if (counts && !module_->addFunctionCounts(counts)) {
js_delete(counts);
@ -1480,15 +1481,19 @@ class MOZ_STACK_CLASS ModuleCompiler
module_->finishFunctionBodies(masm_.currentOffset());
}
void startGeneratingEntry(unsigned exportIndex) {
module_->exportedFunction(exportIndex).initCodeOffset(masm_.currentOffset());
}
bool finishGeneratingEntry(unsigned exportIndex) {
return module_->addEntryCodeRange(exportIndex, masm_.currentOffset());
}
void setInterpExitOffset(unsigned exitIndex) {
module_->exit(exitIndex).initInterpOffset(masm_.currentOffset());
}
void setIonExitOffset(unsigned exitIndex) {
module_->exit(exitIndex).initIonOffset(masm_.currentOffset());
}
void setEntryOffset(unsigned exportIndex) {
module_->exportedFunction(exportIndex).initCodeOffset(masm_.currentOffset());
}
void buildCompilationTimeReport(bool storedInCache, ScopedJSFreePtr<char> *out) {
ScopedJSFreePtr<char> slowFuns;
@ -1808,7 +1813,6 @@ class FunctionCompiler
ModuleCompiler & m_;
LifoAlloc & lifo_;
ParseNode * fn_;
uint32_t functionNameIndex_;
LocalMap locals_;
VarInitializerVector varInitializers_;
@ -1829,15 +1833,11 @@ class FunctionCompiler
LabeledBlockMap labeledBreaks_;
LabeledBlockMap labeledContinues_;
static const uint32_t NO_FUNCTION_NAME_INDEX = UINT32_MAX;
JS_STATIC_ASSERT(NO_FUNCTION_NAME_INDEX > CallSiteDesc::FUNCTION_NAME_INDEX_MAX);
public:
FunctionCompiler(ModuleCompiler &m, ParseNode *fn, LifoAlloc &lifo)
: m_(m),
lifo_(lifo),
fn_(fn),
functionNameIndex_(NO_FUNCTION_NAME_INDEX),
locals_(m.cx()),
varInitializers_(m.cx()),
alloc_(nullptr),
@ -2279,12 +2279,7 @@ class FunctionCompiler
uint32_t line, column;
m_.tokenStream().srcCoords.lineNumAndColumnIndex(call.node_->pn_pos.begin, &line, &column);
if (functionNameIndex_ == NO_FUNCTION_NAME_INDEX) {
if (!m_.addFunctionName(FunctionName(fn_), &functionNameIndex_))
return false;
}
CallSiteDesc desc(line, column, functionNameIndex_);
CallSiteDesc desc(line, column);
MAsmJSCall *ins = MAsmJSCall::New(alloc(), desc, callee, call.regArgs_, returnType,
call.spIncrement_);
if (!ins)
@ -5955,7 +5950,7 @@ GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFu
// PushRegsInMask(NonVolatileRegs).
masm.setFramePushed(0);
// See AsmJSFrameSize comment in Assembler-*.h.
// See AsmJSFrameSize comment in Assembler-shared.h.
#if defined(JS_CODEGEN_ARM)
masm.push(lr);
#endif // JS_CODEGEN_ARM
@ -6030,7 +6025,7 @@ GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFu
// Call into the real function.
AssertStackAlignment(masm);
masm.call(CallSiteDesc::Entry(), func.code());
masm.call(func.code());
// Pop the stack and recover the original 'argv' argument passed to the
// trampoline (which was pushed on the stack).
@ -6214,14 +6209,18 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript
m.setInterpExitOffset(exitIndex);
masm.setFramePushed(0);
// See AsmJSFrameSize comment in Assembler-*.h.
// See AsmJSFrameSize comment in Assembler-shared.h.
#if defined(JS_CODEGEN_ARM)
masm.push(lr);
#endif
#if defined(JS_CODEGEN_MIPS)
#elif defined(JS_CODEGEN_MIPS)
masm.push(ra);
#endif
// Store the frame pointer in AsmJSActivation::exitFP for stack unwinding.
Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
LoadAsmJSActivationIntoRegister(masm, activation);
masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitFP()));
MIRType typeArray[] = { MIRType_Pointer, // cx
MIRType_Pointer, // exitDatum
MIRType_Int32, // argc
@ -6240,16 +6239,11 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript
// Fill the argument array.
unsigned offsetToCallerStackArgs = AsmJSFrameSize + masm.framePushed();
Register scratch = ABIArgGenerator::NonArgReturnVolatileReg0;
Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1;
FillArgumentArray(m, exit.sig().args(), offsetToArgv, offsetToCallerStackArgs, scratch);
// Prepare the arguments for the call to InvokeFromAsmJS_*.
ABIArgMIRTypeIter i(invokeArgTypes);
Register activation = ABIArgGenerator::NonArgReturnVolatileReg1;
LoadAsmJSActivationIntoRegister(masm, activation);
// Record sp in the AsmJSActivation for stack-walking.
masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitSP()));
// argument 0: cx
if (i->kind() == ABIArg::GPR) {
@ -6290,16 +6284,16 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript
AssertStackAlignment(masm);
switch (exit.sig().retType().which()) {
case RetType::Void:
masm.callExit(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_Ignore), i.stackBytesConsumedSoFar());
masm.call(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_Ignore));
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
break;
case RetType::Signed:
masm.callExit(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToInt32), i.stackBytesConsumedSoFar());
masm.call(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToInt32));
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.unboxInt32(argv, ReturnReg);
break;
case RetType::Double:
masm.callExit(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToNumber), i.stackBytesConsumedSoFar());
masm.call(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToNumber));
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.loadDouble(argv, ReturnDoubleReg);
break;
@ -6311,6 +6305,11 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript
// Note: the caller is IonMonkey code which means there are no non-volatile
// registers to restore.
masm.freeStack(stackDec);
// Clear exitFP before the frame is destroyed.
LoadAsmJSActivationIntoRegister(masm, activation);
masm.storePtr(ImmWord(0), Address(activation, AsmJSActivation::offsetOfExitFP()));
masm.ret();
}
@ -6335,9 +6334,6 @@ GenerateOOLConvert(ModuleCompiler &m, RetType retType, Label *throwLabel)
Register activation = ABIArgGenerator::NonArgReturnVolatileReg1;
LoadAsmJSActivationIntoRegister(masm, activation);
// Record sp in the AsmJSActivation for stack-walking.
masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitSP()));
// Store real arguments
ABIArgMIRTypeIter i(callArgTypes);
@ -6365,12 +6361,12 @@ GenerateOOLConvert(ModuleCompiler &m, RetType retType, Label *throwLabel)
AssertStackAlignment(masm);
switch (retType.which()) {
case RetType::Signed:
masm.callExit(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToInt32), i.stackBytesConsumedSoFar());
masm.call(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToInt32));
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.unboxInt32(Address(StackPointer, offsetToArgv), ReturnReg);
break;
case RetType::Double:
masm.callExit(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToNumber), i.stackBytesConsumedSoFar());
masm.call(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToNumber));
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.loadDouble(Address(StackPointer, offsetToArgv), ReturnDoubleReg);
break;
@ -6388,19 +6384,22 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
m.setIonExitOffset(exitIndex);
masm.setFramePushed(0);
// See AsmJSFrameSize comment in Assembler-*.h.
#if defined(JS_CODEGEN_X64)
masm.Push(HeapReg);
#elif defined(JS_CODEGEN_ARM)
// See AsmJSFrameSize comment in Assembler-shared.h.
#if defined(JS_CODEGEN_ARM)
masm.push(lr);
// The GlobalReg (r10) and HeapReg (r11) also need to be restored before
// returning to asm.js code.
// The NANReg also needs to be restored, but is a constant and is reloaded before
// returning to asm.js code.
masm.PushRegsInMask(GeneralRegisterSet((1<<GlobalReg.code()) | (1<<HeapReg.code())));
#elif defined(JS_CODEGEN_MIPS)
masm.push(ra);
#endif
// Store the frame pointer in AsmJSActivation::exitFP for stack unwinding.
Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
LoadAsmJSActivationIntoRegister(masm, activation);
masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitFP()));
// Ion does not preserve nonvolatile registers, so we have to preserve them.
#if defined(JS_CODEGEN_X64)
masm.Push(HeapReg);
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
masm.PushRegsInMask(GeneralRegisterSet((1<<GlobalReg.code()) | (1<<HeapReg.code())));
#endif
@ -6498,19 +6497,6 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
Register reg2 = AsmJSIonExitRegE2;
Register reg3 = AsmJSIonExitRegE3;
LoadAsmJSActivationIntoRegister(masm, reg0);
// Record sp in the AsmJSActivation for stack-walking.
#if defined(JS_CODEGEN_MIPS)
// Add a flag to indicate to AsmJSFrameIterator that we are calling
// into Ion, since the offset from SP to the return address is
// different when calling Ion vs. the native ABI.
masm.ma_or(reg1, StackPointer, Imm32(0x1));
masm.storePtr(reg1, Address(reg0, AsmJSActivation::offsetOfExitSP()));
#else
masm.storePtr(StackPointer, Address(reg0, AsmJSActivation::offsetOfExitSP()));
#endif
// The following is inlined:
// JSContext *cx = activation->cx();
// Activation *act = cx->mainThread().activation();
@ -6524,6 +6510,7 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
size_t offsetOfJitTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, jitTop);
size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) +
offsetof(PerThreadData, jitJSContext);
LoadAsmJSActivationIntoRegister(masm, reg0);
masm.loadPtr(Address(reg0, AsmJSActivation::offsetOfContext()), reg3);
masm.loadPtr(Address(reg3, JSContext::offsetOfRuntime()), reg0);
masm.loadPtr(Address(reg0, offsetOfActivation), reg1);
@ -6595,13 +6582,19 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
masm.bind(&done);
masm.freeStack(stackDec);
// Restore non-volatile registers saved in the prologue.
#if defined(JS_CODEGEN_X64)
masm.Pop(HeapReg);
#endif
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
masm.loadConstantDouble(GenericNaN(), NANReg);
masm.PopRegsInMask(GeneralRegisterSet((1<<GlobalReg.code()) | (1<<HeapReg.code())));
#endif
// Clear exitFP before the frame is destroyed.
LoadAsmJSActivationIntoRegister(masm, activation);
masm.storePtr(ImmWord(0), Address(activation, AsmJSActivation::offsetOfExitFP()));
masm.ret();
JS_ASSERT(masm.framePushed() == 0);
@ -6642,18 +6635,20 @@ GenerateStackOverflowExit(ModuleCompiler &m, Label *throwLabel)
masm.align(CodeAlignment);
masm.bind(&m.stackOverflowLabel());
// The stack-overflow is checked before bumping the stack.
masm.setFramePushed(0);
// Store the frame pointer in AsmJSActivation::exitFP for stack unwinding.
Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
LoadAsmJSActivationIntoRegister(masm, activation);
masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitFP()));
MIRTypeVector argTypes(m.cx());
argTypes.infallibleAppend(MIRType_Pointer); // cx
unsigned stackDec = StackDecrementForCall(masm, argTypes, MaybeRetAddr);
masm.reserveStack(stackDec);
Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
LoadAsmJSActivationIntoRegister(masm, activation);
// Record sp in the AsmJSActivation for stack-walking.
masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitSP()));
ABIArgMIRTypeIter i(argTypes);
// argument 0: cx
@ -6668,7 +6663,11 @@ GenerateStackOverflowExit(ModuleCompiler &m, Label *throwLabel)
JS_ASSERT(i.done());
AssertStackAlignment(masm);
masm.callExit(AsmJSImmPtr(AsmJSImm_ReportOverRecursed), i.stackBytesConsumedSoFar());
masm.call(AsmJSImmPtr(AsmJSImm_ReportOverRecursed));
// Clear exitFP before the frame is destroyed.
LoadAsmJSActivationIntoRegister(masm, activation);
masm.storePtr(ImmWord(0), Address(activation, AsmJSActivation::offsetOfExitFP()));
// Don't worry about restoring the stack; throwLabel will pop everything.
masm.jump(throwLabel);
@ -6867,10 +6866,10 @@ static bool
GenerateStubs(ModuleCompiler &m)
{
for (unsigned i = 0; i < m.module().numExportedFunctions(); i++) {
m.setEntryOffset(i);
m.startGeneratingEntry(i);
if (!GenerateEntry(m, m.module().exportedFunction(i)))
return false;
if (m.masm().oom())
if (m.masm().oom() || !m.finishGeneratingEntry(i))
return false;
}

View File

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/AsmJSFrameIterator.h"
#include "jit/AsmJS.h"
#include "jit/AsmJSModule.h"
using namespace js;
using namespace js::jit;
static void *
ReturnAddressFromFP(uint8_t *fp)
{
// In asm.js code, the "frame" consists of a single word: the saved
// return address of the caller.
static_assert(AsmJSFrameSize == sizeof(void*), "Frame size mismatch");
return *(uint8_t**)fp;
}
AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation &activation)
: module_(&activation.module()),
fp_(activation.exitFP())
{
if (!fp_)
return;
settle(ReturnAddressFromFP(fp_));
}
void
AsmJSFrameIterator::operator++()
{
JS_ASSERT(!done());
fp_ += callsite_->stackDepth();
settle(ReturnAddressFromFP(fp_));
}
void
AsmJSFrameIterator::settle(void *returnAddress)
{
const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(ReturnAddressFromFP(fp_));
JS_ASSERT(codeRange);
codeRange_ = codeRange;
switch (codeRange->kind()) {
case AsmJSModule::CodeRange::Entry:
fp_ = nullptr;
JS_ASSERT(done());
return;
case AsmJSModule::CodeRange::Function:
callsite_ = module_->lookupCallSite(returnAddress);
JS_ASSERT(callsite_);
break;
}
}
JSAtom *
AsmJSFrameIterator::functionDisplayAtom() const
{
JS_ASSERT(!done());
return reinterpret_cast<const AsmJSModule::CodeRange*>(codeRange_)->functionName(*module_);
}
unsigned
AsmJSFrameIterator::computeLine(uint32_t *column) const
{
JS_ASSERT(!done());
if (column)
*column = callsite_->column();
return callsite_->line();
}

View File

@ -0,0 +1,46 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef jit_AsmJSFrameIterator_h
#define jit_AsmJSFrameIterator_h
#include "mozilla/NullPtr.h"
#include <stdint.h>
class JSAtom;
namespace js {
class AsmJSActivation;
class AsmJSModule;
namespace jit { struct CallSite; }
// Iterates over the frames of a single AsmJSActivation.
class AsmJSFrameIterator
{
const AsmJSModule *module_;
const jit::CallSite *callsite_;
uint8_t *fp_;
// Really, a const AsmJSModule::CodeRange*, but no forward declarations of
// nested classes, so use void* to avoid pulling in all of AsmJSModule.h.
const void *codeRange_;
void settle(void *returnAddress);
public:
explicit AsmJSFrameIterator() : module_(nullptr) {}
explicit AsmJSFrameIterator(const AsmJSActivation &activation);
void operator++();
bool done() const { return !fp_; }
JSAtom *functionDisplayAtom() const;
unsigned computeLine(uint32_t *column) const;
};
} // namespace js
#endif // jit_AsmJSFrameIterator_h

View File

@ -34,101 +34,6 @@ using namespace js::jit;
using mozilla::IsNaN;
using mozilla::PodZero;
static uint8_t *
ReturnAddressForExitCall(uint8_t **psp)
{
uint8_t *sp = *psp;
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
// For calls to Ion/C++ on x86/x64, the exitSP is the SP right before the call
// to C++. Since the call instruction pushes the return address, we know
// that the return address is 1 word below exitSP.
return *(uint8_t**)(sp - sizeof(void*));
#elif defined(JS_CODEGEN_ARM)
// For calls to Ion/C++ on ARM, the *caller* pushes the return address on
// the stack. For Ion, this is just part of the ABI. For C++, the return
// address is explicitly pushed before the call since we cannot expect the
// callee to immediately push lr. This means that exitSP points to the
// return address.
return *(uint8_t**)sp;
#elif defined(JS_CODEGEN_MIPS)
// On MIPS we have two cases: an exit to C++ will store the return address
// at ShadowStackSpace above sp; an exit to Ion will store the return
// address at sp. To distinguish the two cases, the low bit of sp (which is
// aligned and therefore zero) is set for Ion exits.
if (uintptr_t(sp) & 0x1) {
sp = *psp -= 0x1; // Clear the low bit
return *(uint8_t**)sp;
}
return *(uint8_t**)(sp + ShadowStackSpace);
#else
# error "Unknown architecture!"
#endif
}
static uint8_t *
ReturnAddressForJitCall(uint8_t *sp)
{
// Once inside JIT code, sp always points to the word before the return
// address.
return *(uint8_t**)(sp - sizeof(void*));
}
AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation *activation)
: module_(nullptr)
{
if (!activation || activation->isInterruptedSP())
return;
module_ = &activation->module();
sp_ = activation->exitSP();
settle(ReturnAddressForExitCall(&sp_));
}
void
AsmJSFrameIterator::operator++()
{
settle(ReturnAddressForJitCall(sp_));
}
void
AsmJSFrameIterator::settle(uint8_t *returnAddress)
{
callsite_ = module_->lookupCallSite(returnAddress);
if (!callsite_ || callsite_->isEntry()) {
module_ = nullptr;
return;
}
if (callsite_->isEntry()) {
module_ = nullptr;
return;
}
sp_ += callsite_->stackDepth();
if (callsite_->isExit())
return settle(ReturnAddressForJitCall(sp_));
JS_ASSERT(callsite_->isNormal());
}
JSAtom *
AsmJSFrameIterator::functionDisplayAtom() const
{
JS_ASSERT(!done());
return module_->functionName(callsite_->functionNameIndex());
}
unsigned
AsmJSFrameIterator::computeLine(uint32_t *column) const
{
JS_ASSERT(!done());
if (column)
*column = callsite_->column();
return callsite_->line();
}
static bool
CloneModule(JSContext *cx, MutableHandle<AsmJSModuleObject*> moduleObj)
{

View File

@ -9,31 +9,8 @@
#include "NamespaceImports.h"
class JSAtom;
namespace js {
class AsmJSActivation;
class AsmJSModule;
namespace jit { struct CallSite; }
// Iterates over the frames of a single AsmJSActivation.
class AsmJSFrameIterator
{
const AsmJSModule *module_;
const jit::CallSite *callsite_;
uint8_t *sp_;
void settle(uint8_t *returnAddress);
public:
explicit AsmJSFrameIterator(const AsmJSActivation *activation);
void operator++();
bool done() const { return !module_; }
JSAtom *functionDisplayAtom() const;
unsigned computeLine(uint32_t *column) const;
};
#ifdef JS_ION
// Create a new JSFunction to replace originalFun as the representation of the

View File

@ -167,6 +167,7 @@ AsmJSModule::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t *asmJSModu
exits_.sizeOfExcludingThis(mallocSizeOf) +
exports_.sizeOfExcludingThis(mallocSizeOf) +
callSites_.sizeOfExcludingThis(mallocSizeOf) +
codeRanges_.sizeOfExcludingThis(mallocSizeOf) +
functionNames_.sizeOfExcludingThis(mallocSizeOf) +
heapAccesses_.sizeOfExcludingThis(mallocSizeOf) +
functionCounts_.sizeOfExcludingThis(mallocSizeOf) +
@ -189,11 +190,11 @@ struct CallSiteRetAddrOffset
};
const CallSite *
AsmJSModule::lookupCallSite(uint8_t *returnAddress) const
AsmJSModule::lookupCallSite(void *returnAddress) const
{
JS_ASSERT(isFinished());
uint32_t target = returnAddress - code_;
uint32_t target = ((uint8_t*)returnAddress) - code_;
size_t lowerBound = 0;
size_t upperBound = callSites_.length();
@ -204,6 +205,45 @@ AsmJSModule::lookupCallSite(uint8_t *returnAddress) const
return &callSites_[match];
}
namespace js {
// Create an ordering on CodeRange and pc offsets suitable for BinarySearch.
// Stick these in the same namespace as AsmJSModule so that argument-dependent
// lookup will find it.
bool
operator==(size_t pcOffset, const AsmJSModule::CodeRange &rhs)
{
return pcOffset >= rhs.begin() && pcOffset < rhs.end();
}
bool
operator<=(const AsmJSModule::CodeRange &lhs, const AsmJSModule::CodeRange &rhs)
{
return lhs.begin() <= rhs.begin();
}
bool
operator<(size_t pcOffset, const AsmJSModule::CodeRange &rhs)
{
return pcOffset < rhs.begin();
}
} // namespace js
const AsmJSModule::CodeRange *
AsmJSModule::lookupCodeRange(void *pc) const
{
JS_ASSERT(isFinished());
uint32_t target = ((uint8_t*)pc) - code_;
size_t lowerBound = 0;
size_t upperBound = codeRanges_.length();
size_t match;
if (!BinarySearch(codeRanges_, lowerBound, upperBound, target, &match))
return nullptr;
return &codeRanges_[match];
}
struct HeapAccessOffset
{
const AsmJSHeapAccessVector &accesses;
@ -214,12 +254,12 @@ struct HeapAccessOffset
};
const AsmJSHeapAccess *
AsmJSModule::lookupHeapAccess(uint8_t *pc) const
AsmJSModule::lookupHeapAccess(void *pc) const
{
JS_ASSERT(isFinished());
JS_ASSERT(containsPC(pc));
uint32_t target = pc - code_;
uint32_t target = ((uint8_t*)pc) - code_;
size_t lowerBound = 0;
size_t upperBound = heapAccesses_.length();
@ -293,6 +333,11 @@ AsmJSModule::finish(ExclusiveContext *cx, TokenStream &tokenStream, MacroAssembl
CallSite &c = callSites_[i];
c.setReturnAddressOffset(masm.actualOffset(c.returnAddressOffset()));
}
for (size_t i = 0; i < codeRanges_.length(); i++) {
CodeRange &c = codeRanges_[i];
c.begin_ = masm.actualOffset(c.begin_);
c.end_ = masm.actualOffset(c.end_);
}
#endif
JS_ASSERT(pod.functionBytes_ % AsmJSPageSize == 0);
@ -1084,6 +1129,7 @@ AsmJSModule::serializedSize() const
SerializedVectorSize(exits_) +
SerializedVectorSize(exports_) +
SerializedPodVectorSize(callSites_) +
SerializedPodVectorSize(codeRanges_) +
SerializedVectorSize(functionNames_) +
SerializedPodVectorSize(heapAccesses_) +
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
@ -1104,6 +1150,7 @@ AsmJSModule::serialize(uint8_t *cursor) const
cursor = SerializeVector(cursor, exits_);
cursor = SerializeVector(cursor, exports_);
cursor = SerializePodVector(cursor, callSites_);
cursor = SerializePodVector(cursor, codeRanges_);
cursor = SerializeVector(cursor, functionNames_);
cursor = SerializePodVector(cursor, heapAccesses_);
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
@ -1130,6 +1177,7 @@ AsmJSModule::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
(cursor = DeserializeVector(cx, cursor, &exits_)) &&
(cursor = DeserializeVector(cx, cursor, &exports_)) &&
(cursor = DeserializePodVector(cx, cursor, &callSites_)) &&
(cursor = DeserializePodVector(cx, cursor, &codeRanges_)) &&
(cursor = DeserializeVector(cx, cursor, &functionNames_)) &&
(cursor = DeserializePodVector(cx, cursor, &heapAccesses_)) &&
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
@ -1200,6 +1248,7 @@ AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) con
!CloneVector(cx, exits_, &out.exits_) ||
!CloneVector(cx, exports_, &out.exports_) ||
!ClonePodVector(cx, callSites_, &out.callSites_) ||
!ClonePodVector(cx, codeRanges_, &out.codeRanges_) ||
!CloneVector(cx, functionNames_, &out.functionNames_) ||
!ClonePodVector(cx, heapAccesses_, &out.heapAccesses_) ||
!staticLinkData_.clone(cx, &out.staticLinkData_))

View File

@ -313,6 +313,33 @@ class AsmJSModule
bool clone(ExclusiveContext *cx, ExportedFunction *out) const;
};
class CodeRange
{
public:
enum Kind { Entry, Function };
private:
Kind kind_;
uint32_t begin_;
uint32_t end_;
uint32_t functionNameIndex_;
friend class AsmJSModule;
CodeRange(Kind k, uint32_t begin, uint32_t end, uint32_t functionNameIndex)
: kind_(k), begin_(begin), end_(end), functionNameIndex_(functionNameIndex)
{}
public:
CodeRange() {}
Kind kind() const { return kind_; }
uint32_t begin() const { return begin_; }
uint32_t end() const { return end_; }
PropertyName *functionName(const AsmJSModule &module) const {
JS_ASSERT(kind_ == Function);
return module.functionNames_[functionNameIndex_].name();
}
};
class Name
{
PropertyName *name_;
@ -479,6 +506,7 @@ class AsmJSModule
Vector<Exit, 0, SystemAllocPolicy> exits_;
Vector<ExportedFunction, 0, SystemAllocPolicy> exports_;
Vector<jit::CallSite, 0, SystemAllocPolicy> callSites_;
Vector<CodeRange, 0, SystemAllocPolicy> codeRanges_;
Vector<Name, 0, SystemAllocPolicy> functionNames_;
Vector<jit::AsmJSHeapAccess, 0, SystemAllocPolicy> heapAccesses_;
Vector<jit::IonScriptCounts*, 0, SystemAllocPolicy> functionCounts_;
@ -666,17 +694,20 @@ class AsmJSModule
if (len > pod.minHeapLength_)
pod.minHeapLength_ = len;
}
bool addFunctionName(PropertyName *name, uint32_t *nameIndex) {
bool addFunctionCodeRange(PropertyName *name, uint32_t begin, uint32_t end) {
JS_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
JS_ASSERT(name->isTenured());
if (functionNames_.length() > jit::CallSiteDesc::FUNCTION_NAME_INDEX_MAX)
JS_ASSERT(begin <= end);
JS_ASSERT_IF(!codeRanges_.empty(), codeRanges_.back().end() <= begin);
if (functionNames_.length() >= UINT32_MAX)
return false;
*nameIndex = functionNames_.length();
return functionNames_.append(name);
CodeRange codeRange(CodeRange::Function, begin, end, functionNames_.length());
return functionNames_.append(name) && codeRanges_.append(codeRange);
}
PropertyName *functionName(uint32_t i) const {
JS_ASSERT(isFinished());
return functionNames_[i].name();
bool addEntryCodeRange(unsigned exportIndex, uint32_t end) {
uint32_t begin = exports_[exportIndex].pod.codeOffset_;
CodeRange codeRange(CodeRange::Entry, begin, end, UINT32_MAX);
return codeRanges_.append(codeRange);
}
bool addExit(unsigned ffiIndex, unsigned *exitIndex) {
JS_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
@ -852,11 +883,15 @@ class AsmJSModule
// Lookup a callsite by the return pc (from the callee to the caller).
// Return null if no callsite was found.
const jit::CallSite *lookupCallSite(uint8_t *returnAddress) const;
const jit::CallSite *lookupCallSite(void *returnAddress) const;
// Lookup the name the code range containing the given pc. Return null if no
// code range was found.
const CodeRange *lookupCodeRange(void *pc) const;
// Lookup a heap access site by the pc which performs the access. Return
// null if no heap access was found.
const jit::AsmJSHeapAccess *lookupHeapAccess(uint8_t *pc) const;
const jit::AsmJSHeapAccess *lookupHeapAccess(void *pc) const;
// The global data section is placed after the executable code (i.e., at
// offset codeBytes_) in the module's linear allocation. The global data

View File

@ -356,7 +356,7 @@ HandleSimulatorInterrupt(JSRuntime *rt, AsmJSActivation *activation, void *fault
if (module.containsPC((void *)rt->mainThread.simulator()->get_pc()) &&
module.containsPC(faultingAddress))
{
activation->setInterrupted(nullptr);
activation->setResumePC(nullptr);
int32_t nextpc = int32_t(module.interruptExit());
rt->mainThread.simulator()->set_resume_pc(nextpc);
return true;
@ -465,7 +465,7 @@ HandleException(PEXCEPTION_POINTERS exception)
// The trampoline will jump to activation->resumePC if execution isn't
// interrupted.
if (module.containsPC(faultingAddress)) {
activation->setInterrupted(pc);
activation->setResumePC(pc);
*ppc = module.interruptExit();
JSRuntime::AutoLockForInterrupt lock(rt);
@ -668,7 +668,7 @@ HandleMachException(JSRuntime *rt, const ExceptionRequest &request)
// The trampoline will jump to activation->resumePC if execution isn't
// interrupted.
if (module.containsPC(faultingAddress)) {
activation->setInterrupted(pc);
activation->setResumePC(pc);
*ppc = module.interruptExit();
JSRuntime::AutoLockForInterrupt lock(rt);
@ -918,7 +918,7 @@ HandleSignal(int signum, siginfo_t *info, void *ctx)
// The trampoline will jump to activation->resumePC if execution isn't
// interrupted.
if (module.containsPC(faultingAddress)) {
activation->setInterrupted(pc);
activation->setResumePC(pc);
*ppc = module.interruptExit();
JSRuntime::AutoLockForInterrupt lock(rt);

View File

@ -8583,7 +8583,7 @@ CodeGenerator::visitAsmJSCall(LAsmJSCall *ins)
masm.call(mir->desc(), ToRegister(ins->getOperand(mir->dynamicCalleeOperandIndex())));
break;
case MAsmJSCall::Callee::Builtin:
masm.call(mir->desc(), AsmJSImmPtr(callee.builtin()));
masm.call(AsmJSImmPtr(callee.builtin()));
break;
}

View File

@ -140,12 +140,6 @@ static const uint32_t StackAlignment = 8;
static const uint32_t CodeAlignment = 8;
static const bool StackKeptAligned = true;
// As an invariant across architectures, within asm.js code:
// $sp % StackAlignment = (AsmJSFrameSize + masm.framePushed) % StackAlignment
// To achieve this on ARM, the first instruction of the asm.js prologue pushes
// lr without incrementing masm.framePushed.
static const uint32_t AsmJSFrameSize = sizeof(void*);
static const Scale ScalePointer = TimesFour;
class Instruction;

View File

@ -53,7 +53,7 @@ CodeGeneratorARM::generateAsmJSPrologue(Label *stackOverflowLabel)
{
JS_ASSERT(gen->compilingAsmJS());
// See comment in Assembler-arm.h about AsmJSFrameSize.
// See comment in Assembler-shared.h about AsmJSFrameSize.
masm.push(lr);
// The asm.js over-recursed handler wants to be able to assume that SP

View File

@ -1791,6 +1791,17 @@ MacroAssemblerARMCompat::callIon(Register callee)
}
}
void
MacroAssemblerARMCompat::callIonFromAsmJS(Register callee)
{
ma_callIonNoPush(callee);
// The Ion ABI has the callee pop the return address off the stack.
// The asm.js caller assumes that the call leaves sp unchanged, so bump
// the stack.
subPtr(Imm32(sizeof(void*)), sp);
}
void
MacroAssemblerARMCompat::reserveStack(uint32_t amount)
{
@ -3626,19 +3637,6 @@ MacroAssemblerARM::ma_call(ImmPtr dest)
as_blx(CallReg);
}
void
MacroAssemblerARM::ma_callAndStoreRet(const Register r, uint32_t stackArgBytes)
{
// Note: this function stores the return address to sp[0]. The caller must
// anticipate this by pushing additional space on the stack. The ABI does
// not provide space for a return address so this function may only be
// called if no argument are passed.
JS_ASSERT(stackArgBytes == 0);
AutoForbidPools afp(this);
as_dtr(IsStore, 32, Offset, pc, DTRAddr(sp, DtrOffImm(0)));
as_blx(r);
}
void
MacroAssemblerARMCompat::breakpoint()
{

View File

@ -398,9 +398,6 @@ class MacroAssemblerARM : public Assembler
void ma_call(ImmPtr dest);
// calls reg, storing the return address into sp[0]
void ma_callAndStoreRet(const Register reg, uint32_t stackArgBytes);
// Float registers can only be loaded/stored in continuous runs
// when using vstm/vldm.
// This function breaks set into continuous runs and loads/stores
@ -567,38 +564,13 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
ma_movPatchable(ImmPtr(c->raw()), ScratchRegister, Always, rs);
ma_callIonHalfPush(ScratchRegister);
}
void appendCallSite(const CallSiteDesc &desc) {
// Add an extra sizeof(void*) to include the return address that was
// pushed by the call instruction (see CallSite::stackDepth).
enoughMemory_ &= append(CallSite(desc, currentOffset(), framePushed_ + AsmJSFrameSize));
}
void call(const CallSiteDesc &desc, const Register reg) {
call(reg);
appendCallSite(desc);
enoughMemory_ &= append(desc, currentOffset(), framePushed_);
}
void call(const CallSiteDesc &desc, Label *label) {
call(label);
appendCallSite(desc);
}
void call(const CallSiteDesc &desc, AsmJSImmPtr imm) {
call(imm);
appendCallSite(desc);
}
void callExit(AsmJSImmPtr imm, uint32_t stackArgBytes) {
movePtr(imm, CallReg);
ma_callAndStoreRet(CallReg, stackArgBytes);
appendCallSite(CallSiteDesc::Exit());
}
void callIonFromAsmJS(const Register reg) {
ma_callIonNoPush(reg);
appendCallSite(CallSiteDesc::Exit());
// The Ion ABI has the callee pop the return address off the stack.
// The asm.js caller assumes that the call leaves sp unchanged, so bump
// the stack.
subPtr(Imm32(sizeof(void*)), sp);
enoughMemory_ &= append(desc, currentOffset(), framePushed_);
}
void branch(JitCode *c) {
@ -1280,6 +1252,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
// Makes an Ion call using the only two methods that it is sane for
// indep code to make a call
void callIon(Register callee);
void callIonFromAsmJS(Register callee);
void reserveStack(uint32_t amount);
void freeStack(uint32_t amount);

View File

@ -4070,7 +4070,7 @@ Simulator::execute()
int32_t rpc = resume_pc_;
if (MOZ_UNLIKELY(rpc != 0)) {
// AsmJS signal handler ran and we have to adjust the pc.
activation->setInterrupted((void *)get_pc());
activation->setResumePC((void *)get_pc());
set_pc(rpc);
resume_pc_ = 0;
}

View File

@ -152,12 +152,6 @@ static const uint32_t StackAlignment = 8;
static const uint32_t CodeAlignment = 4;
static const bool StackKeptAligned = true;
// As an invariant across architectures, within asm.js code:
// $sp % StackAlignment = (AsmJSFrameSize + masm.framePushed) % StackAlignment
// To achieve this on MIPS, the first instruction of the asm.js prologue pushes
// ra without incrementing masm.framePushed.
static const uint32_t AsmJSFrameSize = sizeof(void*);
static const Scale ScalePointer = TimesFour;
// MIPS instruction types

View File

@ -52,7 +52,7 @@ CodeGeneratorMIPS::generateAsmJSPrologue(Label *stackOverflowLabel)
{
JS_ASSERT(gen->compilingAsmJS());
// See comment in Assembler-mips.h about AsmJSFrameSize.
// See comment in Assembler-shared.h about AsmJSFrameSize.
masm.push(ra);
// The asm.js over-recursed handler wants to be able to assume that SP

View File

@ -1523,6 +1523,16 @@ MacroAssemblerMIPSCompat::callIon(Register callee)
ma_callIon(callee);
}
}
void
MacroAssemblerMIPSCompat::callIonFromAsmJS(Register callee)
{
ma_callIonNoPush(reg);
// The Ion ABI has the callee pop the return address off the stack.
// The asm.js caller assumes that the call leaves sp unchanged, so bump
// the stack.
subPtr(Imm32(sizeof(void*)), StackPointer);
}
void
MacroAssemblerMIPSCompat::reserveStack(uint32_t amount)
@ -3024,21 +3034,6 @@ MacroAssemblerMIPS::ma_callIonHalfPush(const Register r)
as_sw(ra, StackPointer, 0);
}
void
MacroAssemblerMIPS::ma_callAndStoreRet(const Register r, uint32_t stackArgBytes)
{
// Note: this function stores the return address to sp[16]. The caller
// must anticipate this by reserving additional space on the stack.
// The ABI does not provide space for a return address so this function
// stores 'ra' before any ABI arguments.
// This function may only be called if there are 4 or less arguments.
JS_ASSERT(stackArgBytes == 4 * sizeof(uintptr_t));
// This is a MIPS hack to push return address during jalr delay slot.
as_jalr(r);
as_sw(ra, StackPointer, 4 * sizeof(uintptr_t));
}
void
MacroAssemblerMIPS::ma_call(ImmPtr dest)
{

View File

@ -301,9 +301,6 @@ class MacroAssemblerMIPS : public Assembler
// calls an ion function, assuming that the stack is currently not 8 byte aligned
void ma_callIonHalfPush(const Register reg);
// calls reg, storing the return address into sp[stackArgBytes]
void ma_callAndStoreRet(const Register reg, uint32_t stackArgBytes);
void ma_call(ImmPtr dest);
void ma_jump(ImmPtr dest);
@ -415,38 +412,13 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
ma_liPatchable(ScratchRegister, Imm32((uint32_t)c->raw()));
ma_callIonHalfPush(ScratchRegister);
}
void appendCallSite(const CallSiteDesc &desc) {
// Add an extra sizeof(void*) to include the return address that was
// pushed by the call instruction (see CallSite::stackDepth).
enoughMemory_ &= append(CallSite(desc, currentOffset(), framePushed_ + AsmJSFrameSize));
}
void call(const CallSiteDesc &desc, const Register reg) {
call(reg);
appendCallSite(desc);
enoughMemory_ &= append(desc, currentOffset(), framePushed_);
}
void call(const CallSiteDesc &desc, Label *label) {
call(label);
appendCallSite(desc);
}
void call(const CallSiteDesc &desc, AsmJSImmPtr imm) {
call(imm);
appendCallSite(desc);
}
void callExit(AsmJSImmPtr imm, uint32_t stackArgBytes) {
movePtr(imm, CallReg);
ma_callAndStoreRet(CallReg, stackArgBytes);
appendCallSite(CallSiteDesc::Exit());
}
void callIonFromAsmJS(const Register reg) {
ma_callIonNoPush(reg);
appendCallSite(CallSiteDesc::Exit());
// The Ion ABI has the callee pop the return address off the stack.
// The asm.js caller assumes that the call leaves sp unchanged, so bump
// the stack.
subPtr(Imm32(sizeof(void*)), StackPointer);
enoughMemory_ &= append(desc, currentOffset(), framePushed_);
}
void branch(JitCode *c) {
@ -1002,6 +974,7 @@ public:
// Makes an Ion call using the only two methods that it is sane for
// indep code to make a call
void callIon(Register callee);
void callIonFromAsmJS(Register callee);
void reserveStack(uint32_t amount);
void freeStack(uint32_t amount);

View File

@ -577,48 +577,19 @@ class CodeLocationLabel
}
};
// Describes the user-visible properties of a callsite.
//
// A few general notes about the stack-walking supported by CallSite(Desc):
// - This information facilitates stack-walking performed by FrameIter which
// is used by Error.stack and other user-visible stack-walking functions.
// - Ion/asm.js calling conventions do not maintain a frame-pointer so
// stack-walking must lookup the stack depth based on the PC.
// - Stack-walking only occurs from C++ after a synchronous calls (JS-to-JS and
// JS-to-C++). Thus, we do not need to map arbitrary PCs to stack-depths,
// just the return address at callsites.
// - An exception to the above rule is the interrupt callback which can happen
// at arbitrary PCs. In such cases, we drop frames from the stack-walk. In
// the future when a full PC->stack-depth map is maintained, we handle this
// case.
// While the frame-pointer chain allows the stack to be unwound without
// metadata, Error.stack still needs to know the line/column of every call in
// the chain. A CallSiteDesc describes the line/column of a single callsite.
// A CallSiteDesc is created by callers of MacroAssembler.
class CallSiteDesc
{
uint32_t line_;
uint32_t column_;
uint32_t functionNameIndex_;
static const uint32_t sEntryTrampoline = UINT32_MAX;
static const uint32_t sExit = UINT32_MAX - 1;
public:
static const uint32_t FUNCTION_NAME_INDEX_MAX = UINT32_MAX - 2;
CallSiteDesc() {}
CallSiteDesc(uint32_t line, uint32_t column, uint32_t functionNameIndex)
: line_(line), column_(column), functionNameIndex_(functionNameIndex)
{}
static CallSiteDesc Entry() { return CallSiteDesc(0, 0, sEntryTrampoline); }
static CallSiteDesc Exit() { return CallSiteDesc(0, 0, sExit); }
bool isEntry() const { return functionNameIndex_ == sEntryTrampoline; }
bool isExit() const { return functionNameIndex_ == sExit; }
bool isNormal() const { return !(isEntry() || isExit()); }
uint32_t line() const { JS_ASSERT(isNormal()); return line_; }
uint32_t column() const { JS_ASSERT(isNormal()); return column_; }
uint32_t functionNameIndex() const { JS_ASSERT(isNormal()); return functionNameIndex_; }
CallSiteDesc(uint32_t line, uint32_t column) : line_(line), column_(column) {}
uint32_t line() const { return line_; }
uint32_t column() const { return column_; }
};
// Adds to CallSiteDesc the metadata necessary to walk the stack given an
@ -641,13 +612,21 @@ struct CallSite : public CallSiteDesc
uint32_t returnAddressOffset() const { return returnAddressOffset_; }
// The stackDepth measures the amount of stack space pushed since the
// function was called. In particular, this includes the word pushed by the
// call instruction on x86/x64.
uint32_t stackDepth() const { JS_ASSERT(!isEntry()); return stackDepth_; }
// function was called. In particular, this includes the pushed return
// address on all archs (whether or not the call instruction pushes the
// return address (x86/x64) or the prologue does (ARM/MIPS).
uint32_t stackDepth() const { return stackDepth_; }
};
typedef Vector<CallSite, 0, SystemAllocPolicy> CallSiteVector;
// As an invariant across architectures, within asm.js code:
// $sp % StackAlignment = (AsmJSFrameSize + masm.framePushed) % StackAlignment
// AsmJSFrameSize is 1 word, for the return address pushed by the call (or, in
// the case of ARM/MIPS, by the first instruction of the prologue). This means
// masm.framePushed never includes the pushed return address.
static const uint32_t AsmJSFrameSize = sizeof(void*);
// Summarizes a heap access made by asm.js code that needs to be patched later
// and/or looked up by the asm.js signal handlers. Different architectures need
// to know different things (x64: offset and length, ARM: where to patch in
@ -821,7 +800,11 @@ class AssemblerShared
return !enoughMemory_;
}
bool append(CallSite callsite) { return callsites_.append(callsite); }
bool append(const CallSiteDesc &desc, size_t currentOffset, size_t framePushed) {
// framePushed does not include AsmJSFrameSize, so add it in here (see
// CallSite::stackDepth).
return callsites_.append(CallSite(desc, currentOffset, framePushed + AsmJSFrameSize));
}
CallSiteVector &&extractCallSites() { return Move(callsites_); }
bool append(AsmJSHeapAccess access) { return asmJSHeapAccesses_.append(access); }

View File

@ -667,26 +667,23 @@ class MacroAssemblerX86Shared : public Assembler
bool buildFakeExitFrame(Register scratch, uint32_t *offset);
void callWithExitFrame(JitCode *target);
void callIon(Register callee) {
call(callee);
}
void appendCallSite(const CallSiteDesc &desc) {
// Add an extra sizeof(void*) to include the return address that was
// pushed by the call instruction (see CallSite::stackDepth).
enoughMemory_ &= append(CallSite(desc, currentOffset(), framePushed_ + AsmJSFrameSize));
}
void call(const CallSiteDesc &desc, Label *label) {
call(label);
appendCallSite(desc);
enoughMemory_ &= append(desc, currentOffset(), framePushed_);
}
void call(const CallSiteDesc &desc, Register reg) {
call(reg);
appendCallSite(desc);
enoughMemory_ &= append(desc, currentOffset(), framePushed_);
}
void callIonFromAsmJS(Register reg) {
call(CallSiteDesc::Exit(), reg);
void callIon(Register callee) {
call(callee);
}
void callIonFromAsmJS(Register callee) {
call(callee);
}
void call(AsmJSImmPtr target) {
mov(target, eax);
call(eax);
}
void checkStackAlignment() {

View File

@ -185,13 +185,6 @@ static const uint32_t StackAlignment = 16;
static const bool StackKeptAligned = false;
static const uint32_t CodeAlignment = 8;
// As an invariant across architectures, within asm.js code:
// $sp % StackAlignment = (AsmJSFrameSize + masm.framePushed) % StackAlignment
// On x64, this naturally falls out of the fact that the 'call' instruction
// pushes the return address on the stack and masm.framePushed = 0 at the first
// instruction of the prologue.
static const uint32_t AsmJSFrameSize = sizeof(void*);
static const Scale ScalePointer = TimesEight;
} // namespace jit

View File

@ -100,18 +100,6 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
void call(ImmPtr target) {
call(ImmWord(uintptr_t(target.value)));
}
void call(AsmJSImmPtr target) {
mov(target, rax);
call(rax);
}
void call(const CallSiteDesc &desc, AsmJSImmPtr target) {
call(target);
appendCallSite(desc);
}
void callExit(AsmJSImmPtr target, uint32_t stackArgBytes) {
call(CallSiteDesc::Exit(), target);
}
// Refers to the upper 32 bits of a 64-bit Value operand.
// On x86_64, the upper 32 bits do not necessarily only contain the type.

View File

@ -113,13 +113,6 @@ static const uint32_t StackAlignment = 4;
static const bool StackKeptAligned = false;
static const uint32_t CodeAlignment = 8;
// As an invariant across architectures, within asm.js code:
// $sp % StackAlignment = (AsmJSFrameSize + masm.framePushed) % StackAlignment
// On x86, this naturally falls out of the fact that the 'call' instruction
// pushes the return address on the stack and masm.framePushed = 0 at the first
// instruction of the prologue.
static const uint32_t AsmJSFrameSize = sizeof(void*);
struct ImmTag : public Imm32
{
ImmTag(JSValueTag mask)
@ -382,13 +375,6 @@ class Assembler : public AssemblerX86Shared
JmpSrc src = masm.call();
addPendingJump(src, target, Relocation::HARDCODED);
}
void call(AsmJSImmPtr target) {
// Moving to a register is suboptimal. To fix (use a single
// call-immediate instruction) we'll need to distinguish a new type of
// relative patch to an absolute address in AsmJSAbsoluteLink.
mov(target, eax);
call(eax);
}
// Emit a CALL or CMP (nop) instruction. ToggleCall can be used to patch
// this instruction.

View File

@ -1120,13 +1120,6 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
Push(dynStack);
call(target);
}
void call(const CallSiteDesc &desc, AsmJSImmPtr target) {
call(target);
appendCallSite(desc);
}
void callExit(AsmJSImmPtr target, uint32_t stackArgBytes) {
call(CallSiteDesc::Exit(), target);
}
#ifdef JSGC_GENERATIONAL
void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label *label);

View File

@ -6589,12 +6589,12 @@ JS::SetOutOfMemoryCallback(JSRuntime *rt, OutOfMemoryCallback cb, void *data)
}
JS_PUBLIC_API(bool)
JS::CaptureCurrentStack(JSContext *cx, JS::MutableHandleObject stackp)
JS::CaptureCurrentStack(JSContext *cx, JS::MutableHandleObject stackp, unsigned maxFrameCount)
{
JSCompartment *compartment = cx->compartment();
JS_ASSERT(compartment);
Rooted<SavedFrame *> frame(cx);
if (!compartment->savedStacks().saveCurrentStack(cx, &frame))
if (!compartment->savedStacks().saveCurrentStack(cx, &frame, maxFrameCount))
return false;
stackp.set(frame.get());
return true;

View File

@ -5194,8 +5194,14 @@ typedef void
extern JS_PUBLIC_API(void)
SetOutOfMemoryCallback(JSRuntime *rt, OutOfMemoryCallback cb, void *data);
/*
* Capture the current call stack as a chain of SavedFrame objects, and set
* |stackp| to the SavedFrame for the newest stack frame. If |maxFrameCount| is
* non-zero, capture at most the youngest |maxFrameCount| frames.
*/
extern JS_PUBLIC_API(bool)
CaptureCurrentStack(JSContext *cx, MutableHandleObject stackp);
CaptureCurrentStack(JSContext *cx, MutableHandleObject stackp, unsigned maxFrameCount = 0);
} /* namespace JS */

View File

@ -800,7 +800,7 @@ js::FindBody(JSContext *cx, HandleFunction fun, HandleLinearString src, size_t *
*bodyStart = ts.currentToken().pos.begin;
if (braced)
*bodyStart += 1;
RangedPtr<const jschar> end = srcChars.end();
mozilla::RangedPtr<const jschar> end = srcChars.end();
if (end[-1] == '}') {
end--;
} else {

View File

@ -27,7 +27,6 @@
#include <ctype.h>
#include <string.h>
#include <wchar.h>
#include "jsapi.h"
#include "jsarray.h"
@ -1168,22 +1167,12 @@ FirstCharMatcher8bit(const char *text, uint32_t n, const char pat)
}
static const jschar *
FirstCharMatcher16bit (const jschar *text, uint32_t n, const jschar pat)
FirstCharMatcher16bit(const jschar *text, uint32_t n, const jschar pat)
{
/* Some platforms define wchar_t as signed and others not. */
#if (WCHAR_MIN == 0 && WCHAR_MAX == UINT16_MAX) || (WCHAR_MIN == INT16_MIN && WCHAR_MAX == INT16_MAX)
#if defined(XP_DARWIN) || defined(XP_WIN)
/*
* Wmemchr works the best.
* But only possible to use this when,
* size of jschar = size of wchar_t.
*/
const wchar_t *wtext = (const wchar_t *) text;
const wchar_t wpat = (const wchar_t) pat;
return (jschar *) (wmemchr(wtext, wpat, n));
#elif defined(__clang__)
/*
* Performance under memchr is horrible in clang.
* Hence it is best to use UnrolledMatcher in this case
* Performance of memchr is horrible in OSX. Windows is better,
* but it is still better to use UnrolledMatcher.
*/
return FirstCharMatcherUnrolled<jschar, jschar>(text, n, pat);
#else

View File

@ -248,6 +248,7 @@ if CONFIG['ENABLE_ION']:
'irregexp/NativeRegExpMacroAssembler.cpp',
'jit/AliasAnalysis.cpp',
'jit/AsmJS.cpp',
'jit/AsmJSFrameIterator.cpp',
'jit/AsmJSLink.cpp',
'jit/AsmJSModule.cpp',
'jit/AsmJSSignalHandlers.cpp',

View File

@ -396,13 +396,13 @@ SavedStacks::init()
}
bool
SavedStacks::saveCurrentStack(JSContext *cx, MutableHandleSavedFrame frame)
SavedStacks::saveCurrentStack(JSContext *cx, MutableHandleSavedFrame frame, unsigned maxFrameCount)
{
JS_ASSERT(initialized());
JS_ASSERT(&cx->compartment()->savedStacks() == this);
ScriptFrameIter iter(cx);
return insertFrames(cx, iter, frame);
return insertFrames(cx, iter, frame, maxFrameCount);
}
void
@ -476,7 +476,8 @@ SavedStacks::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
}
bool
SavedStacks::insertFrames(JSContext *cx, ScriptFrameIter &iter, MutableHandleSavedFrame frame)
SavedStacks::insertFrames(JSContext *cx, ScriptFrameIter &iter, MutableHandleSavedFrame frame,
unsigned maxFrameCount)
{
if (iter.done()) {
frame.set(nullptr);
@ -497,8 +498,20 @@ SavedStacks::insertFrames(JSContext *cx, ScriptFrameIter &iter, MutableHandleSav
// script and callee should keep compartment alive.
JSCompartment *compartment = iter.compartment();
RootedSavedFrame parentFrame(cx);
if (!insertFrames(cx, ++iter, &parentFrame))
return false;
// If maxFrameCount is zero, then there's no limit on the number of frames.
if (maxFrameCount == 0) {
if (!insertFrames(cx, ++iter, &parentFrame, 0))
return false;
} else if (maxFrameCount == 1) {
// Since we were only asked to save one frame, the SavedFrame we're
// building here should have no parent, even if there are older frames
// on the stack.
parentFrame = nullptr;
} else {
if (!insertFrames(cx, ++iter, &parentFrame, maxFrameCount - 1))
return false;
}
AutoLocationValueRooter location(cx);
if (!getLocation(cx, script, pc, &location))

View File

@ -104,7 +104,7 @@ class SavedStacks {
bool init();
bool initialized() const { return frames.initialized(); }
bool saveCurrentStack(JSContext *cx, MutableHandleSavedFrame frame);
bool saveCurrentStack(JSContext *cx, MutableHandleSavedFrame frame, unsigned maxFrameCount = 0);
void sweep(JSRuntime *rt);
void trace(JSTracer *trc);
uint32_t count();
@ -116,7 +116,8 @@ class SavedStacks {
SavedFrame::Set frames;
JSObject *savedFrameProto;
bool insertFrames(JSContext *cx, ScriptFrameIter &iter, MutableHandleSavedFrame frame);
bool insertFrames(JSContext *cx, ScriptFrameIter &iter, MutableHandleSavedFrame frame,
unsigned maxFrameCount = 0);
SavedFrame *getOrCreateSavedFrame(JSContext *cx, const SavedFrame::Lookup &lookup);
// |SavedFrame.prototype| is created lazily and held weakly. It should only
// be accessed through this method.

View File

@ -587,7 +587,7 @@ FrameIter::settleOnActivation()
}
if (activation->isAsmJS()) {
data_.asmJSFrames_ = AsmJSFrameIterator(data_.activations_->asAsmJS());
data_.asmJSFrames_ = AsmJSFrameIterator(*data_.activations_->asAsmJS());
if (data_.asmJSFrames_.done()) {
++data_.activations_;
@ -639,7 +639,7 @@ FrameIter::Data::Data(ThreadSafeContext *cx, SavedOption savedOption,
#ifdef JS_ION
, jitFrames_((uint8_t *)nullptr, SequentialExecution)
, ionInlineFrameNo_(0)
, asmJSFrames_(nullptr)
, asmJSFrames_()
#endif
{
}
@ -1688,7 +1688,7 @@ AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module)
errorRejoinSP_(nullptr),
profiler_(nullptr),
resumePC_(nullptr),
exitSP_(nullptr)
exitFP_(nullptr)
{
if (cx->runtime()->spsProfiler.enabled()) {
// Use a profiler string that matches jsMatch regex in

View File

@ -12,7 +12,7 @@
#include "jsfun.h"
#include "jsscript.h"
#include "jit/AsmJSLink.h"
#include "jit/AsmJSFrameIterator.h"
#include "jit/JitFrameIterator.h"
#ifdef CHECK_OSIPOINT_REGISTERS
#include "jit/Registers.h" // for RegisterDump
@ -1511,9 +1511,7 @@ class AsmJSActivation : public Activation
void *errorRejoinSP_;
SPSProfiler *profiler_;
void *resumePC_;
uint8_t *exitSP_;
static const intptr_t InterruptedSP = -1;
uint8_t *exitFP_;
public:
AsmJSActivation(JSContext *cx, AsmJSModule &module);
@ -1529,16 +1527,18 @@ class AsmJSActivation : public Activation
// Initialized by JIT code:
static unsigned offsetOfErrorRejoinSP() { return offsetof(AsmJSActivation, errorRejoinSP_); }
static unsigned offsetOfExitSP() { return offsetof(AsmJSActivation, exitSP_); }
static unsigned offsetOfExitFP() { return offsetof(AsmJSActivation, exitFP_); }
// Set from SIGSEGV handler:
void setInterrupted(void *pc) { resumePC_ = pc; exitSP_ = (uint8_t*)InterruptedSP; }
bool isInterruptedSP() const { return exitSP_ == (uint8_t*)InterruptedSP; }
void setResumePC(void *pc) { resumePC_ = pc; }
// Note: exitSP is the sp right before the call instruction. On x86, this
// means before the return address is pushed on the stack, on ARM, this
// means after.
uint8_t *exitSP() const { JS_ASSERT(!isInterruptedSP()); return exitSP_; }
// If pc is in C++/Ion code, exitFP points to the innermost asm.js frame
// (the one that called into C++). While in asm.js code, exitFP is either
// null or points to the innermost asm.js frame. Thus, it is always valid to
// unwind a non-null exitFP. The only way C++ can observe a null exitFP is
// asychronous interruption of asm.js execution (viz., via the profiler,
// a signal handler, or the interrupt exit).
uint8_t *exitFP() const { return exitFP_; }
};
// A FrameIter walks over the runtime's stack of JS script activations,

View File

@ -79,7 +79,8 @@ StackScopedCloneRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t ta
if (!JS_WrapObject(cx, &obj))
return nullptr;
if (!xpc::NewFunctionForwarder(cx, obj, true, &functionValue))
FunctionForwarderOptions forwarderOptions(cx);
if (!xpc::NewFunctionForwarder(cx, JSID_VOIDHANDLE, obj, forwarderOptions, &functionValue))
return nullptr;
return &functionValue.toObject();
@ -139,9 +140,14 @@ StackScopedCloneWrite(JSContext *cx, JSStructuredCloneWriter *writer,
return true;
}
if (cloneData->mOptions->cloneFunctions && JS_ObjectIsCallable(cx, obj)) {
cloneData->mFunctions.append(obj);
return JS_WriteUint32Pair(writer, SCTAG_FUNCTION, cloneData->mFunctions.length() - 1);
if (JS_ObjectIsCallable(cx, obj)) {
if (cloneData->mOptions->cloneFunctions) {
cloneData->mFunctions.append(obj);
return JS_WriteUint32Pair(writer, SCTAG_FUNCTION, cloneData->mFunctions.length() - 1);
} else {
JS_ReportError(cx, "Permission denied to pass a Function via structured clone");
return false;
}
}
JS_ReportError(cx, "Encountered unsupported value type writing stack-scoped structured clone");
@ -201,17 +207,30 @@ CloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
NS_ASSERTION(v.isObject(), "weird function");
RootedObject origFunObj(cx, UncheckedUnwrap(&v.toObject()));
// Grab the options from the reserved slot.
RootedObject optionsObj(cx, &js::GetFunctionNativeReserved(&args.callee(), 1).toObject());
FunctionForwarderOptions options(cx, optionsObj);
if (!options.Parse())
return false;
// Grab and unwrap the underlying callable.
RootedObject forwarderObj(cx, &js::GetFunctionNativeReserved(&args.callee(), 0).toObject());
RootedObject origFunObj(cx, UncheckedUnwrap(forwarderObj));
{
JSAutoCompartment ac(cx, origFunObj);
// Note: only the arguments are cloned not the |this| or the |callee|.
// Function forwarder does not use those.
StackScopedCloneOptions options;
options.wrapReflectors = true;
StackScopedCloneOptions cloneOptions;
cloneOptions.wrapReflectors = true;
for (unsigned i = 0; i < args.length(); i++) {
if (!StackScopedClone(cx, options, args[i])) {
RootedObject argObj(cx, args[i].isObject() ? &args[i].toObject() : nullptr);
if (options.allowCallbacks && argObj && JS_ObjectIsCallable(cx, argObj)) {
FunctionForwarderOptions innerOptions(cx);
if (!JS_WrapObject(cx, &argObj))
return false;
if (!xpc::NewFunctionForwarder(cx, JSID_VOIDHANDLE, argObj, innerOptions, args[i]))
return nullptr;
} else if (!StackScopedClone(cx, cloneOptions, args[i])) {
return false;
}
}
@ -243,13 +262,39 @@ NonCloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp)
return JS_CallFunctionValue(cx, obj, v, args, args.rval());
}
bool
NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone,
MutableHandleValue vp)
NewFunctionForwarder(JSContext *cx, HandleId idArg, HandleObject callable,
FunctionForwarderOptions &options, MutableHandleValue vp)
{
JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder :
NonCloningFunctionForwarder,
0,0, JS::CurrentGlobalOrNull(cx), id);
RootedId id(cx, idArg);
if (id == JSID_VOIDHANDLE)
id = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EMPTYSTRING);
JSFunction *fun = js::NewFunctionByIdWithReserved(cx, CloningFunctionForwarder, 0,0,
JS::CurrentGlobalOrNull(cx), id);
if (!fun)
return false;
// Stash the callable in slot 0.
AssertSameCompartment(cx, callable);
RootedObject funObj(cx, JS_GetFunctionObject(fun));
js::SetFunctionNativeReserved(funObj, 0, ObjectValue(*callable));
// Stash the options in slot 1.
RootedObject optionsObj(cx, options.ToJSObject(cx));
if (!optionsObj)
return false;
js::SetFunctionNativeReserved(funObj, 1, ObjectValue(*optionsObj));
vp.setObject(*funObj);
return true;
}
bool
NewNonCloningFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable,
MutableHandleValue vp)
{
JSFunction *fun = js::NewFunctionByIdWithReserved(cx, NonCloningFunctionForwarder,
0,0, JS::CurrentGlobalOrNull(cx), id);
if (!fun)
return false;
@ -259,18 +304,6 @@ NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doc
return true;
}
bool
NewFunctionForwarder(JSContext *cx, HandleObject callable, bool doclone,
MutableHandleValue vp)
{
RootedId emptyId(cx);
RootedValue emptyStringValue(cx, JS_GetEmptyStringValue(cx));
if (!JS_ValueToId(cx, emptyStringValue, &emptyId))
return false;
return NewFunctionForwarder(cx, emptyId, callable, doclone, vp);
}
bool
ExportFunction(JSContext *cx, HandleValue vfunction, HandleValue vscope, HandleValue voptions,
MutableHandleValue rval)
@ -283,7 +316,7 @@ ExportFunction(JSContext *cx, HandleValue vfunction, HandleValue vscope, HandleV
RootedObject funObj(cx, &vfunction.toObject());
RootedObject targetScope(cx, &vscope.toObject());
ExportOptions options(cx, hasOptions ? &voptions.toObject() : nullptr);
ExportFunctionOptions options(cx, hasOptions ? &voptions.toObject() : nullptr);
if (hasOptions && !options.Parse())
return false;
@ -334,7 +367,9 @@ ExportFunction(JSContext *cx, HandleValue vfunction, HandleValue vscope, HandleV
// And now, let's create the forwarder function in the target compartment
// for the function the be exported.
if (!NewFunctionForwarder(cx, id, funObj, /* doclone = */ true, rval)) {
FunctionForwarderOptions forwarderOptions(cx);
forwarderOptions.allowCallbacks = options.allowCallbacks;
if (!NewFunctionForwarder(cx, id, funObj, forwarderOptions, rval)) {
JS_ReportError(cx, "Exporting function failed");
return false;
}

View File

@ -3084,7 +3084,7 @@ nsXPCComponents_Utils::MakeObjectPropsNormal(HandleValue vobj, JSContext *cx)
if (!js::IsWrapper(propobj) || !JS_ObjectIsCallable(cx, propobj))
continue;
if (!NewFunctionForwarder(cx, id, propobj, /* doclone = */ false, &v) ||
if (!NewNonCloningFunctionForwarder(cx, id, propobj, &v) ||
!JS_SetPropertyById(cx, obj, id, v))
return NS_ERROR_FAILURE;
}

View File

@ -85,6 +85,7 @@ const char* const XPCJSRuntime::mStrings[] = {
"length", // IDX_LENGTH
"name", // IDX_NAME
"undefined", // IDX_UNDEFINED
"", // IDX_EMPTYSTRING
};
/***************************************************************************/

View File

@ -481,6 +481,7 @@ public:
IDX_LENGTH ,
IDX_NAME ,
IDX_UNDEFINED ,
IDX_EMPTYSTRING ,
IDX_TOTAL_COUNT // just a count of the above
};
@ -3280,17 +3281,23 @@ Atob(JSContext *cx, unsigned argc, jsval *vp);
bool
Btoa(JSContext *cx, unsigned argc, jsval *vp);
class FunctionForwarderOptions;
// Helper function that creates a JSFunction that wraps a native function that
// forwards the call to the original 'callable'. If the 'doclone' argument is
// set, it also structure clones non-native arguments for extra security.
// forwards the call to the original 'callable'. For improved security, any
// object-valued arguments are cloned at call time, unless either:
//
// * The object is a function and FunctionForwarderOptions::allowCallbacks is set
// * The object is a reflector, in which case it is wrapped.
bool
NewFunctionForwarder(JSContext *cx, JS::HandleId id, JS::HandleObject callable,
bool doclone, JS::MutableHandleValue vp);
FunctionForwarderOptions &options, JS::MutableHandleValue vp);
// Old-style function forwarding without structured-cloning for arguments. This
// is deprecated.
bool
NewFunctionForwarder(JSContext *cx, JS::HandleObject callable,
bool doclone, JS::MutableHandleValue vp);
NewNonCloningFunctionForwarder(JSContext *cx, JS::HandleId id,
JS::HandleObject callable, JS::MutableHandleValue vp);
// Old fashioned xpc error reporter. Try to use JS_ReportError instead.
nsresult
@ -3395,17 +3402,22 @@ public:
JS::RootedId defineAs;
};
class MOZ_STACK_CLASS ExportOptions : public OptionsBase {
class MOZ_STACK_CLASS ExportFunctionOptions : public OptionsBase {
public:
ExportOptions(JSContext *cx = xpc_GetSafeJSContext(),
ExportFunctionOptions(JSContext *cx = xpc_GetSafeJSContext(),
JSObject* options = nullptr)
: OptionsBase(cx, options)
, defineAs(cx, JSID_VOID)
, allowCallbacks(false)
{ }
virtual bool Parse() { return ParseId("defineAs", &defineAs); };
virtual bool Parse() {
return ParseId("defineAs", &defineAs) &&
ParseBoolean("allowCallbacks", &allowCallbacks);
};
JS::RootedId defineAs;
bool allowCallbacks;
};
class MOZ_STACK_CLASS StackScopedCloneOptions : public OptionsBase {
@ -3422,10 +3434,51 @@ public:
ParseBoolean("cloneFunctions", &cloneFunctions);
};
// When a reflector is encountered, wrap it rather than aborting the clone.
bool wrapReflectors;
// When a function is encountered, clone it (exportFunction-style) rather than
// aborting the clone.
bool cloneFunctions;
};
class MOZ_STACK_CLASS FunctionForwarderOptions : public OptionsBase {
public:
FunctionForwarderOptions(JSContext *cx = xpc_GetSafeJSContext(),
JSObject* options = nullptr)
: OptionsBase(cx, options)
, allowCallbacks(false)
{ }
JSObject *ToJSObject(JSContext *cx) {
JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
JS::RootedObject obj(cx, JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), global));
if (!obj)
return nullptr;
JS::RootedValue val(cx);
unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
val = JS::BooleanValue(allowCallbacks);
if (!JS_DefineProperty(cx, obj, "allowCallbacks", val, attrs))
return nullptr;
return obj;
}
virtual bool Parse() {
return ParseBoolean("allowCallbacks", &allowCallbacks);
};
// Allow callback arguments. This is similar to setting cloneFunctions in
// StackScopedCloneOptions, except that cloneFunctions will clone any Function
// encountered in the object graph, whereas this option only allows the base
// object to be supported.
//
// So invoking: |forwardedFunction(callback)| will work, but
// |forwardedFunction({ cb: callback })| will not.
bool allowCallbacks;
};
JSObject *
CreateGlobalObject(JSContext *cx, const JSClass *clasp, nsIPrincipal *principal,
JS::CompartmentOptions& aOptions);

View File

@ -10,6 +10,7 @@ function run_test() {
epsb.do_check_true = do_check_true;
epsb.do_check_eq = do_check_eq;
epsb.do_check_neq = do_check_neq;
subsb.do_check_true = do_check_true;
// Exporting should work if prinicipal of the source sandbox
// subsumes the principal of the target sandbox.
@ -17,7 +18,7 @@ function run_test() {
Object.prototype.protoProp = "common";
var wasCalled = false;
var _this = this;
this.funToExport = function(a, obj, native, mixed) {
this.funToExport = function(a, obj, native, mixed, callback) {
do_check_eq(a, 42);
do_check_neq(obj, subsb.tobecloned);
do_check_eq(obj.cloned, "cloned");
@ -26,13 +27,18 @@ function run_test() {
do_check_eq(_this, this);
do_check_eq(mixed.xrayed, subsb.xrayed);
do_check_eq(mixed.xrayed2, subsb.xrayed2);
if (typeof callback == 'function') {
do_check_eq(typeof subsb.callback, 'function');
do_check_neq(callback, subsb.callback);
callback();
}
wasCalled = true;
};
this.checkIfCalled = function() {
do_check_true(wasCalled);
wasCalled = false;
}
exportFunction(funToExport, subsb, { defineAs: "imported" });
exportFunction(funToExport, subsb, { defineAs: "imported", allowCallbacks: true });
}.toSource() + ")()", epsb);
subsb.xrayed = Cu.evalInSandbox("(" + function () {
@ -47,7 +53,17 @@ function run_test() {
xrayed2 = XPCNativeWrapper(new XMLHttpRequest());
mixed = { xrayed: xrayed, xrayed2: xrayed2 };
tobecloned = { cloned: "cloned" };
imported(42,tobecloned, native, mixed);
invokedCallback = false;
callback = function() { invokedCallback = true; };
imported(42, tobecloned, native, mixed, callback);
do_check_true(invokedCallback);
try {
// Callbacks must be functions, not objects leading to functions.
imported(42, tobecloned, native, mixed, { cb: callback });
do_check_true(false);
} catch (e) {
do_check_true(/denied/.test(e) && /Function/.test(e));
}
}.toSource() + ")()", subsb);
// Invoking an exported function with cross-origin arguments should throw.
@ -114,6 +130,16 @@ function run_test() {
imported2(42, tobecloned, native, mixed);
}.toSource() + ")()", subsb);
// Make sure that functions may not be passed when allowCallbacks is not set.
try {
Cu.evalInSandbox("(" + function () {
imported2(42, tobecloned, native, mixed, callback);
}.toSource() + ")()", subsb);
do_check_true(false);
} catch (e) {
do_check_true(/denied/.test(e) && /Function/.test(e));
}
Cu.evalInSandbox("(" + function() {
checkIfCalled();
}.toSource() + ")()", epsb);

View File

@ -2013,33 +2013,37 @@ ContainerState::PopThebesLayerData()
ThebesLayerData* containingThebesLayerData =
mLayerBuilder->GetContainingThebesLayerData();
if (containingThebesLayerData) {
nsRect rect = nsLayoutUtils::TransformFrameRectToAncestor(
mContainerReferenceFrame,
data->mDispatchToContentHitRegion.GetBounds(),
containingThebesLayerData->mReferenceFrame);
containingThebesLayerData->mDispatchToContentHitRegion.Or(
containingThebesLayerData->mDispatchToContentHitRegion, rect);
rect = nsLayoutUtils::TransformFrameRectToAncestor(
mContainerReferenceFrame,
data->mMaybeHitRegion.GetBounds(),
containingThebesLayerData->mReferenceFrame);
containingThebesLayerData->mMaybeHitRegion.Or(
containingThebesLayerData->mMaybeHitRegion, rect);
// Our definitely-hit region must go to the maybe-hit-region since
// this function is an approximation.
gfx3DMatrix matrix = nsLayoutUtils::GetTransformToAncestor(
mContainerReferenceFrame, containingThebesLayerData->mReferenceFrame);
gfxMatrix matrix2D;
bool isPrecise = matrix.Is2D(&matrix2D) && !matrix2D.HasNonAxisAlignedTransform();
rect = nsLayoutUtils::TransformFrameRectToAncestor(
mContainerReferenceFrame,
data->mHitRegion.GetBounds(),
containingThebesLayerData->mReferenceFrame);
nsRegion* dest = isPrecise ? &containingThebesLayerData->mHitRegion
: &containingThebesLayerData->mMaybeHitRegion;
dest->Or(*dest, rect);
if (!data->mDispatchToContentHitRegion.GetBounds().IsEmpty()) {
nsRect rect = nsLayoutUtils::TransformFrameRectToAncestor(
mContainerReferenceFrame,
data->mDispatchToContentHitRegion.GetBounds(),
containingThebesLayerData->mReferenceFrame);
containingThebesLayerData->mDispatchToContentHitRegion.Or(
containingThebesLayerData->mDispatchToContentHitRegion, rect);
}
if (!data->mMaybeHitRegion.GetBounds().IsEmpty()) {
nsRect rect = nsLayoutUtils::TransformFrameRectToAncestor(
mContainerReferenceFrame,
data->mMaybeHitRegion.GetBounds(),
containingThebesLayerData->mReferenceFrame);
containingThebesLayerData->mMaybeHitRegion.Or(
containingThebesLayerData->mMaybeHitRegion, rect);
}
if (!data->mHitRegion.GetBounds().IsEmpty()) {
// Our definitely-hit region must go to the maybe-hit-region since
// this function is an approximation.
gfx3DMatrix matrix = nsLayoutUtils::GetTransformToAncestor(
mContainerReferenceFrame, containingThebesLayerData->mReferenceFrame);
gfxMatrix matrix2D;
bool isPrecise = matrix.Is2D(&matrix2D) && !matrix2D.HasNonAxisAlignedTransform();
nsRect rect = nsLayoutUtils::TransformFrameRectToAncestor(
mContainerReferenceFrame,
data->mHitRegion.GetBounds(),
containingThebesLayerData->mReferenceFrame);
nsRegion* dest = isPrecise ? &containingThebesLayerData->mHitRegion
: &containingThebesLayerData->mMaybeHitRegion;
dest->Or(*dest, rect);
}
} else {
EventRegions regions;
regions.mHitRegion = ScaleRegionToOutsidePixels(data->mHitRegion);

View File

@ -2619,7 +2619,13 @@ CalculateFrameMetricsForDisplayPort(nsIFrame* aScrollFrame,
nsIPresShell* presShell = presContext->PresShell();
CSSToLayoutDeviceScale deviceScale(float(nsPresContext::AppUnitsPerCSSPixel())
/ presContext->AppUnitsPerDevPixel());
ParentLayerToLayerScale resolution(presShell->GetResolution().width);
ParentLayerToLayerScale resolution;
if (aScrollFrame == presShell->GetRootScrollFrame()) {
// Only the root scrollable frame for a given presShell should pick up
// the presShell's resolution. All the other frames are 1.0.
resolution = ParentLayerToLayerScale(presShell->GetXResolution(),
presShell->GetYResolution());
}
LayoutDeviceToLayerScale cumulativeResolution(presShell->GetCumulativeResolution().width);
metrics.mDevPixelsPerCSSPixel = deviceScale;

View File

@ -3326,9 +3326,20 @@ StyleAnimationValue::ExtractComputedValue(nsCSSProperty aProperty,
return false;
};
return true;
case eStyleAnimType_Coord:
return StyleCoordToValue(*static_cast<const nsStyleCoord*>(
StyleDataAtOffset(styleStruct, ssOffset)), aComputedValue);
case eStyleAnimType_Coord: {
const nsStyleCoord& coord = *static_cast<const nsStyleCoord*>(
StyleDataAtOffset(styleStruct, ssOffset));
if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_NUMBERS_ARE_PIXELS) &&
coord.GetUnit() == eStyleUnit_Coord) {
// For SVG properties where number means the same thing as length,
// we want to animate them the same way. Normalize both to number
// since it has more accuracy (float vs nscoord).
aComputedValue.SetFloatValue(nsPresContext::
AppUnitsToFloatCSSPixels(coord.GetCoordValue()));
return true;
}
return StyleCoordToValue(coord, aComputedValue);
}
case eStyleAnimType_Sides_Top:
case eStyleAnimType_Sides_Right:
case eStyleAnimType_Sides_Bottom:

View File

@ -3817,7 +3817,8 @@ CSS_PROP_SVG(
stroke_dasharray,
StrokeDasharray,
CSS_PROPERTY_PARSE_FUNCTION |
CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
CSS_PROPERTY_NUMBERS_ARE_PIXELS,
// NOTE: Internal values have range restrictions.
"",
0,
@ -3828,7 +3829,8 @@ CSS_PROP_SVG(
stroke-dashoffset,
stroke_dashoffset,
StrokeDashoffset,
CSS_PROPERTY_PARSE_VALUE,
CSS_PROPERTY_PARSE_VALUE |
CSS_PROPERTY_NUMBERS_ARE_PIXELS,
"",
VARIANT_HLPN | VARIANT_OPENTYPE_SVG_KEYWORD,
kStrokeContextValueKTable,
@ -3880,7 +3882,8 @@ CSS_PROP_SVG(
stroke_width,
StrokeWidth,
CSS_PROPERTY_PARSE_VALUE |
CSS_PROPERTY_VALUE_NONNEGATIVE,
CSS_PROPERTY_VALUE_NONNEGATIVE |
CSS_PROPERTY_NUMBERS_ARE_PIXELS,
"",
VARIANT_HLPN | VARIANT_OPENTYPE_SVG_KEYWORD,
kStrokeContextValueKTable,

View File

@ -213,6 +213,9 @@ static_assert((CSS_PROPERTY_PARSE_PROPERTY_MASK &
// aliases.
#define CSS_PROPERTY_ALWAYS_ENABLED_IN_CHROME_OR_CERTIFIED_APP (1<<23)
// This property's unitless values are pixels.
#define CSS_PROPERTY_NUMBERS_ARE_PIXELS (1<<24)
/**
* Types of animatable values.
*/

View File

@ -218,8 +218,8 @@ var supported_properties = {
"stroke-dasharray": [ test_dasharray_transition ],
// NOTE: when calc() is supported on 'stroke-dashoffset', we should
// add test_length_percent_calc_transition.
"stroke-dashoffset": [ test_length_transition, test_percent_transition,
test_length_unclamped, test_percent_unclamped ],
"stroke-dashoffset": [ test_length_transition_svg, test_percent_transition,
test_length_unclamped_svg, test_percent_unclamped ],
"stroke-miterlimit": [ test_float_aboveOne_transition,
test_float_aboveOne_clamped ],
"stroke-opacity" : [ test_float_zeroToOne_transition,
@ -228,8 +228,8 @@ var supported_properties = {
test_float_zeroToOne_clamped ],
// NOTE: when calc() is supported on 'stroke-width', we should add
// test_length_percent_calc_transition.
"stroke-width": [ test_length_transition, test_percent_transition,
test_length_clamped, test_percent_clamped ],
"stroke-width": [ test_length_transition_svg, test_percent_transition,
test_length_clamped_svg, test_percent_clamped ],
"text-indent": [ test_length_transition, test_percent_transition,
test_length_percent_calc_transition,
test_length_unclamped, test_percent_unclamped ],
@ -872,27 +872,43 @@ function check_distance(prop, start, quarter, end)
ok(Math.abs((qe * 4 - se * 3) / se) < 0.0001, "property '" + prop + "': distance " + qe + " from quarter '" + quarter + "' to end '" + end + "' should be three quarters distance " + se + " from start '" + start + "' to end '" + end + "'");
}
function test_length_transition(prop) {
function test_length_transition_svg_or_units(prop, numbers_are_pixels) {
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "4px", "");
is(cs.getPropertyValue(prop), "4px",
"length-valued property " + prop + ": computed value before transition");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "12px", "");
is(cs.getPropertyValue(prop), "6px",
is(cs.getPropertyValue(prop), numbers_are_pixels ? "6" : "6px",
"length-valued property " + prop + ": interpolation of lengths");
check_distance(prop, "4px", "6px", "12px");
}
function test_length_transition(prop) {
test_length_transition_svg_or_units(prop, false);
}
function test_length_transition_svg(prop) {
test_length_transition_svg_or_units(prop, true);
}
function test_length_clamped(prop) {
test_length_clamped_or_unclamped(prop, true);
test_length_clamped_or_unclamped(prop, true, false);
}
function test_length_unclamped(prop) {
test_length_clamped_or_unclamped(prop, false);
test_length_clamped_or_unclamped(prop, false, false);
}
function test_length_clamped_or_unclamped(prop, is_clamped) {
function test_length_clamped_svg(prop) {
test_length_clamped_or_unclamped(prop, true, true);
}
function test_length_unclamped_svg(prop) {
test_length_clamped_or_unclamped(prop, false, true);
}
function test_length_clamped_or_unclamped(prop, is_clamped, numbers_are_pixels) {
div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
div.style.setProperty("transition-property", "none", "");
div.style.setProperty(prop, "0px", "");
@ -900,7 +916,8 @@ function test_length_clamped_or_unclamped(prop, is_clamped) {
"length-valued property " + prop + ": flush before clamping test");
div.style.setProperty("transition-property", prop, "");
div.style.setProperty(prop, "100px", "");
(is_clamped ? is : isnot)(cs.getPropertyValue(prop), "0px",
(is_clamped ? is : isnot)(cs.getPropertyValue(prop),
numbers_are_pixels ? "0" : "0px",
"length-valued property " + prop + ": clamping of negatives");
div.style.setProperty("transition-timing-function", "linear", "");
}

View File

@ -41,7 +41,10 @@ BinarySearch(const Container& aContainer, size_t aBegin, size_t aEnd,
size_t high = aEnd;
while (low != high) {
size_t middle = low + (high - low) / 2;
const T& middleValue = aContainer[middle];
// Allow any intermediate type so long as it provides a suitable ordering
// relation.
const auto& middleValue = aContainer[middle];
MOZ_ASSERT(aContainer[low] <= aContainer[middle]);
MOZ_ASSERT(aContainer[middle] <= aContainer[high - 1]);

View File

@ -3702,7 +3702,12 @@ pref("canvas.image.cache.limit", 0);
pref("image.onload.decode.limit", 0);
// WebGL prefs
#ifdef ANDROID
// Disable MSAA on mobile.
pref("gl.msaa-level", 0);
#else
pref("gl.msaa-level", 2);
#endif
pref("webgl.force-enabled", false);
pref("webgl.disabled", false);
pref("webgl.shader_validator", true);

View File

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -60,26 +60,39 @@ nsTemporaryFileInputStream::ReadSegments(nsWriteSegmentFun writer,
mozilla::MutexAutoLock lock(mFileDescOwner->FileMutex());
PR_Seek64(mFileDescOwner->mFD, mStartPos, PR_SEEK_SET);
// Limit requested count to the amount remaining in our section of the file.
count = std::min(count, uint32_t(mEndPos - mStartPos));
uint32_t remainBufCount = count;
char buf[4096];
while (remainBufCount > 0) {
uint32_t bufCount = std::min(remainBufCount, (uint32_t)sizeof(buf));
int32_t read_result = PR_Read(mFileDescOwner->mFD, buf, bufCount);
if (read_result < 0) {
while (*result < count) {
uint32_t bufCount = std::min(count - *result, (uint32_t) sizeof(buf));
int32_t bytesRead = PR_Read(mFileDescOwner->mFD, buf, bufCount);
if (bytesRead < 0) {
return NS_ErrorAccordingToNSPR();
}
uint32_t write_result = 0;
nsresult rv = writer(this, closure, buf,
count - remainBufCount, bufCount, &write_result);
remainBufCount -= bufCount;
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(write_result <= bufCount,
"writer should not write more than we asked it to write");
mStartPos += bufCount;
int32_t bytesWritten = 0;
while (bytesWritten < bytesRead) {
uint32_t writerCount = 0;
nsresult rv = writer(this, closure, buf + bytesWritten, *result,
bytesRead - bytesWritten, &writerCount);
if (NS_FAILED(rv) || writerCount == 0) {
// nsIInputStream::ReadSegments' contract specifies that errors
// from writer are not propagated to ReadSegments' caller.
//
// If writer fails, leaving bytes still in buf, that's okay: we
// only update mStartPos to reflect successful writes, so the call
// to PR_Seek64 at the top will restart us at the right spot.
return NS_OK;
}
NS_ASSERTION(writerCount <= (uint32_t) (bytesRead - bytesWritten),
"writer should not write more than we asked it to write");
bytesWritten += writerCount;
*result += writerCount;
mStartPos += writerCount;
}
}
*result = count;
return NS_OK;
}

View File

@ -123,10 +123,18 @@ static const char kGOOGLE_PIN_AlphaSSL_G2Fingerprint[] =
static const char kGOOGLE_PIN_CryptoCat1Fingerprint[] =
"vKaqtTLWmVuXPVJE+0OqN5sRc4VCcSQHI/W3XTDVR24=";
/* GOOGLE_PIN_EntrustRootEC1 */
static const char kGOOGLE_PIN_EntrustRootEC1Fingerprint[] =
"/qK31kX7pz11PB7Jp4cMQOH3sMVh6Se5hb9xGGbjbyI=";
/* GOOGLE_PIN_Entrust_G2 */
static const char kGOOGLE_PIN_Entrust_G2Fingerprint[] =
"du6FkDdMcVQ3u8prumAo6t3i3G27uMP2EOhR8R0at/U=";
/* GOOGLE_PIN_GoDaddySecure */
static const char kGOOGLE_PIN_GoDaddySecureFingerprint[] =
"MrZLZnJ6IGPkBm87lYywqu5Xal7O/ZUzmbuIdHMdlYc=";
/* GOOGLE_PIN_Libertylavabitcom */
static const char kGOOGLE_PIN_LibertylavabitcomFingerprint[] =
"WnKzsDXgqPtS1KvtImrhQPqcxfpmfssuI2cSJt4LMks=";
@ -658,6 +666,33 @@ static const StaticPinset kPinset_lavabit = {
&kPinset_lavabit_sha256
};
static const char* kPinset_dropbox_sha256_Data[] = {
kGOOGLE_PIN_EntrustRootEC1Fingerprint,
kThawte_Premium_Server_CAFingerprint,
kthawte_Primary_Root_CA___G3Fingerprint,
kthawte_Primary_Root_CAFingerprint,
kEntrust_net_Premium_2048_Secure_Server_CAFingerprint,
kDigiCert_Assured_ID_Root_CAFingerprint,
kGo_Daddy_Root_Certificate_Authority___G2Fingerprint,
kGOOGLE_PIN_GoDaddySecureFingerprint,
kGeoTrust_Primary_Certification_AuthorityFingerprint,
kGo_Daddy_Class_2_CAFingerprint,
kDigiCert_High_Assurance_EV_Root_CAFingerprint,
kthawte_Primary_Root_CA___G2Fingerprint,
kEntrust_Root_Certification_AuthorityFingerprint,
kGOOGLE_PIN_Entrust_G2Fingerprint,
kGeoTrust_Global_CAFingerprint,
kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
kDigiCert_Global_Root_CAFingerprint,
kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
};
static const StaticFingerprints kPinset_dropbox_sha256 = { 18, kPinset_dropbox_sha256_Data };
static const StaticPinset kPinset_dropbox = {
nullptr,
&kPinset_dropbox_sha256
};
/* Domainlist */
struct TransportSecurityPreload {
const char* mHost;
@ -676,6 +711,7 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
{ "addons.mozilla.org", true, false, true, 1, &kPinset_mozilla },
{ "admin.google.com", true, false, false, -1, &kPinset_google_root_pems },
{ "android.com", true, false, false, -1, &kPinset_google_root_pems },
{ "api.accounts.firefox.com", true, true, false, 5, &kPinset_mozilla_fxa },
{ "api.twitter.com", true, false, false, -1, &kPinset_twitterCDN },
{ "apis.google.com", true, false, false, -1, &kPinset_google_root_pems },
{ "appengine.google.com", true, false, false, -1, &kPinset_google_root_pems },
@ -702,6 +738,7 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
{ "docs.google.com", true, false, false, -1, &kPinset_google_root_pems },
{ "doubleclick.net", true, false, false, -1, &kPinset_google_root_pems },
{ "drive.google.com", true, false, false, -1, &kPinset_google_root_pems },
{ "dropbox.com", false, true, false, -1, &kPinset_dropbox },
{ "encrypted.google.com", true, false, false, -1, &kPinset_google_root_pems },
{ "exclude-subdomains.pinning.example.com", false, false, false, 0, &kPinset_mozilla_test },
{ "g.co", true, false, false, -1, &kPinset_google_root_pems },

View File

@ -190,6 +190,8 @@
"pins": "mozilla", "test_mode": true, "id": 3 },
{ "name": "accounts.firefox.com", "include_subdomains": true,
"pins": "mozilla_fxa", "test_mode": true, "id": 4 },
{ "name": "api.accounts.firefox.com", "include_subdomains": true,
"pins": "mozilla_fxa", "test_mode": true, "id": 5 },
{ "name": "cdn.mozilla.net", "include_subdomains": true,
"pins": "mozilla", "test_mode": false },
{ "name": "cdn.mozilla.org", "include_subdomains": true,

View File

@ -2,7 +2,7 @@ import os
from setuptools import setup, find_packages
import sys
version = '0.7.10'
version = '0.7.11'
# dependencies
with open('requirements.txt') as f:

View File

@ -617,10 +617,10 @@ private:
continue;
}
scanned = fscanf(sizeFile, "%" SCNu64, &size);
fclose(sizeFile);
if (NS_WARN_IF(scanned != 1)) {
continue;
}
fclose(sizeFile);
// Read mapped regions; format described below.
uint64_t freeSize = size;