Merge mozilla-central and inbound

This commit is contained in:
Ed Morley 2013-09-06 16:23:50 +01:00
commit 93ac26880b
169 changed files with 5962 additions and 365 deletions

View File

@ -1,88 +1,40 @@
af
ak
ar
as
ast
be
bg
bn-BD
bn-IN
br
bs
ca
cs
cy
da
de
el
en-GB
en-ZA
eo
es-AR
es-CL
es-ES
es-MX
et
eu
fa
fi
fr
fy-NL
ga-IE
gd
gl
gu-IN
he
hi-IN
hr
hu
hy-AM
id
is
it
ja
ja-JP-mac
ka
kk
km
kn
ko
ku
lg
lt
lv
mai
mk
ml
mn
mr
nb-NO
nl
nn-NO
nso
oc
or
pa-IN
pl
pt-BR
pt-PT
rm
ro
ru
si
sk
sl
son
sq
sr
sv-SE
ta
ta-LK
te
th
tr
uk
vi
zh-CN
zh-TW
zu

View File

@ -6483,7 +6483,8 @@ fi
MOZ_ARG_ENABLE_BOOL(content-sandbox,
[ --enable-content-sandbox Enable sandboxing support for content-processes],
MOZ_CONTENT_SANDBOX=1)
MOZ_CONTENT_SANDBOX=1,
MOZ_CONTENT_SANDBOX=)
if test -n "$MOZ_CONTENT_SANDBOX"; then
AC_DEFINE(MOZ_CONTENT_SANDBOX)
@ -6493,7 +6494,8 @@ AC_SUBST(MOZ_CONTENT_SANDBOX)
MOZ_ARG_ENABLE_BOOL(content-sandbox-reporter,
[ --enable-content-sandbox-reporter Enable syscall reporter to troubleshoot syscalls denied by the content-processes sandbox],
MOZ_CONTENT_SANDBOX_REPORTER=1)
MOZ_CONTENT_SANDBOX_REPORTER=1,
MOZ_CONTENT_SANDBOX_REPORTER=)
if test -n "$MOZ_CONTENT_SANDBOX_REPORTER"; then
AC_DEFINE(MOZ_CONTENT_SANDBOX_REPORTER)

View File

@ -26,6 +26,8 @@
#include "nsPrintfCString.h"
#include "prprf.h"
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/DOMErrorBinding.h"
#include "mozilla/dom/HTMLObjectElement.h"
#include "mozilla/dom/HTMLObjectElementBinding.h"
#include "mozilla/dom/HTMLSharedObjectElement.h"
@ -185,6 +187,41 @@ ErrorResult::ReportJSException(JSContext* cx)
JS_RemoveValueRoot(cx, &mJSException);
}
void
ErrorResult::ReportJSExceptionFromJSImplementation(JSContext* aCx)
{
MOZ_ASSERT(!mMightHaveUnreportedJSException,
"Why didn't you tell us you planned to handle JS exceptions?");
dom::DOMError* domError;
nsresult rv = UNWRAP_OBJECT(DOMError, aCx, &mJSException.toObject(),
domError);
if (NS_FAILED(rv)) {
// Unwrapping really shouldn't fail here, if mExceptionHandling is set to
// eRethrowContentExceptions then the CallSetup destructor only stores an
// exception if it unwraps to DOMError. If we reach this then either
// mExceptionHandling wasn't set to eRethrowContentExceptions and we
// shouldn't be calling ReportJSExceptionFromJSImplementation or something
// went really wrong.
NS_RUNTIMEABORT("We stored a non-DOMError exception!");
}
nsString message;
domError->GetMessage(message);
JSErrorReport errorReport;
memset(&errorReport, 0, sizeof(JSErrorReport));
errorReport.errorNumber = JSMSG_USER_DEFINED_ERROR;
errorReport.ucmessage = message.get();
errorReport.exnType = JSEXN_ERR;
JS_ThrowReportedError(aCx, nullptr, &errorReport);
JS_RemoveValueRoot(aCx, &mJSException);
// We no longer have a useful exception but we do want to signal that an error
// occured.
mResult = NS_ERROR_FAILURE;
}
void
ErrorResult::StealJSException(JSContext* cx,
JS::MutableHandle<JS::Value> value)

View File

@ -89,14 +89,19 @@ template<bool mainThread>
inline bool
ThrowMethodFailedWithDetails(JSContext* cx, ErrorResult& rv,
const char* ifaceName,
const char* memberName)
const char* memberName,
bool reportJSContentExceptions = false)
{
if (rv.IsTypeError()) {
rv.ReportTypeError(cx);
return false;
}
if (rv.IsJSException()) {
rv.ReportJSException(cx);
if (reportJSContentExceptions) {
rv.ReportJSExceptionFromJSImplementation(cx);
} else {
rv.ReportJSException(cx);
}
return false;
}
if (rv.IsNotEnoughArgsError()) {
@ -183,8 +188,8 @@ IsDOMObject(JSObject* obj)
}
#define UNWRAP_OBJECT(Interface, cx, obj, value) \
UnwrapObject<prototypes::id::Interface, \
mozilla::dom::Interface##Binding::NativeType>(cx, obj, value)
mozilla::dom::UnwrapObject<mozilla::dom::prototypes::id::Interface, \
mozilla::dom::Interface##Binding::NativeType>(cx, obj, value)
// Some callers don't want to set an exception when unwrapping fails
// (for example, overload resolution uses unwrapping to tell what sort

View File

@ -5,6 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/CallbackObject.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/DOMErrorBinding.h"
#include "jsfriendapi.h"
#include "nsIScriptGlobalObject.h"
#include "nsIXPConnect.h"
@ -40,8 +43,10 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
ErrorResult& aRv,
ExceptionHandling aExceptionHandling)
ExceptionHandling aExceptionHandling,
JSCompartment* aCompartment)
: mCx(nullptr)
, mCompartment(aCompartment)
, mErrorResult(aRv)
, mExceptionHandling(aExceptionHandling)
{
@ -123,25 +128,55 @@ CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
mCx = cx;
// Make sure the JS engine doesn't report exceptions we want to re-throw
if (mExceptionHandling == eRethrowExceptions) {
if (mExceptionHandling == eRethrowContentExceptions ||
mExceptionHandling == eRethrowExceptions) {
mSavedJSContextOptions = JS_GetOptions(cx);
JS_SetOptions(cx, mSavedJSContextOptions | JSOPTION_DONT_REPORT_UNCAUGHT);
}
}
bool
CallbackObject::CallSetup::ShouldRethrowException(JS::Handle<JS::Value> aException)
{
if (mExceptionHandling == eRethrowExceptions) {
return true;
}
MOZ_ASSERT(mExceptionHandling == eRethrowContentExceptions);
// For eRethrowContentExceptions we only want to throw an exception if the
// object that was thrown is a DOMError object in the caller compartment
// (which we stored in mCompartment).
if (!aException.isObject()) {
return false;
}
JS::Rooted<JSObject*> obj(mCx, &aException.toObject());
obj = js::UncheckedUnwrap(obj, /* stopAtOuter = */ false);
if (js::GetObjectCompartment(obj) != mCompartment) {
return false;
}
DOMError* domError;
return NS_SUCCEEDED(UNWRAP_OBJECT(DOMError, mCx, obj, domError));
}
CallbackObject::CallSetup::~CallSetup()
{
// First things first: if we have a JSContext, report any pending
// errors on it, unless we were told to re-throw them.
if (mCx) {
bool dealtWithPendingException = false;
if (mExceptionHandling == eRethrowExceptions) {
if (mExceptionHandling == eRethrowContentExceptions ||
mExceptionHandling == eRethrowExceptions) {
// Restore the old context options
JS_SetOptions(mCx, mSavedJSContextOptions);
mErrorResult.MightThrowJSException();
if (JS_IsExceptionPending(mCx)) {
JS::Rooted<JS::Value> exn(mCx);
if (JS_GetPendingException(mCx, exn.address())) {
if (JS_GetPendingException(mCx, exn.address()) &&
ShouldRethrowException(exn)) {
mErrorResult.ThrowJSException(mCx, exn);
JS_ClearPendingException(mCx);
dealtWithPendingException = true;

View File

@ -78,7 +78,13 @@ public:
}
enum ExceptionHandling {
// Report any exception and don't throw it to the caller code.
eReportExceptions,
// Throw an exception to the caller code if the thrown exception is a
// binding object for a DOMError from the caller's scope, otherwise report
// it.
eRethrowContentExceptions,
// Throw any exception to the caller code.
eRethrowExceptions
};
@ -118,8 +124,11 @@ protected:
* non-null.
*/
public:
// If aExceptionHandling == eRethrowContentExceptions then aCompartment
// needs to be set to the caller's compartment.
CallSetup(JS::Handle<JSObject*> aCallable, ErrorResult& aRv,
ExceptionHandling aExceptionHandling);
ExceptionHandling aExceptionHandling,
JSCompartment* aCompartment = nullptr);
~CallSetup();
JSContext* GetContext() const
@ -131,9 +140,15 @@ protected:
// We better not get copy-constructed
CallSetup(const CallSetup&) MOZ_DELETE;
bool ShouldRethrowException(JS::Handle<JS::Value> aException);
// Members which can go away whenever
JSContext* mCx;
// Caller's compartment. This will only have a sensible value if
// mExceptionHandling == eRethrowContentExceptions.
JSCompartment* mCompartment;
// And now members whose construction/destruction order we need to control.
// Put our nsAutoMicrotask first, so it gets destroyed after everything else

View File

@ -4807,15 +4807,26 @@ if (global.Failed()) {
if needsCx and not (static and descriptor.workers):
argsPre.append("cx")
needsUnwrap = isConstructor
if needScopeObject(returnType, arguments, self.extendedAttributes,
descriptor, descriptor.wrapperCache,
not descriptor.interface.isJSImplemented()):
needsUnwrap = False
if isConstructor:
needsUnwrap = True
needsUnwrappedVar = False
unwrappedVar = "obj"
elif descriptor.interface.isJSImplemented():
needsUnwrap = True
needsUnwrappedVar = True
argsPre.append("js::GetObjectCompartment(unwrappedObj.empty() ? obj : unwrappedObj.ref())")
elif needScopeObject(returnType, arguments, self.extendedAttributes,
descriptor, descriptor.wrapperCache, True):
needsUnwrap = True
needsUnwrappedVar = True
argsPre.append("unwrappedObj.empty() ? obj : unwrappedObj.ref()")
if needsUnwrap and needsUnwrappedVar:
# We cannot assign into obj because it's a Handle, not a
# MutableHandle, so we need a separate Rooted.
cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;"))
argsPre.append("unwrappedObj.empty() ? obj : unwrappedObj.ref()")
needsUnwrap = True
unwrappedVar = "unwrappedObj.ref()"
if idlNode.isMethod() and idlNode.isLegacycaller():
# If we can have legacycaller with identifier, we can't
@ -4844,7 +4855,7 @@ if (global.Failed()) {
if needsUnwrap:
# Something depends on having the unwrapped object, so unwrap it now.
xraySteps = []
if not isConstructor:
if needsUnwrappedVar:
xraySteps.append(
CGGeneric("unwrappedObj.construct(cx, obj);"))
@ -4854,7 +4865,7 @@ if (global.Failed()) {
CGGeneric(string.Template("""${obj} = js::CheckedUnwrap(${obj});
if (!${obj}) {
return false;
}""").substitute({ 'obj' : 'obj' if isConstructor else 'unwrappedObj.ref()' })))
}""").substitute({ 'obj' : unwrappedVar })))
if isConstructor:
# If we're called via an xray, we need to enter the underlying
# object's compartment and then wrap up all of our arguments into
@ -4915,10 +4926,14 @@ if (!${obj}) {
self.idlNode.identifier.name))
def getErrorReport(self):
return CGGeneric('return ThrowMethodFailedWithDetails<%s>(cx, rv, "%s", "%s");'
jsImplemented = ""
if self.descriptor.interface.isJSImplemented():
jsImplemented = ", true"
return CGGeneric('return ThrowMethodFailedWithDetails<%s>(cx, rv, "%s", "%s"%s);'
% (toStringBool(not self.descriptor.workers),
self.descriptor.interface.identifier.name,
self.idlNode.identifier.name))
self.idlNode.identifier.name,
jsImplemented))
def define(self):
return (self.cgRoot.define() + "\n" + self.wrap_return_value())
@ -9509,9 +9524,38 @@ class CGExampleRoot(CGThing):
def jsImplName(name):
return name + "JSImpl"
class CGJSImplMethod(CGNativeMember):
class CGJSImplMember(CGNativeMember):
"""
Base class for generating code for the members of the implementation class
for a JS-implemented WebIDL interface.
"""
def __init__(self, descriptorProvider, member, name, signature,
extendedAttrs, breakAfter=True, passJSBitsAsNeeded=True,
visibility="public", jsObjectsArePtr=False,
variadicIsSequence=False):
CGNativeMember.__init__(self, descriptorProvider, member, name,
signature, extendedAttrs, breakAfter=breakAfter,
passJSBitsAsNeeded=passJSBitsAsNeeded,
visibility=visibility,
jsObjectsArePtr=jsObjectsArePtr,
variadicIsSequence=variadicIsSequence)
self.body = self.getImpl()
def getArgs(self, returnType, argList):
args = CGNativeMember.getArgs(self, returnType, argList)
args.insert(0, Argument("JSCompartment*", "aCompartment"))
return args
class CGJSImplMethod(CGJSImplMember):
"""
Class for generating code for the methods for a JS-implemented WebIDL
interface.
"""
def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
CGNativeMember.__init__(self, descriptor, method,
self.signature = signature
self.descriptor = descriptor
self.isConstructor = isConstructor
CGJSImplMember.__init__(self, descriptor, method,
CGSpecializedMethod.makeNativeName(descriptor,
method),
signature,
@ -9519,29 +9563,29 @@ class CGJSImplMethod(CGNativeMember):
breakAfter=breakAfter,
variadicIsSequence=True,
passJSBitsAsNeeded=False)
self.signature = signature
self.descriptor = descriptor
if isConstructor:
self.body = self.getConstructorImpl()
else:
self.body = self.getImpl()
def getArgs(self, returnType, argList):
if self.isConstructor:
# Skip the JSCompartment bits for constructors; it's handled
# manually in getImpl.
return CGNativeMember.getArgs(self, returnType, argList)
return CGJSImplMember.getArgs(self, returnType, argList)
def getImpl(self):
callbackArgs = [arg.name for arg in self.getArgs(self.signature[0], self.signature[1])]
return 'return mImpl->%s(%s);' % (self.name, ", ".join(callbackArgs))
args = self.getArgs(self.signature[0], self.signature[1])
if not self.isConstructor:
return 'return mImpl->%s(%s);' % (self.name, ", ".join(arg.name for arg in args))
def getConstructorImpl(self):
assert self.descriptor.interface.isJSImplemented()
if self.name != 'Constructor':
raise TypeError("Named constructors are not supported for JS implemented WebIDL. See bug 851287.")
if len(self.signature[1]) != 0:
args = self.getArgs(self.signature[0], self.signature[1])
# The first two arguments to the constructor implementation are not
# arguments to the WebIDL constructor, so don't pass them to __Init()
assert args[0].argType == 'const GlobalObject&'
assert args[1].argType == 'JSContext*'
args = args[2:]
constructorArgs = [arg.name for arg in args]
constructorArgs = [arg.name for arg in args[2:]]
constructorArgs.insert(0, "js::GetObjectCompartment(scopeObj)")
initCall = """
// Wrap the object before calling __Init so that __DOM_IMPL__ is available.
nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(window);
@ -9588,24 +9632,31 @@ def callbackGetterName(attr):
def callbackSetterName(attr):
return "Set" + MakeNativeName(attr.identifier.name)
class CGJSImplGetter(CGNativeMember):
class CGJSImplGetter(CGJSImplMember):
"""
Class for generating code for the getters of attributes for a JS-implemented
WebIDL interface.
"""
def __init__(self, descriptor, attr):
CGNativeMember.__init__(self, descriptor, attr,
CGJSImplMember.__init__(self, descriptor, attr,
CGSpecializedGetter.makeNativeName(descriptor,
attr),
(attr.type, []),
descriptor.getExtendedAttributes(attr,
getter=True),
passJSBitsAsNeeded=False)
self.body = self.getImpl()
def getImpl(self):
callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])]
return 'return mImpl->%s(%s);' % (callbackGetterName(self.member), ", ".join(callbackArgs))
class CGJSImplSetter(CGNativeMember):
class CGJSImplSetter(CGJSImplMember):
"""
Class for generating code for the setters of attributes for a JS-implemented
WebIDL interface.
"""
def __init__(self, descriptor, attr):
CGNativeMember.__init__(self, descriptor, attr,
CGJSImplMember.__init__(self, descriptor, attr,
CGSpecializedSetter.makeNativeName(descriptor,
attr),
(BuiltinTypes[IDLBuiltinType.Types.void],
@ -9613,7 +9664,6 @@ class CGJSImplSetter(CGNativeMember):
descriptor.getExtendedAttributes(attr,
setter=True),
passJSBitsAsNeeded=False)
self.body = self.getImpl()
def getImpl(self):
callbackArgs = [arg.name for arg in self.getArgs(BuiltinTypes[IDLBuiltinType.Types.void],
@ -9929,11 +9979,13 @@ class FakeMember():
return None
class CallbackMember(CGNativeMember):
def __init__(self, sig, name, descriptorProvider, needThisHandling):
def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False):
"""
needThisHandling is True if we need to be able to accept a specified
thisObj, False otherwise.
"""
assert not rethrowContentException or not needThisHandling
self.retvalType = sig[0]
self.originalSig = sig
args = sig[1]
@ -9951,6 +10003,7 @@ class CallbackMember(CGNativeMember):
# If needThisHandling, we generate ourselves as private and the caller
# will handle generating public versions that handle the "this" stuff.
visibility = "private" if needThisHandling else "public"
self.rethrowContentException = rethrowContentException
# We don't care, for callback codegen, whether our original member was
# a method or attribute or whatnot. Just always pass FakeMember()
# here.
@ -10109,8 +10162,12 @@ class CallbackMember(CGNativeMember):
if not self.needThisHandling:
# Since we don't need this handling, we're the actual method that
# will be called, so we need an aRethrowExceptions argument.
return args + [Argument("ExceptionHandling", "aExceptionHandling",
"eReportExceptions")]
if self.rethrowContentException:
args.insert(0, Argument("JSCompartment*", "aCompartment"))
else:
args.append(Argument("ExceptionHandling", "aExceptionHandling",
"eReportExceptions"))
return args
# We want to allow the caller to pass in a "this" object, as
# well as a JSContext.
return [Argument("JSContext*", "cx"),
@ -10120,13 +10177,22 @@ class CallbackMember(CGNativeMember):
if self.needThisHandling:
# It's been done for us already
return ""
callSetup = "CallSetup s(CallbackPreserveColor(), aRv"
if self.rethrowContentException:
# getArgs doesn't add the aExceptionHandling argument but does add
# aCompartment for us.
callSetup += ", eRethrowContentExceptions, aCompartment"
else:
callSetup += ", aExceptionHandling"
callSetup += ");"
return string.Template(
"CallSetup s(CallbackPreserveColor(), aRv, aExceptionHandling);\n"
"${callSetup}\n"
"JSContext* cx = s.GetContext();\n"
"if (!cx) {\n"
" aRv.Throw(NS_ERROR_UNEXPECTED);\n"
" return${errorReturn};\n"
"}\n").substitute({
"callSetup": callSetup,
"errorReturn" : self.getDefaultRetval(),
})
@ -10149,9 +10215,9 @@ class CallbackMember(CGNativeMember):
idlObject.location))
class CallbackMethod(CallbackMember):
def __init__(self, sig, name, descriptorProvider, needThisHandling):
def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False):
CallbackMember.__init__(self, sig, name, descriptorProvider,
needThisHandling)
needThisHandling, rethrowContentException)
def getRvalDecl(self):
return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
@ -10189,10 +10255,10 @@ class CallbackOperationBase(CallbackMethod):
"""
Common class for implementing various callback operations.
"""
def __init__(self, signature, jsName, nativeName, descriptor, singleOperation):
def __init__(self, signature, jsName, nativeName, descriptor, singleOperation, rethrowContentException=False):
self.singleOperation = singleOperation
self.methodName = jsName
CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation)
CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation, rethrowContentException)
def getThisObj(self):
if not self.singleOperation:
@ -10232,7 +10298,8 @@ class CallbackOperation(CallbackOperationBase):
jsName = method.identifier.name
CallbackOperationBase.__init__(self, signature,
jsName, MakeNativeName(jsName),
descriptor, descriptor.interface.isSingleOperationInterface())
descriptor, descriptor.interface.isSingleOperationInterface(),
rethrowContentException=descriptor.interface.isJSImplemented())
class CallbackGetter(CallbackMember):
def __init__(self, attr, descriptor):
@ -10242,7 +10309,8 @@ class CallbackGetter(CallbackMember):
(attr.type, []),
callbackGetterName(attr),
descriptor,
needThisHandling=False)
needThisHandling=False,
rethrowContentException=descriptor.interface.isJSImplemented())
def getRvalDecl(self):
return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
@ -10267,7 +10335,8 @@ class CallbackSetter(CallbackMember):
[FakeArgument(attr.type, attr)]),
callbackSetterName(attr),
descriptor,
needThisHandling=False)
needThisHandling=False,
rethrowContentException=descriptor.interface.isJSImplemented())
def getRvalDecl(self):
# We don't need an rval
@ -10296,7 +10365,7 @@ class CGJSImplInitOperation(CallbackOperationBase):
def __init__(self, sig, descriptor):
assert sig in descriptor.interface.ctor().signatures()
CallbackOperationBase.__init__(self, (BuiltinTypes[IDLBuiltinType.Types.void], sig[1]),
"__init", "__Init", descriptor, False)
"__init", "__Init", descriptor, False, True)
class GlobalGenRoots():
"""

View File

@ -70,11 +70,14 @@ public:
// Facilities for throwing a preexisting JS exception value via this
// ErrorResult. The contract is that any code which might end up calling
// ThrowJSException() must call MightThrowJSException() even if no exception
// is being thrown. Code that would call ReportJSException or
// is being thrown. Code that would call ReportJSException* or
// StealJSException as needed must first call WouldReportJSException even if
// this ErrorResult has not failed.
void ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn);
void ReportJSException(JSContext* cx);
// Used to implement throwing exceptions from the JS implementation of
// bindings to callers of the binding.
void ReportJSExceptionFromJSImplementation(JSContext* aCx);
bool IsJSException() const { return ErrorCode() == NS_ERROR_DOM_JS_EXCEPTION; }
void ThrowNotEnoughArgsError() { mResult = NS_ERROR_XPC_NOT_ENOUGH_ARGS; }

View File

@ -359,30 +359,28 @@ RTCPeerConnection.prototype = {
* ErrorMsg is passed in to detail which array-entry failed, if any.
*/
_mustValidateRTCConfiguration: function(rtcConfig, errorMsg) {
var errorCtor = this._win.DOMError;
function nicerNewURI(uriStr, errorMsg) {
let ios = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
try {
return ios.newURI(uriStr, null, null);
} catch (e if (e.result == Cr.NS_ERROR_MALFORMED_URI)) {
throw new Components.Exception(errorMsg + " - malformed URI: " + uriStr,
Cr.NS_ERROR_MALFORMED_URI);
throw new errorCtor("", errorMsg + " - malformed URI: " + uriStr);
}
}
function mustValidateServer(server) {
let url = nicerNewURI(server.url, errorMsg);
if (url.scheme in { turn:1, turns:1 }) {
if (!server.username) {
throw new Components.Exception(errorMsg + " - missing username: " +
server.url, Cr.NS_ERROR_MALFORMED_URI);
throw new errorCtor("", errorMsg + " - missing username: " + server.url);
}
if (!server.credential) {
throw new Components.Exception(errorMsg + " - missing credential: " +
server.url, Cr.NS_ERROR_MALFORMED_URI);
throw new errorCtor("", errorMsg + " - missing credential: " +
server.url);
}
}
else if (!(url.scheme in { stun:1, stuns:1 })) {
throw new Components.Exception(errorMsg + " - improper scheme: " + url.scheme,
Cr.NS_ERROR_MALFORMED_URI);
throw new errorCtor("", errorMsg + " - improper scheme: " + url.scheme);
}
}
if (rtcConfig.iceServers) {

View File

@ -1127,9 +1127,16 @@ function otherWindowFocused(otherWindow)
otherWindow.close();
// next, check modal dialogs
getById("n2").focus();
var nextWindow = window.openDialog("focus_window2.xul", "_blank", "chrome,modal", modalWindowOpened);
// next, check modal dialogs
// XXXndeakin Bug 621399 - modal dialog test sometimes fails on Windows 8 so disable it.
if (navigator.userAgent.indexOf("Windows NT 6.2") >= 0) {
window.open("focus_frameset.html", "_blank", "width=400,height=400,toolbar=no");
}
else {
window.openDialog("focus_window2.xul", "_blank", "chrome,modal", modalWindowOpened);
}
}
function modalWindowOpened(modalWindow)

View File

@ -65,7 +65,7 @@ rdtsc(void)
}
#endif
const char* const TraceLogging::type_name[] = {
const char* const TraceLogging::typeName[] = {
"1,s", // start script
"0,s", // stop script
"1,c", // start ion compilation
@ -86,29 +86,31 @@ const char* const TraceLogging::type_name[] = {
"e,b", // engine baseline
"e,o" // engine ionmonkey
};
TraceLogging* TraceLogging::_defaultLogger = NULL;
TraceLogging* TraceLogging::loggers[] = {NULL, NULL};
bool TraceLogging::atexitSet = false;
uint64_t TraceLogging::startupTime = 0;
TraceLogging::TraceLogging()
: startupTime(rdtsc()),
loggingTime(0),
TraceLogging::TraceLogging(Logger id)
: loggingTime(0),
nextTextId(1),
entries(NULL),
curEntry(0),
numEntries(1000000),
fileno(0),
out(NULL)
out(NULL),
id(id)
{
textMap.init();
}
TraceLogging::~TraceLogging()
{
if (out != NULL) {
if (out) {
fclose(out);
out = NULL;
}
if (entries != NULL) {
if (entries) {
flush();
free(entries);
entries = NULL;
@ -122,7 +124,7 @@ TraceLogging::grow()
// Allocating a bigger array failed.
// Keep using the current storage, but remove all entries by flushing them.
if (nentries == NULL) {
if (!nentries) {
flush();
return;
}
@ -195,8 +197,19 @@ void
TraceLogging::flush()
{
// Open the logging file, when not opened yet.
if (out == NULL)
out = fopen(TRACE_LOG_DIR "tracelogging.log", "w");
if (!out) {
switch(id) {
case DEFAULT:
out = fopen(TRACE_LOG_DIR "tracelogging.log", "w");
break;
case ION_BACKGROUND_COMPILER:
out = fopen(TRACE_LOG_DIR "tracelogging-compile.log", "w");
break;
default:
MOZ_ASSUME_UNREACHABLE("Bad trigger");
return;
}
}
// Print all log entries into the file
for (unsigned int i = 0; i < curEntry; i++) {
@ -209,36 +222,29 @@ TraceLogging::flush()
if (entry.text()) {
written = fprintf(out, "%llu,%s,%s,%d\n",
(unsigned long long)entry.tick(),
type_name[entry.type()],
typeName[entry.type()],
entry.text(),
entry.lineno());
} else {
written = fprintf(out, "%llu,%s,%d,%d\n",
(unsigned long long)entry.tick(),
type_name[entry.type()],
typeName[entry.type()],
entry.textId(),
entry.lineno());
}
} else {
written = fprintf(out, "%llu,%s\n",
(unsigned long long)entry.tick(),
type_name[entry.type()]);
typeName[entry.type()]);
}
}
// A logging file can only be 2GB of length (fwrite limit).
// When we exceed this limit, the writing will fail.
// In that case try creating a extra file to write the log entries.
if (written < 0) {
fprintf(stderr, "Writing tracelog to disk failed,");
fprintf(stderr, "probably because the file would've exceeded the maximum size of 2GB");
fclose(out);
if (fileno >= 9999)
exit(-1);
char filename[21 + sizeof(TRACE_LOG_DIR)];
sprintf (filename, TRACE_LOG_DIR "tracelogging-%d.log", ++fileno);
out = fopen(filename, "w");
i--; // Try to print message again
continue;
exit(-1);
}
if (entries[i].text() != NULL) {
@ -250,21 +256,29 @@ TraceLogging::flush()
}
TraceLogging*
TraceLogging::defaultLogger()
TraceLogging::getLogger(Logger id)
{
if (_defaultLogger == NULL) {
_defaultLogger = new TraceLogging();
atexit (releaseDefaultLogger);
if (!loggers[id]) {
loggers[id] = new TraceLogging(id);
if (!atexitSet) {
startupTime = rdtsc();
atexit (releaseLoggers);
atexitSet = true;
}
}
return _defaultLogger;
return loggers[id];
}
void
TraceLogging::releaseDefaultLogger()
TraceLogging::releaseLoggers()
{
if (_defaultLogger != NULL) {
delete _defaultLogger;
_defaultLogger = NULL;
for (size_t i = 0; i < LAST_LOGGER; i++) {
if (!loggers[i])
continue;
delete loggers[i];
loggers[i] = NULL;
}
}

View File

@ -46,6 +46,12 @@ class TraceLogging
INFO_ENGINE_IONMONKEY,
INFO
};
enum Logger {
DEFAULT,
ION_BACKGROUND_COMPILER,
LAST_LOGGER
};
private:
struct Entry {
@ -74,7 +80,6 @@ class TraceLogging
PointerHasher<const char *, 3>,
SystemAllocPolicy> TextHashMap;
uint64_t startupTime;
uint64_t loggingTime;
TextHashMap textMap;
uint32_t nextTextId;
@ -83,11 +88,14 @@ class TraceLogging
unsigned int numEntries;
int fileno;
FILE *out;
Logger id;
static const char * const type_name[];
static TraceLogging* _defaultLogger;
static bool atexitSet;
static const char * const typeName[];
static TraceLogging* loggers[];
static uint64_t startupTime;
public:
TraceLogging();
TraceLogging(Logger id);
~TraceLogging();
void log(Type type, const char* text = NULL, unsigned int number = 0);
@ -96,8 +104,11 @@ class TraceLogging
void log(const char* log);
void flush();
static TraceLogging* defaultLogger();
static void releaseDefaultLogger();
static TraceLogging* getLogger(Logger id);
static TraceLogging* defaultLogger() {
return getLogger(DEFAULT);
}
static void releaseLoggers();
private:
void grow();

View File

@ -1496,6 +1496,24 @@ public:
m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset);
}
void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
spew("movw %s, %d(%s,%s,%d)",
nameIReg(2, src), offset, nameIReg(base), nameIReg(index), 1<<scale);
m_formatter.prefix(PRE_OPERAND_SIZE);
m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
}
#if !WTF_CPU_X86_64
void movw_rm(RegisterID src, const void* addr)
{
spew("movw %s, %p",
nameIReg(2, src), addr);
m_formatter.prefix(PRE_OPERAND_SIZE);
m_formatter.oneByteOp(OP_MOV_EvGv, src, addr);
}
#endif
void movl_rm(RegisterID src, int offset, RegisterID base)
{
spew("movl %s, %s0x%x(%s)",
@ -1509,14 +1527,6 @@ public:
m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset);
}
void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
spew("movw %s, %d(%s,%s,%d)",
nameIReg(2, src), offset, nameIReg(base), nameIReg(index), 1<<scale);
m_formatter.prefix(PRE_OPERAND_SIZE);
m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
}
void movl_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
spew("movl %s, %d(%s,%s,%d)",
@ -1564,6 +1574,18 @@ public:
m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, index, scale, offset);
}
#if !WTF_CPU_X86_64
void movl_mr(const void* addr, RegisterID dst)
{
spew("movl %p, %s",
addr, nameIReg(4, dst));
if (dst == X86Registers::eax)
movl_mEAX(addr);
else
m_formatter.oneByteOp(OP_MOV_GvEv, dst, addr);
}
#endif
void movl_i32r(int imm, RegisterID dst)
{
spew("movl $0x%x, %s",
@ -1780,16 +1802,6 @@ public:
m_formatter.oneByteOp(OP_MOV_EvGv, src, addr);
}
void movl_mr(const void* addr, RegisterID dst)
{
spew("movl %p, %s",
addr, nameIReg(4, dst));
if (dst == X86Registers::eax)
movl_mEAX(addr);
else
m_formatter.oneByteOp(OP_MOV_GvEv, dst, addr);
}
void movl_i32m(int imm, const void* addr)
{
FIXME_INSN_PRINTING;
@ -1819,6 +1831,15 @@ public:
m_formatter.oneByteOp8(OP_MOV_EbGv, src, base, index, scale, offset);
}
#if !WTF_CPU_X86_64
void movb_rm(RegisterID src, const void* addr)
{
spew("movb %s, %p",
nameIReg(1, src), addr);
m_formatter.oneByteOp(OP_MOV_EbGv, src, addr);
}
#endif
void movzbl_mr(int offset, RegisterID base, RegisterID dst)
{
spew("movzbl %s0x%x(%s), %s",
@ -1840,6 +1861,15 @@ public:
m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, index, scale, offset);
}
#if !WTF_CPU_X86_64
void movzbl_mr(const void* addr, RegisterID dst)
{
spew("movzbl %p, %s",
addr, nameIReg(dst));
m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, addr);
}
#endif
void movsbl_mr(int offset, RegisterID base, RegisterID dst)
{
spew("movsbl %s0x%x(%s), %s",
@ -1861,6 +1891,15 @@ public:
m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, index, scale, offset);
}
#if !WTF_CPU_X86_64
void movsbl_mr(const void* addr, RegisterID dst)
{
spew("movsbl %p, %s",
addr, nameIReg(4, dst));
m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, addr);
}
#endif
void movzwl_mr(int offset, RegisterID base, RegisterID dst)
{
spew("movzwl %s0x%x(%s), %s",
@ -1882,6 +1921,15 @@ public:
m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, index, scale, offset);
}
#if !WTF_CPU_X86_64
void movzwl_mr(const void* addr, RegisterID dst)
{
spew("movzwl %p, %s",
addr, nameIReg(4, dst));
m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, addr);
}
#endif
void movswl_mr(int offset, RegisterID base, RegisterID dst)
{
spew("movswl %s0x%x(%s), %s",
@ -1903,6 +1951,15 @@ public:
m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, index, scale, offset);
}
#if !WTF_CPU_X86_64
void movswl_mr(const void* addr, RegisterID dst)
{
spew("movswl %p, %s",
addr, nameIReg(4, dst));
m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, addr);
}
#endif
void movzbl_rr(RegisterID src, RegisterID dst)
{
spew("movzbl %s, %s",
@ -2334,6 +2391,16 @@ public:
m_formatter.twoByteOp_disp32(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset);
}
#if !WTF_CPU_X86_64
void movss_rm(XMMRegisterID src, const void* addr)
{
spew("movss %s, %p",
nameFPReg(src), addr);
m_formatter.prefix(PRE_SSE_F3);
m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, addr);
}
#endif
void movss_mr(int offset, RegisterID base, XMMRegisterID dst)
{
spew("movss %s0x%x(%s), %s",
@ -2350,6 +2417,16 @@ public:
m_formatter.twoByteOp_disp32(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset);
}
#if !WTF_CPU_X86_64
void movss_mr(const void* addr, XMMRegisterID dst)
{
spew("movss %p, %s",
addr, nameFPReg(dst));
m_formatter.prefix(PRE_SSE_F3);
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, addr);
}
#endif
void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
spew("movsd %s, %d(%s,%s,%d)",

View File

@ -165,5 +165,283 @@ assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'functi
new Float64Array(BUF_64KB)[0] = 3.5;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +-f64[0] } return f'), this, null, BUF_64KB)(), -3.5);
// Test constant loads.
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[1] = -1 } return f'), this, null, buf)();
assertEq(new Uint8Array(buf)[0], 0);
assertEq(new Uint8Array(buf)[1], 255);
assertEq(new Uint8Array(buf)[2], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[4095] = -1 } return f'), this, null, buf)();
assertEq(new Uint8Array(buf)[4094], 0);
assertEq(new Uint8Array(buf)[4095], 255);
assertEq(new Uint8Array(buf)[4096], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[4096] = -1 } return f'), this, null, buf)();
assertEq(new Uint8Array(buf)[4095], 0);
assertEq(new Uint8Array(buf)[4096], 255);
assertEq(new Uint8Array(buf)[4097], 0);
assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[8192] = -1 } return f'), this, null, buf);
var buf = new ArrayBuffer(262144);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[258048] = -1 } return f'), this, null, buf)();
assertEq(new Uint8Array(buf)[258047], 0);
assertEq(new Uint8Array(buf)[258048], 255);
assertEq(new Uint8Array(buf)[258049], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[1] = -1 } return f'), this, null, buf)();
assertEq(new Int8Array(buf)[0], 0);
assertEq(new Int8Array(buf)[1], -1);
assertEq(new Int8Array(buf)[2], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[4095] = -1 } return f'), this, null, buf)();
assertEq(new Int8Array(buf)[4094], 0);
assertEq(new Int8Array(buf)[4095], -1);
assertEq(new Int8Array(buf)[4096], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[4096] = -1 } return f'), this, null, buf)();
assertEq(new Int8Array(buf)[4095], 0);
assertEq(new Int8Array(buf)[4096], -1);
assertEq(new Int8Array(buf)[4097], 0);
assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[8192] = -1 } return f'), this, null, buf);
var buf = new ArrayBuffer(262144);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[258048] = -1 } return f'), this, null, buf)();
assertEq(new Int8Array(buf)[258047], 0);
assertEq(new Int8Array(buf)[258048], -1);
assertEq(new Int8Array(buf)[258049], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[1] = -1 } return f'), this, null, buf)();
assertEq(new Uint16Array(buf)[0], 0);
assertEq(new Uint16Array(buf)[1], 65535);
assertEq(new Uint16Array(buf)[2], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[2047] = -1 } return f'), this, null, buf)();
assertEq(new Uint16Array(buf)[2046], 0);
assertEq(new Uint16Array(buf)[2047], 65535);
assertEq(new Uint16Array(buf)[2048], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[2048] = -1 } return f'), this, null, buf)();
assertEq(new Uint16Array(buf)[2047], 0);
assertEq(new Uint16Array(buf)[2048], 65535);
assertEq(new Uint16Array(buf)[2049], 0);
assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[4096] = -1 } return f'), this, null, buf);
var buf = new ArrayBuffer(262144);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[126976] = -1 } return f'), this, null, buf)();
assertEq(new Uint16Array(buf)[126975], 0);
assertEq(new Uint16Array(buf)[126976], 65535);
assertEq(new Uint16Array(buf)[126977], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[1] = -1 } return f'), this, null, buf)();
assertEq(new Int16Array(buf)[0], 0);
assertEq(new Int16Array(buf)[1], -1);
assertEq(new Int16Array(buf)[2], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[2047] = -1 } return f'), this, null, buf)();
assertEq(new Int16Array(buf)[2046], 0);
assertEq(new Int16Array(buf)[2047], -1);
assertEq(new Int16Array(buf)[2048], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[2048] = -1 } return f'), this, null, buf)();
assertEq(new Int16Array(buf)[2047], 0);
assertEq(new Int16Array(buf)[2048], -1);
assertEq(new Int16Array(buf)[2049], 0);
assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[4096] = -1 } return f'), this, null, buf);
var buf = new ArrayBuffer(262144);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[126976] = -1 } return f'), this, null, buf)();
assertEq(new Int16Array(buf)[126975], 0);
assertEq(new Int16Array(buf)[126976], -1);
assertEq(new Int16Array(buf)[126977], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[1] = -1 } return f'), this, null, buf)();
assertEq(new Uint32Array(buf)[0], 0);
assertEq(new Uint32Array(buf)[1], 4294967295);
assertEq(new Uint32Array(buf)[2], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[1023] = -1 } return f'), this, null, buf)();
assertEq(new Uint32Array(buf)[1022], 0);
assertEq(new Uint32Array(buf)[1023], 4294967295);
assertEq(new Uint32Array(buf)[1024], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[1024] = -1 } return f'), this, null, buf)();
assertEq(new Uint32Array(buf)[1023], 0);
assertEq(new Uint32Array(buf)[1024], 4294967295);
assertEq(new Uint32Array(buf)[1025], 0);
assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[2048] = -1 } return f'), this, null, buf);
var buf = new ArrayBuffer(262144);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[61440] = -1 } return f'), this, null, buf)();
assertEq(new Uint32Array(buf)[61439], 0);
assertEq(new Uint32Array(buf)[61440], 4294967295);
assertEq(new Uint32Array(buf)[61441], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[1] = -1 } return f'), this, null, buf)();
assertEq(new Int32Array(buf)[0], 0);
assertEq(new Int32Array(buf)[1], -1);
assertEq(new Int32Array(buf)[2], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[1023] = -1 } return f'), this, null, buf)();
assertEq(new Int32Array(buf)[1022], 0);
assertEq(new Int32Array(buf)[1023], -1);
assertEq(new Int32Array(buf)[124], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[1024] = -1 } return f'), this, null, buf)();
assertEq(new Int32Array(buf)[1023], 0);
assertEq(new Int32Array(buf)[1024], -1);
assertEq(new Int32Array(buf)[1025], 0);
assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[2048] = -1 } return f'), this, null, buf);
var buf = new ArrayBuffer(262144);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[61440] = -1 } return f'), this, null, buf)();
assertEq(new Int32Array(buf)[61439], 0);
assertEq(new Int32Array(buf)[61440], -1);
assertEq(new Int32Array(buf)[61441], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[1] = -1.0 } return f'), this, null, buf)();
assertEq(new Int32Array(buf)[0], 0);
assertEq(new Float32Array(buf)[1], -1.0);
assertEq(new Int32Array(buf)[2], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[1023] = -1.0 } return f'), this, null, buf)();
assertEq(new Int32Array(buf)[1022], 0);
assertEq(new Float32Array(buf)[1023], -1.0);
assertEq(new Int32Array(buf)[124], 0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[1024] = -1.0 } return f'), this, null, buf)();
assertEq(new Int32Array(buf)[1023], 0);
assertEq(new Float32Array(buf)[1024], -1.0);
assertEq(new Int32Array(buf)[1025], 0);
assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[2048] = -1.0 } return f'), this, null, buf);
var buf = new ArrayBuffer(262144);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[61440] = -1.0 } return f'), this, null, buf)();
assertEq(new Float32Array(buf)[61439], 0.0);
assertEq(new Float32Array(buf)[61440], -1.0);
assertEq(new Float32Array(buf)[61441], 0.0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[1] = -1.0 } return f'), this, null, buf)();
assertEq(new Float64Array(buf)[0], 0.0);
assertEq(new Float64Array(buf)[1], -1.0);
assertEq(new Float64Array(buf)[2], 0.0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[511] = -1.0 } return f'), this, null, buf)();
assertEq(new Float64Array(buf)[510], 0.0);
assertEq(new Float64Array(buf)[511], -1.0);
assertEq(new Float64Array(buf)[512], 0.0);
var buf = new ArrayBuffer(8192);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[512] = -1.0 } return f'), this, null, buf)();
assertEq(new Float64Array(buf)[511], 0.0);
assertEq(new Float64Array(buf)[512], -1.0);
assertEq(new Float64Array(buf)[513], 0.0);
assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[1024] = -1.0 } return f'), this, null, buf);
var buf = new ArrayBuffer(262144);
asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[28672] = -1.0 } return f'), this, null, buf)();
assertEq(new Float64Array(buf)[28671], 0.0);
assertEq(new Float64Array(buf)[28672], -1.0);
assertEq(new Float64Array(buf)[28673], 0.0);
var buf = new ArrayBuffer(8192);
new Uint8Array(buf)[1] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[1]|0; } return f'), this, null, buf)(),255);
new Int8Array(buf)[1] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i8[1]|0; } return f'), this, null, buf)(),-1);
var buf = new ArrayBuffer(262144);
new Uint8Array(buf)[126976] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[126976]|0; } return f'), this, null, buf)(),255);
new Int8Array(buf)[126976] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i8[126976]|0; } return f'), this, null, buf)(),-1);
var buf = new ArrayBuffer(8192);
new Uint16Array(buf)[1] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u16[1]|0; } return f'), this, null, buf)(),65535);
new Int16Array(buf)[1] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i16[1]|0; } return f'), this, null, buf)(),-1);
var buf = new ArrayBuffer(262144);
new Uint16Array(buf)[126976] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u16[126976]|0; } return f'), this, null, buf)(),65535);
new Int16Array(buf)[126976] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i16[126976]|0; } return f'), this, null, buf)(),-1);
var buf = new ArrayBuffer(8192);
new Uint32Array(buf)[1] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[1]|0; } return f'), this, null, buf)(),-1);
new Int32Array(buf)[1] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[1]|0; } return f'), this, null, buf)(),-1);
var buf = new ArrayBuffer(262144);
new Int32Array(buf)[61440] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[61440]|0; } return f'), this, null, buf)(),-1);
var buf = new ArrayBuffer(8192);
new Float32Array(buf)[1] = -1.0;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[1]; } return f'), this, null, buf)(),-1.0);
new Float32Array(buf)[1023] = -1.0;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[1023]; } return f'), this, null, buf)(),-1.0);
new Float32Array(buf)[1024] = -1.0;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[1024]; } return f'), this, null, buf)(),-1.0);
assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[2048]; } return f'), this, null, buf);
var buf = new ArrayBuffer(262144);
new Float32Array(buf)[61440] = -1.0;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[61440]; } return f'), this, null, buf)(),-1.0);
var buf = new ArrayBuffer(8192);
new Float64Array(buf)[1] = -1.0;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[1]; } return f'), this, null, buf)(),-1.0);
new Float64Array(buf)[511] = -1.0;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[511]; } return f'), this, null, buf)(),-1.0);
new Float64Array(buf)[512] = -1.0;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[512]; } return f'), this, null, buf)(),-1.0);
assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[1024]; } return f'), this, null, buf);
var buf = new ArrayBuffer(262144);
new Float64Array(buf)[28672] = -1.0;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[28672]; } return f'), this, null, buf)(),-1.0);
// Test bitwise-and optimizations.
var buf = new ArrayBuffer(8192);
new Uint8Array(buf)[8191] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[8191&8191]|0; } return f'), this, null, buf)(),255);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[(8191&8191)>>0]|0; } return f'), this, null, buf)(),255);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[8192&8191] = -1; u8[0] = 0; return u8[8192&8191]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[(8192&8191)>>0] = -1; u8[0] = 0; return u8[(8192&8191)>>0]|0; } return f'), this, null, buf)(),0);
new Int8Array(buf)[8191] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i8[8191&8191]|0; } return f'), this, null, buf)(),-1);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i8[(8191&8191)>>0]|0; } return f'), this, null, buf)(),-1);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[8192&8191] = -1; i8[0] = 0; return i8[8192&8191]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[(8192&8191)>>0] = -1; i8[0] = 0; return i8[(8192&8191)>>0]|0; } return f'), this, null, buf)(),0);
var buf = new ArrayBuffer(8192);
new Uint16Array(buf)[4095] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u16[(8190&8191)>>1]|0; } return f'), this, null, buf)(),65535);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u16[(8191&8191)>>1]|0; } return f'), this, null, buf)(),65535);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[(8192&8191)>>1] = -1; u16[0] = 0; return u16[(8192&8191)>>1]|0; } return f'), this, null, buf)(),0);
new Int16Array(buf)[4095] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i16[(8190&8191)>>1]|0; } return f'), this, null, buf)(),-1);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i16[(8191&8191)>>1]|0; } return f'), this, null, buf)(),-1);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[(8192&8191)>>1] = -1; i16[0] = 0; return i16[(8192&8191)>>1]|0; } return f'), this, null, buf)(),0);
var buf = new ArrayBuffer(8192);
new Uint32Array(buf)[2047] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(8188&8191)>>2]|0; } return f'), this, null, buf)(),-1);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(8191&8191)>>2]|0; } return f'), this, null, buf)(),-1);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[(8192&8191)>>2] = -1; u32[0] = 0; return u32[(8192&8191)>>2]|0; } return f'), this, null, buf)(),0);
new Int32Array(buf)[2047] = -1;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[(8188&8191)>>2]|0; } return f'), this, null, buf)(),-1);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[(8191&8191)>>2]|0; } return f'), this, null, buf)(),-1);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[(8192&8191)>>2] = -1; i32[0] = 0; return i32[(8192&8191)>>2]|0; } return f'), this, null, buf)(),0);
var buf = new ArrayBuffer(8192);
new Float32Array(buf)[2047] = -1.0;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[(8188&8191)>>2]; } return f'), this, null, buf)(),-1.0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[(8191&8191)>>2]; } return f'), this, null, buf)(),-1.0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[(8192&8191)>>2] = -1.0; f32[0] = 0.0; return +f32[(8192&8191)>>2]; } return f'), this, null, buf)(),0.0);
var buf = new ArrayBuffer(8192);
new Float64Array(buf)[1023] = -1.0;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[(8184&8191)>>3]; } return f'), this, null, buf)(),-1.0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[(8191&8191)>>3]; } return f'), this, null, buf)(),-1.0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[(8192&8191)>>3] = -1.0; f64[0] = 0.0; return +f64[(8192&8191)>>3]; } return f'), this, null, buf)(),0.0);
// Bug 882012
assertEq(asmLink(asmCompile('stdlib', 'foreign', 'heap', USE_ASM + "var id=foreign.id;var doubles=new stdlib.Float64Array(heap);function g(){doubles[0]=+id(2.0);return +doubles[0];}return g"), this, {id: function(x){return x;}}, BUF_64KB)(), 2.0);

View File

@ -2,15 +2,49 @@ load(libdir + "asm.js");
// constants
var buf = new ArrayBuffer(4096);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x7fffffff]|0 } return f'), this, null, buf)(), 0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x80000000]|0 } return f'), this, null, buf)(), 0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x8fffffff]|0 } return f'), this, null, buf)(), 0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0xffffffff]|0 } return f'), this, null, buf)(), 0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[-1]|0 } return f'), this, null, buf)(), 0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[-2]|0 } return f'), this, null, buf)(), 0);
// An unshifted literal constant byte index in the range 0 to 2^31-1 inclusive should give a link failure.
assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x7fffffff]|0 } return f'), this, null, buf);
assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x1fffffff]|0 } return f'), this, null, buf);
// An unshifted literal constant byte index outside the range 0 to 2^31-1 inclusive should cause an error compiling.
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x20000000]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x3fffffff]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x40000000]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x7fffffff]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x80000000]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x8fffffff]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0xffffffff]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x100000000]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x80000000]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0xffffffff]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x100000000]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int16Array(b); function f() {return arr[-1]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[-2]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[10-12]|0 } return f');
// An intish shifted literal constant index should not fail to compile or link.
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x3fffffff>>0]|0 } return f'), this, null, buf)(), 0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x3fffffff>>2]|0 } return f'), this, null, buf)(), 0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0xffffffff>>0]|0 } return f'), this, null, buf)(), 0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0xffffffff>>2]|0 } return f'), this, null, buf)(), 0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[-1>>0]|0 } return f'), this, null, buf)(), 0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[-1>>2]|0 } return f'), this, null, buf)(), 0);
// Unsigned (intish) folded constant index.
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0xffffffff>>>0]|0 } return f'), this, null, buf)(), 0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {arr[0] = 1; return arr[(0xffffffff+1)>>>0]|0 } return f'), this, null, buf)(), 1);
// A non-intish shifted literal constant index should cause an error compiling.
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x100000000>>0]|0 } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x100000000>>2]|0 } return f');
// Folded non-intish constant expressions should cause an error compiling.
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0xffffffff+1]|0 } return f');
function testInt(ctor, shift, scale, disp) {
var ab = new ArrayBuffer(4096);
var arr = new ctor(ab);

View File

@ -909,6 +909,11 @@ TypedArrayStoreType(ArrayBufferView::ViewType viewType)
MOZ_ASSUME_UNREACHABLE("Unexpected array type");
}
enum NeedsBoundsCheck {
NO_BOUNDS_CHECK,
NEEDS_BOUNDS_CHECK
};
/*****************************************************************************/
namespace {
@ -1473,6 +1478,15 @@ class MOZ_STACK_CLASS ModuleCompiler
return globalAccesses_.append(access);
}
// 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.
void requireHeapLengthToBeAtLeast(uint32_t len) {
module_->requireHeapLengthToBeAtLeast(len);
}
uint32_t minHeapLength() const {
return module_->minHeapLength();
}
bool collectAccesses(MIRGenerator &gen) {
if (!module_->addHeapAccesses(gen.heapAccesses()))
return false;
@ -1951,20 +1965,25 @@ class FunctionCompiler
curBlock_->setSlot(info().localSlot(local.slot), def);
}
MDefinition *loadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr)
MDefinition *loadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, NeedsBoundsCheck chk)
{
if (!curBlock_)
return NULL;
MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(vt, ptr);
curBlock_->add(load);
if (chk == NO_BOUNDS_CHECK)
load->setSkipBoundsCheck(true);
return load;
}
void storeHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v)
void storeHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
{
if (!curBlock_)
return;
curBlock_->add(MAsmJSStoreHeap::New(vt, ptr, v));
MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(vt, ptr, v);
curBlock_->add(store);
if (chk == NO_BOUNDS_CHECK)
store->setSkipBoundsCheck(true);
}
MDefinition *loadGlobalVar(const ModuleCompiler::Global &global)
@ -3087,12 +3106,34 @@ CheckVarRef(FunctionCompiler &f, ParseNode *varRef, MDefinition **def, Type *typ
return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
}
static bool
FoldMaskedArrayIndex(FunctionCompiler &f, ParseNode **indexExpr, int32_t *mask,
NeedsBoundsCheck *needsBoundsCheck)
{
ParseNode *indexNode = BinaryLeft(*indexExpr);
ParseNode *maskNode = BinaryRight(*indexExpr);
uint32_t mask2;
if (IsLiteralUint32(maskNode, &mask2)) {
// Flag the access to skip the bounds check if the mask ensures that an 'out of
// bounds' access can not occur based on the current heap length constraint.
if (mozilla::CountLeadingZeroes32(f.m().minHeapLength() - 1) <= mozilla::CountLeadingZeroes32(mask2))
*needsBoundsCheck = NO_BOUNDS_CHECK;
*mask &= mask2;
*indexExpr = indexNode;
return true;
}
return false;
}
static bool
CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType *viewType,
MDefinition **def)
MDefinition **def, NeedsBoundsCheck *needsBoundsCheck)
{
ParseNode *viewName = ElemBase(elem);
ParseNode *indexExpr = ElemIndex(elem);
*needsBoundsCheck = NEEDS_BOUNDS_CHECK;
if (!viewName->isKind(PNK_NAME))
return f.fail(viewName, "base of array access must be a typed array view name");
@ -3105,11 +3146,23 @@ CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType
uint32_t pointer;
if (IsLiteralUint32(indexExpr, &pointer)) {
if (pointer > (uint32_t(INT32_MAX) >> TypedArrayShift(*viewType)))
return f.fail(indexExpr, "constant index out of range");
pointer <<= TypedArrayShift(*viewType);
// It is adequate to note pointer+1 rather than rounding up to the next
// access-size boundary because access is always aligned and the constraint
// will be rounded up to a larger alignment later.
f.m().requireHeapLengthToBeAtLeast(uint32_t(pointer) + 1);
*needsBoundsCheck = NO_BOUNDS_CHECK;
*def = f.constant(Int32Value(pointer));
return true;
}
// Mask off the low bits to account for the clearing effect of a right shift
// followed by the left shift implicit in the array access. E.g., H32[i>>2]
// loses the low two bits.
int32_t mask = ~((uint32_t(1) << TypedArrayShift(*viewType)) - 1);
MDefinition *pointerDef;
if (indexExpr->isKind(PNK_RSH)) {
ParseNode *shiftNode = BinaryRight(indexExpr);
@ -3123,6 +3176,21 @@ CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType
if (shift != requiredShift)
return f.failf(shiftNode, "shift amount must be %u", requiredShift);
if (pointerNode->isKind(PNK_BITAND))
FoldMaskedArrayIndex(f, &pointerNode, &mask, needsBoundsCheck);
// Fold a 'literal constant right shifted' now, and skip the bounds check if
// currently possible. This handles the optimization of many of these uses without
// the need for range analysis, and saves the generation of a MBitAnd op.
if (IsLiteralUint32(pointerNode, &pointer) && pointer <= uint32_t(INT32_MAX)) {
// Cases: b[c>>n], and b[(c&m)>>n]
pointer &= mask;
if (pointer < f.m().minHeapLength())
*needsBoundsCheck = NO_BOUNDS_CHECK;
*def = f.constant(Int32Value(pointer));
return true;
}
Type pointerType;
if (!CheckExpr(f, pointerNode, &pointerDef, &pointerType))
return false;
@ -3133,19 +3201,32 @@ CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType
if (TypedArrayShift(*viewType) != 0)
return f.fail(indexExpr, "index expression isn't shifted; must be an Int8/Uint8 access");
JS_ASSERT(mask == -1);
bool folded = false;
if (indexExpr->isKind(PNK_BITAND))
folded = FoldMaskedArrayIndex(f, &indexExpr, &mask, needsBoundsCheck);
Type pointerType;
if (!CheckExpr(f, indexExpr, &pointerDef, &pointerType))
return false;
if (!pointerType.isInt())
return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars());
if (folded) {
if (!pointerType.isIntish())
return f.failf(indexExpr, "%s is not a subtype of intish", pointerType.toChars());
} else {
if (!pointerType.isInt())
return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars());
}
}
// Mask off the low bits to account for clearing effect of a right shift
// followed by the left shift implicit in the array access. E.g., H32[i>>2]
// loses the low two bits.
int32_t mask = ~((uint32_t(1) << TypedArrayShift(*viewType)) - 1);
*def = f.bitwise<MBitAnd>(pointerDef, f.constant(Int32Value(mask)));
// Don't generate the mask op if there is no need for it which could happen for
// a shift of zero.
if (mask == -1)
*def = pointerDef;
else
*def = f.bitwise<MBitAnd>(pointerDef, f.constant(Int32Value(mask)));
return true;
}
@ -3154,10 +3235,11 @@ CheckArrayLoad(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *ty
{
ArrayBufferView::ViewType viewType;
MDefinition *pointerDef;
if (!CheckArrayAccess(f, elem, &viewType, &pointerDef))
NeedsBoundsCheck needsBoundsCheck;
if (!CheckArrayAccess(f, elem, &viewType, &pointerDef, &needsBoundsCheck))
return false;
*def = f.loadHeap(viewType, pointerDef);
*def = f.loadHeap(viewType, pointerDef, needsBoundsCheck);
*type = TypedArrayLoadType(viewType);
return true;
}
@ -3167,7 +3249,8 @@ CheckStoreArray(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition
{
ArrayBufferView::ViewType viewType;
MDefinition *pointerDef;
if (!CheckArrayAccess(f, lhs, &viewType, &pointerDef))
NeedsBoundsCheck needsBoundsCheck;
if (!CheckArrayAccess(f, lhs, &viewType, &pointerDef, &needsBoundsCheck))
return false;
MDefinition *rhsDef;
@ -3186,7 +3269,7 @@ CheckStoreArray(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition
break;
}
f.storeHeap(viewType, pointerDef, rhsDef);
f.storeHeap(viewType, pointerDef, rhsDef, needsBoundsCheck);
*def = rhsDef;
*type = rhsType;
@ -4713,7 +4796,14 @@ CheckFunction(ModuleCompiler &m, LifoAlloc &lifo, MIRGenerator **mir, ModuleComp
m.parser().release(mark);
// Copy the cumulative minimum heap size constraint to the MIR for use in analysis. The length
// is also constrained to be a power of two, so firstly round up - a larger 'heap required
// length' can help range analysis to prove that bounds checks are not needed.
size_t len = mozilla::RoundUpPow2((size_t) m.minHeapLength());
m.requireHeapLengthToBeAtLeast(len);
*mir = f.extractMIR();
(*mir)->noteMinAsmJSHeapLength(len);
*funcOut = func;
return true;
}

View File

@ -218,6 +218,12 @@ DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
if (!IsPowerOfTwo(heap->byteLength()) || heap->byteLength() < AsmJSAllocationGranularity)
return LinkFail(cx, "ArrayBuffer byteLength must be a power of two greater than or equal to 4096");
// This check is sufficient without considering the size of the loaded datum because heap
// loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
if (heap->byteLength() < module.minHeapLength())
return LinkFail(cx, "ArrayBuffer byteLength less than the largest source code heap length constraint.");
if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");

View File

@ -26,11 +26,15 @@ AsmJSModule::patchHeapAccesses(ArrayBufferObject *heap, JSContext *cx)
{
JS_ASSERT(IsPowerOfTwo(heap->byteLength()));
#if defined(JS_CPU_X86)
void *heapOffset = (void*)heap->dataPointer();
uint8_t *heapOffset = heap->dataPointer();
void *heapLength = (void*)heap->byteLength();
for (unsigned i = 0; i < heapAccesses_.length(); i++) {
JSC::X86Assembler::setPointer(heapAccesses_[i].patchLengthAt(code_), heapLength);
JSC::X86Assembler::setPointer(heapAccesses_[i].patchOffsetAt(code_), heapOffset);
const jit::AsmJSHeapAccess &access = heapAccesses_[i];
if (access.hasLengthCheck())
JSC::X86Assembler::setPointer(access.patchLengthAt(code_), heapLength);
void *addr = access.patchOffsetAt(code_);
uint32_t disp = reinterpret_cast<uint32_t>(JSC::X86Assembler::getPointer(addr));
JSC::X86Assembler::setPointer(addr, (void *)(heapOffset + disp));
}
#elif defined(JS_CPU_ARM)
uint32_t bits = mozilla::CeilingLog2(heap->byteLength());

View File

@ -318,6 +318,7 @@ class AsmJSModule
ExitVector exits_;
ExportedFunctionVector exports_;
HeapAccessVector heapAccesses_;
uint32_t minHeapLength_;
#if defined(MOZ_VTUNE) or defined(JS_ION_PERF)
ProfiledFunctionVector profiledFunctions_;
#endif
@ -349,6 +350,7 @@ class AsmJSModule
: globalArgumentName_(NULL),
importArgumentName_(NULL),
bufferArgumentName_(NULL),
minHeapLength_(AsmJSAllocationGranularity),
code_(NULL),
operationCallbackExit_(NULL),
linked_(false)
@ -630,6 +632,14 @@ class AsmJSModule
}
void patchHeapAccesses(ArrayBufferObject *heap, JSContext *cx);
void requireHeapLengthToBeAtLeast(uint32_t len) {
if (len > minHeapLength_)
minHeapLength_ = len;
}
uint32_t minHeapLength() const {
return minHeapLength_;
}
uint8_t *allocateCodeAndGlobalSegment(ExclusiveContext *cx, size_t bytesNeeded);
uint8_t *functionCode() const {

View File

@ -1306,7 +1306,7 @@ OptimizeMIR(MIRGenerator *mir)
}
if (js_IonOptions.rangeAnalysis) {
RangeAnalysis r(graph);
RangeAnalysis r(mir, graph);
if (!r.addBetaNobes())
return false;
IonSpewPass("Beta");

View File

@ -2789,13 +2789,6 @@ LIRGenerator::visitHaveSameClass(MHaveSameClass *ins)
return define(new LHaveSameClass(useRegister(lhs), useRegister(rhs), temp()), ins);
}
bool
LIRGenerator::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
{
LAsmJSLoadHeap *lir = new LAsmJSLoadHeap(useRegisterAtStart(ins->ptr()));
return define(lir, ins);
}
bool
LIRGenerator::visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins)
{

View File

@ -237,7 +237,6 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitFunctionBoundary(MFunctionBoundary *ins);
bool visitIsCallable(MIsCallable *ins);
bool visitHaveSameClass(MHaveSameClass *ins);
bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
bool visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins);
bool visitAsmJSStoreGlobalVar(MAsmJSStoreGlobalVar *ins);
bool visitAsmJSLoadFFIFunc(MAsmJSLoadFFIFunc *ins);

View File

@ -8158,10 +8158,25 @@ class MAsmJSUMod : public MBinaryInstruction
}
};
class MAsmJSLoadHeap : public MUnaryInstruction
class MAsmJSHeapAccess
{
ArrayBufferView::ViewType viewType_;
bool skipBoundsCheck_;
public:
MAsmJSHeapAccess(ArrayBufferView::ViewType vt, bool s)
: viewType_(vt), skipBoundsCheck_(s)
{}
ArrayBufferView::ViewType viewType() const { return viewType_; }
bool skipBoundsCheck() const { return skipBoundsCheck_; }
void setSkipBoundsCheck(bool v) { skipBoundsCheck_ = v; }
};
class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
{
MAsmJSLoadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr)
: MUnaryInstruction(ptr), viewType_(vt)
: MUnaryInstruction(ptr), MAsmJSHeapAccess(vt, false)
{
if (vt == ArrayBufferView::TYPE_FLOAT32 || vt == ArrayBufferView::TYPE_FLOAT64)
setResultType(MIRType_Double);
@ -8169,8 +8184,6 @@ class MAsmJSLoadHeap : public MUnaryInstruction
setResultType(MIRType_Int32);
}
ArrayBufferView::ViewType viewType_;
public:
INSTRUCTION_HEADER(AsmJSLoadHeap);
@ -8178,18 +8191,15 @@ class MAsmJSLoadHeap : public MUnaryInstruction
return new MAsmJSLoadHeap(vt, ptr);
}
ArrayBufferView::ViewType viewType() const { return viewType_; }
MDefinition *ptr() const { return getOperand(0); }
};
class MAsmJSStoreHeap : public MBinaryInstruction
class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
{
MAsmJSStoreHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v)
: MBinaryInstruction(ptr, v), viewType_(vt)
: MBinaryInstruction(ptr, v) , MAsmJSHeapAccess(vt, false)
{}
ArrayBufferView::ViewType viewType_;
public:
INSTRUCTION_HEADER(AsmJSStoreHeap);
@ -8197,7 +8207,6 @@ class MAsmJSStoreHeap : public MBinaryInstruction
return new MAsmJSStoreHeap(vt, ptr, v);
}
ArrayBufferView::ViewType viewType() const { return viewType_; }
MDefinition *ptr() const { return getOperand(0); }
MDefinition *value() const { return getOperand(1); }
};

View File

@ -122,6 +122,12 @@ class MIRGenerator
const Vector<AsmJSHeapAccess, 0, IonAllocPolicy> &heapAccesses() const {
return asmJSHeapAccesses_;
}
void noteMinAsmJSHeapLength(uint32_t len) {
minAsmJSHeapLength_ = len;
}
uint32_t minAsmJSHeapLength() const {
return minAsmJSHeapLength_;
}
bool noteGlobalAccess(unsigned offset, unsigned globalDataOffset) {
return asmJSGlobalAccesses_.append(AsmJSGlobalAccess(offset, globalDataOffset));
}
@ -145,6 +151,7 @@ class MIRGenerator
bool performsAsmJSCall_;
AsmJSHeapAccessVector asmJSHeapAccesses_;
AsmJSGlobalAccessVector asmJSGlobalAccesses_;
uint32_t minAsmJSHeapLength_;
#if defined(JS_ION_PERF)
AsmJSPerfSpewer asmJSPerfSpewer_;

View File

@ -8,6 +8,7 @@
#include "jsanalyze.h"
#include "jit/AsmJS.h"
#include "jit/Ion.h"
#include "jit/IonBuilder.h"
#include "jit/IonSpewer.h"
@ -27,7 +28,8 @@ MIRGenerator::MIRGenerator(JSCompartment *compartment,
error_(false),
cancelBuild_(0),
maxAsmJSStackArgBytes_(0),
performsAsmJSCall_(false)
performsAsmJSCall_(false),
minAsmJSHeapLength_(AsmJSAllocationGranularity)
{ }
bool

View File

@ -1619,6 +1619,26 @@ RangeAnalysis::analyze()
if (block->isLoopHeader())
analyzeLoop(block);
if (mir->compilingAsmJS()) {
for (MInstructionIterator i = block->begin(); i != block->end(); i++) {
if (i->isAsmJSLoadHeap()) {
MAsmJSLoadHeap *ins = i->toAsmJSLoadHeap();
Range *range = ins->ptr()->range();
if (range && !range->isLowerInfinite() && range->lower() >= 0 &&
!range->isUpperInfinite() &&
(uint32_t) range->upper() < mir->minAsmJSHeapLength())
ins->setSkipBoundsCheck(true);
} else if (i->isAsmJSStoreHeap()) {
MAsmJSStoreHeap *ins = i->toAsmJSStoreHeap();
Range *range = ins->ptr()->range();
if (range && !range->isLowerInfinite() && range->lower() >= 0 &&
!range->isUpperInfinite() &&
(uint32_t) range->upper() < mir->minAsmJSHeapLength())
ins->setSkipBoundsCheck(true);
}
}
}
}
return true;

View File

@ -74,11 +74,12 @@ class RangeAnalysis
MBasicBlock *block);
protected:
MIRGenerator *mir;
MIRGraph &graph_;
public:
MOZ_CONSTEXPR RangeAnalysis(MIRGraph &graph) :
graph_(graph) {}
MOZ_CONSTEXPR RangeAnalysis(MIRGenerator *mir, MIRGraph &graph) :
mir(mir), graph_(graph) {}
bool addBetaNobes();
bool analyze();
bool addRangeAssertions();

View File

@ -804,11 +804,13 @@ class AsmJSHeapAccess
public:
#if defined(JS_CPU_X86) || defined(JS_CPU_X64)
// If 'cmp' equals 'offset' or if it is not supplied then the
// cmpDelta_ is zero indicating that there is no length to patch.
AsmJSHeapAccess(uint32_t offset, uint32_t after, ArrayBufferView::ViewType vt,
AnyRegister loadedReg, uint32_t cmp = UINT32_MAX)
: offset_(offset),
# if defined(JS_CPU_X86)
cmpDelta_(offset - cmp),
cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp),
# endif
opLength_(after - offset),
isFloat32Load_(vt == ArrayBufferView::TYPE_FLOAT32),
@ -817,7 +819,7 @@ class AsmJSHeapAccess
AsmJSHeapAccess(uint32_t offset, uint8_t after, uint32_t cmp = UINT32_MAX)
: offset_(offset),
# if defined(JS_CPU_X86)
cmpDelta_(offset - cmp),
cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp),
# endif
opLength_(after - offset),
isFloat32Load_(false),
@ -832,6 +834,7 @@ class AsmJSHeapAccess
uint32_t offset() const { return offset_; }
void setOffset(uint32_t offset) { offset_ = offset; }
#if defined(JS_CPU_X86)
bool hasLengthCheck() const { return cmpDelta_ > 0; }
void *patchLengthAt(uint8_t *code) const { return code + (offset_ - cmpDelta_); }
void *patchOffsetAt(uint8_t *code) const { return code + (offset_ + opLength_); }
#endif

View File

@ -1779,32 +1779,68 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
int size;
bool isFloat = false;
switch (mir->viewType()) {
case ArrayBufferView::TYPE_INT8: isSigned = true; size = 8; break;
case ArrayBufferView::TYPE_UINT8: isSigned = false; size = 8; break;
case ArrayBufferView::TYPE_INT16: isSigned = true; size = 16; break;
case ArrayBufferView::TYPE_INT8: isSigned = true; size = 8; break;
case ArrayBufferView::TYPE_UINT8: isSigned = false; size = 8; break;
case ArrayBufferView::TYPE_INT16: isSigned = true; size = 16; break;
case ArrayBufferView::TYPE_UINT16: isSigned = false; size = 16; break;
case ArrayBufferView::TYPE_INT32:
case ArrayBufferView::TYPE_UINT32: isSigned = true; size = 32; break;
case ArrayBufferView::TYPE_FLOAT64: isFloat = true; size = 64; break;
case ArrayBufferView::TYPE_FLOAT32:
isFloat = true;
size = 32;
break;
case ArrayBufferView::TYPE_FLOAT32: isFloat = true; size = 32; break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}
Register index = ToRegister(ins->ptr());
BufferOffset bo = masm.ma_BoundsCheck(index);
const LAllocation *ptr = ins->ptr();
if (ptr->isConstant()) {
JS_ASSERT(mir->skipBoundsCheck());
int32_t ptrImm = ptr->toConstant()->toInt32();
JS_ASSERT(ptrImm >= 0);
if (isFloat) {
VFPRegister vd(ToFloatRegister(ins->output()));
if (size == 32) {
masm.ma_vldr(Operand(HeapReg, ptrImm), vd.singleOverlay(), Assembler::Always);
masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Always);
} else {
masm.ma_vldr(Operand(HeapReg, ptrImm), vd, Assembler::Always);
}
} else {
masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, Imm32(ptrImm),
ToRegister(ins->output()), Offset, Assembler::Always);
}
return true;
}
Register ptrReg = ToRegister(ptr);
if (mir->skipBoundsCheck()) {
if (isFloat) {
VFPRegister vd(ToFloatRegister(ins->output()));
if (size == 32) {
masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Always);
masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Always);
} else {
masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Always);
}
} else {
masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg,
ToRegister(ins->output()), Offset, Assembler::Always);
}
return true;
}
BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
if (isFloat) {
VFPRegister vd(ToFloatRegister(ins->output()));
if (size == 32) {
masm.ma_vldr(vd.singleOverlay(), HeapReg, index, 0, Assembler::Zero);
masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Zero);
masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Zero);
} else {
masm.ma_vldr(vd, HeapReg, index, 0, Assembler::Zero);
masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Zero);
}
masm.ma_vmov(NANReg, ToFloatRegister(ins->output()), Assembler::NonZero);
} else {
masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, index,
} else {
masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg,
ToRegister(ins->output()), Offset, Assembler::Zero);
masm.ma_mov(Imm32(0), ToRegister(ins->output()), NoSetCond, Assembler::NonZero);
}
@ -1825,25 +1861,56 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
case ArrayBufferView::TYPE_UINT16: isSigned = false; size = 16; break;
case ArrayBufferView::TYPE_INT32:
case ArrayBufferView::TYPE_UINT32: isSigned = true; size = 32; break;
case ArrayBufferView::TYPE_FLOAT64: isFloat = true; size = 64; break;
case ArrayBufferView::TYPE_FLOAT32:
isFloat = true;
size = 32;
break;
case ArrayBufferView::TYPE_FLOAT64: isFloat = true; size = 64; break;
case ArrayBufferView::TYPE_FLOAT32: isFloat = true; size = 32; break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}
Register index = ToRegister(ins->ptr());
const LAllocation *ptr = ins->ptr();
if (ptr->isConstant()) {
JS_ASSERT(mir->skipBoundsCheck());
int32_t ptrImm = ptr->toConstant()->toInt32();
JS_ASSERT(ptrImm >= 0);
if (isFloat) {
VFPRegister vd(ToFloatRegister(ins->value()));
if (size == 32) {
masm.as_vcvt(VFPRegister(ScratchFloatReg).singleOverlay(), vd, false, Assembler::Always);
masm.ma_vstr(VFPRegister(ScratchFloatReg).singleOverlay(), Operand(HeapReg, ptrImm), Assembler::Always);
} else {
masm.ma_vstr(vd, Operand(HeapReg, ptrImm), Assembler::Always);
}
} else {
masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm),
ToRegister(ins->value()), Offset, Assembler::Always);
}
return true;
}
BufferOffset bo = masm.ma_BoundsCheck(index);
Register ptrReg = ToRegister(ptr);
if (mir->skipBoundsCheck()) {
Register ptrReg = ToRegister(ptr);
if (isFloat) {
VFPRegister vd(ToFloatRegister(ins->value()));
if (size == 32)
masm.storeFloat(vd, HeapReg, ptrReg, Assembler::Always);
else
masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Always);
} else {
masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
ToRegister(ins->value()), Offset, Assembler::Always);
}
return true;
}
BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
if (isFloat) {
VFPRegister vd(ToFloatRegister(ins->value()));
if (size == 32) {
masm.storeFloat(vd, HeapReg, index, Assembler::Zero);
} else {
masm.ma_vstr(vd, HeapReg, index, 0, Assembler::Zero);
}
} else {
masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, index,
if (size == 32)
masm.storeFloat(vd, HeapReg, ptrReg, Assembler::Zero);
else
masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Zero);
} else {
masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
ToRegister(ins->value()), Offset, Assembler::Zero);
}
return gen->noteHeapAccess(AsmJSHeapAccess(bo.getOffset()));

View File

@ -501,26 +501,39 @@ LIRGeneratorARM::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins)
return define(lir, ins);
}
bool
LIRGeneratorARM::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
{
MDefinition *ptr = ins->ptr();
JS_ASSERT(ptr->type() == MIRType_Int32);
LAllocation ptrAlloc;
// For the ARM it is best to keep the 'ptr' in a register if a bounds check is needed.
if (ptr->isConstant() && ins->skipBoundsCheck()) {
int32_t ptrValue = ptr->toConstant()->value().toInt32();
// A bounds check is only skipped for a positive index.
JS_ASSERT(ptrValue >= 0);
ptrAlloc = LAllocation(ptr->toConstant()->vp());
} else
ptrAlloc = useRegisterAtStart(ptr);
return define(new LAsmJSLoadHeap(ptrAlloc), ins);
}
bool
LIRGeneratorARM::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
{
LAsmJSStoreHeap *lir;
switch (ins->viewType()) {
case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8:
case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16:
case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32:
lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()),
useRegisterAtStart(ins->value()));
break;
case ArrayBufferView::TYPE_FLOAT32:
case ArrayBufferView::TYPE_FLOAT64:
lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()),
useRegisterAtStart(ins->value()));
break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}
MDefinition *ptr = ins->ptr();
JS_ASSERT(ptr->type() == MIRType_Int32);
LAllocation ptrAlloc;
return add(lir, ins);
if (ptr->isConstant() && ins->skipBoundsCheck()) {
JS_ASSERT(ptr->toConstant()->value().toInt32() >= 0);
ptrAlloc = LAllocation(ptr->toConstant()->vp());
} else
ptrAlloc = useRegisterAtStart(ptr);
return add(new LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())), ins);
}
bool

View File

@ -78,6 +78,7 @@ class LIRGeneratorARM : public LIRGeneratorShared
bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins);
bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);

View File

@ -263,6 +263,14 @@ LIRGeneratorShared::useRegisterOrConstantAtStart(MDefinition *mir)
return useRegisterAtStart(mir);
}
LAllocation
LIRGeneratorShared::useRegisterOrNonNegativeConstantAtStart(MDefinition *mir)
{
if (mir->isConstant() && mir->toConstant()->value().toInt32() >= 0)
return LAllocation(mir->toConstant()->vp());
return useRegisterAtStart(mir);
}
LAllocation
LIRGeneratorShared::useRegisterOrNonDoubleConstant(MDefinition *mir)
{

View File

@ -86,6 +86,7 @@ class LIRGeneratorShared : public MInstructionVisitorWithDefaults
inline LAllocation useKeepaliveOrConstant(MDefinition *mir);
inline LAllocation useRegisterOrConstant(MDefinition *mir);
inline LAllocation useRegisterOrConstantAtStart(MDefinition *mir);
inline LAllocation useRegisterOrNonNegativeConstantAtStart(MDefinition *mir);
inline LAllocation useRegisterOrNonDoubleConstant(MDefinition *mir);
#ifdef JS_NUNBOX32

View File

@ -394,8 +394,21 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
{
MAsmJSLoadHeap *mir = ins->mir();
ArrayBufferView::ViewType vt = mir->viewType();
const LAllocation *ptr = ins->ptr();
Operand srcAddr(HeapReg, ToRegister(ins->ptr()), TimesOne);
// No need to note the access if it will never fault.
bool skipNote = mir->skipBoundsCheck();
Operand srcAddr(HeapReg);
if (ptr->isConstant()) {
int32_t ptrImm = ptr->toConstant()->toInt32();
// Note only a positive index is accepted here because a negative offset would
// not wrap back into the protected area reserved for the heap.
JS_ASSERT(ptrImm >= 0);
srcAddr = Operand(HeapReg, ptrImm);
} else {
srcAddr = Operand(HeapReg, ToRegister(ptr), TimesOne);
}
if (vt == ArrayBufferView::TYPE_FLOAT32) {
FloatRegister dest = ToFloatRegister(ins->output());
@ -403,7 +416,7 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
masm.movss(srcAddr, dest);
uint32_t after = masm.size();
masm.cvtss2sd(dest, dest);
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
}
uint32_t before = masm.size();
@ -412,13 +425,13 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
case ArrayBufferView::TYPE_UINT8: masm.movzbl(srcAddr, ToRegister(ins->output())); break;
case ArrayBufferView::TYPE_INT16: masm.movswl(srcAddr, ToRegister(ins->output())); break;
case ArrayBufferView::TYPE_UINT16: masm.movzwl(srcAddr, ToRegister(ins->output())); break;
case ArrayBufferView::TYPE_INT32: masm.movl(srcAddr, ToRegister(ins->output())); break;
case ArrayBufferView::TYPE_INT32:
case ArrayBufferView::TYPE_UINT32: masm.movl(srcAddr, ToRegister(ins->output())); break;
case ArrayBufferView::TYPE_FLOAT64: masm.movsd(srcAddr, ToFloatRegister(ins->output())); break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}
uint32_t after = masm.size();
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
}
bool
@ -426,42 +439,54 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
{
MAsmJSStoreHeap *mir = ins->mir();
ArrayBufferView::ViewType vt = mir->viewType();
const LAllocation *ptr = ins->ptr();
// No need to note the access if it will never fault.
bool skipNote = mir->skipBoundsCheck();
Operand dstAddr(HeapReg);
Operand dstAddr(HeapReg, ToRegister(ins->ptr()), TimesOne);
if (ptr->isConstant()) {
int32_t ptrImm = ptr->toConstant()->toInt32();
// Note only a positive index is accepted here because a negative offset would
// not wrap back into the protected area reserved for the heap.
JS_ASSERT(ptrImm >= 0);
dstAddr = Operand(HeapReg, ptrImm);
} else {
dstAddr = Operand(HeapReg, ToRegister(ins->ptr()), TimesOne);
}
if (vt == ArrayBufferView::TYPE_FLOAT32) {
masm.convertDoubleToFloat(ToFloatRegister(ins->value()), ScratchFloatReg);
uint32_t before = masm.size();
masm.movss(ScratchFloatReg, dstAddr);
uint32_t after = masm.size();
return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after));
}
uint32_t before = masm.size();
if (ins->value()->isConstant()) {
switch (vt) {
case ArrayBufferView::TYPE_INT8: masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
case ArrayBufferView::TYPE_INT8:
case ArrayBufferView::TYPE_UINT8: masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
case ArrayBufferView::TYPE_INT16: masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break;
case ArrayBufferView::TYPE_INT16:
case ArrayBufferView::TYPE_UINT16: masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break;
case ArrayBufferView::TYPE_INT32: masm.movl(Imm32(ToInt32(ins->value())), dstAddr); break;
case ArrayBufferView::TYPE_INT32:
case ArrayBufferView::TYPE_UINT32: masm.movl(Imm32(ToInt32(ins->value())), dstAddr); break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}
} else {
switch (vt) {
case ArrayBufferView::TYPE_INT8: masm.movb(ToRegister(ins->value()), dstAddr); break;
case ArrayBufferView::TYPE_INT8:
case ArrayBufferView::TYPE_UINT8: masm.movb(ToRegister(ins->value()), dstAddr); break;
case ArrayBufferView::TYPE_INT16: masm.movw(ToRegister(ins->value()), dstAddr); break;
case ArrayBufferView::TYPE_INT16:
case ArrayBufferView::TYPE_UINT16: masm.movw(ToRegister(ins->value()), dstAddr); break;
case ArrayBufferView::TYPE_INT32: masm.movl(ToRegister(ins->value()), dstAddr); break;
case ArrayBufferView::TYPE_INT32:
case ArrayBufferView::TYPE_UINT32: masm.movl(ToRegister(ins->value()), dstAddr); break;
case ArrayBufferView::TYPE_FLOAT64: masm.movsd(ToFloatRegister(ins->value()), dstAddr); break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}
}
uint32_t after = masm.size();
return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after));
}
bool

View File

@ -142,21 +142,42 @@ LIRGeneratorX64::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins)
return define(lir, ins);
}
bool
LIRGeneratorX64::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
{
MDefinition *ptr = ins->ptr();
JS_ASSERT(ptr->type() == MIRType_Int32);
// The X64 does not inline an explicit bounds check so has no need to keep the
// index in a register, however only a positive index is accepted because a
// negative offset encoded as an offset in the addressing mode would not wrap
// back into the protected area reserved for the heap.
if (ptr->isConstant() && ptr->toConstant()->value().toInt32() >= 0) {
LAsmJSLoadHeap *lir = new LAsmJSLoadHeap(LAllocation(ptr->toConstant()->vp()));
return define(lir, ins);
}
return define(new LAsmJSLoadHeap(useRegisterAtStart(ptr)), ins);
}
bool
LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
{
MDefinition *ptr = ins->ptr();
JS_ASSERT(ptr->type() == MIRType_Int32);
LAsmJSStoreHeap *lir;
// Note only a positive constant index is accepted because a negative offset
// encoded as an offset in the addressing mode would not wrap back into the
// protected area reserved for the heap.
LAllocation ptrAlloc = useRegisterOrNonNegativeConstantAtStart(ptr);
switch (ins->viewType()) {
case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8:
case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16:
case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32:
lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()),
useRegisterOrConstantAtStart(ins->value()));
lir = new LAsmJSStoreHeap(ptrAlloc, useRegisterOrConstantAtStart(ins->value()));
break;
case ArrayBufferView::TYPE_FLOAT32:
case ArrayBufferView::TYPE_FLOAT64:
lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()),
useRegisterAtStart(ins->value()));
case ArrayBufferView::TYPE_FLOAT32: case ArrayBufferView::TYPE_FLOAT64:
lir = new LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value()));
break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}

View File

@ -40,6 +40,7 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared
bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins);
bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);

View File

@ -534,6 +534,63 @@ class Assembler : public AssemblerX86Shared
masm.movl_mr(addr, index.code(), scale, dest.code());
return masm.currentOffset();
}
// Load from *address where address can be patched.
CodeOffsetLabel movsblWithPatch(const AbsoluteAddress &src, Register dest) {
masm.movsbl_mr(src.addr, dest.code());
return masm.currentOffset();
}
CodeOffsetLabel movzblWithPatch(const AbsoluteAddress &src, Register dest) {
masm.movzbl_mr(src.addr, dest.code());
return masm.currentOffset();
}
CodeOffsetLabel movswlWithPatch(const AbsoluteAddress &src, Register dest) {
masm.movswl_mr(src.addr, dest.code());
return masm.currentOffset();
}
CodeOffsetLabel movzwlWithPatch(const AbsoluteAddress &src, Register dest) {
masm.movzwl_mr(src.addr, dest.code());
return masm.currentOffset();
}
CodeOffsetLabel movlWithPatch(const AbsoluteAddress &src, Register dest) {
masm.movl_mr(src.addr, dest.code());
return masm.currentOffset();
}
CodeOffsetLabel movssWithPatch(const AbsoluteAddress &src, FloatRegister dest) {
JS_ASSERT(HasSSE2());
masm.movss_mr(src.addr, dest.code());
return masm.currentOffset();
}
CodeOffsetLabel movsdWithPatch(const AbsoluteAddress &src, FloatRegister dest) {
JS_ASSERT(HasSSE2());
masm.movsd_mr(src.addr, dest.code());
return masm.currentOffset();
}
// Store to *address where address can be patched.
CodeOffsetLabel movbWithPatch(Register src, const AbsoluteAddress &dest) {
masm.movb_rm(src.code(), dest.addr);
return masm.currentOffset();
}
CodeOffsetLabel movwWithPatch(Register src, const AbsoluteAddress &dest) {
masm.movw_rm(src.code(), dest.addr);
return masm.currentOffset();
}
CodeOffsetLabel movlWithPatch(Register src, const AbsoluteAddress &dest) {
masm.movl_rm(src.code(), dest.addr);
return masm.currentOffset();
}
CodeOffsetLabel movssWithPatch(FloatRegister src, const AbsoluteAddress &dest) {
JS_ASSERT(HasSSE2());
masm.movss_rm(src.code(), dest.addr);
return masm.currentOffset();
}
CodeOffsetLabel movsdWithPatch(FloatRegister src, const AbsoluteAddress &dest) {
JS_ASSERT(HasSSE2());
masm.movsd_rm(src.code(), dest.addr);
return masm.currentOffset();
}
};
// Get a register in which we plan to put a quantity that will be used as an

View File

@ -403,9 +403,10 @@ class jit::OutOfLineLoadTypedArrayOutOfBounds : public OutOfLineCodeBase<CodeGen
bool accept(CodeGeneratorX86 *codegen) { return codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this); }
};
template<typename T>
void
CodeGeneratorX86::loadViewTypeElement(ArrayBufferView::ViewType vt, const Address &srcAddr,
const LDefinition *out)
CodeGeneratorX86::loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
const LDefinition *out)
{
switch (vt) {
case ArrayBufferView::TYPE_INT8: masm.movsblWithPatch(srcAddr, ToRegister(out)); break;
@ -413,13 +414,32 @@ CodeGeneratorX86::loadViewTypeElement(ArrayBufferView::ViewType vt, const Addres
case ArrayBufferView::TYPE_UINT8: masm.movzblWithPatch(srcAddr, ToRegister(out)); break;
case ArrayBufferView::TYPE_INT16: masm.movswlWithPatch(srcAddr, ToRegister(out)); break;
case ArrayBufferView::TYPE_UINT16: masm.movzwlWithPatch(srcAddr, ToRegister(out)); break;
case ArrayBufferView::TYPE_INT32: masm.movlWithPatch(srcAddr, ToRegister(out)); break;
case ArrayBufferView::TYPE_INT32:
case ArrayBufferView::TYPE_UINT32: masm.movlWithPatch(srcAddr, ToRegister(out)); break;
case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(srcAddr, ToFloatRegister(out)); break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}
}
template<typename T>
bool
CodeGeneratorX86::loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
const LDefinition *out)
{
if (vt == ArrayBufferView::TYPE_FLOAT32) {
FloatRegister dest = ToFloatRegister(out);
uint32_t before = masm.size();
masm.movssWithPatch(srcAddr, dest);
uint32_t after = masm.size();
masm.cvtss2sd(dest, dest);
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, AnyRegister(dest)));
}
uint32_t before = masm.size();
loadNonFloat32ViewTypeElement(vt, srcAddr, out);
uint32_t after = masm.size();
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out)));
}
bool
CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins)
{
@ -452,7 +472,7 @@ CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic
masm.bind(ool->rejoin());
return true;
}
loadViewTypeElement(vt, srcAddr, out);
loadNonFloat32ViewTypeElement(vt, srcAddr, out);
if (vt == ArrayBufferView::TYPE_FLOAT64)
masm.canonicalizeDouble(ToFloatRegister(out));
if (ool)
@ -463,23 +483,32 @@ CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic
bool
CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
{
// This is identical to LoadTypedArrayElementStatic, except that the
// array's base and length are not known ahead of time and can be patched
// later on, and the instruction is always infallible.
const MAsmJSLoadHeap *mir = ins->mir();
ArrayBufferView::ViewType vt = mir->viewType();
Register ptr = ToRegister(ins->ptr());
const LAllocation *ptr = ins->ptr();
const LDefinition *out = ins->output();
if (ptr->isConstant()) {
JS_ASSERT(mir->skipBoundsCheck());
int32_t ptrImm = ptr->toConstant()->toInt32();
JS_ASSERT(ptrImm >= 0);
AbsoluteAddress srcAddr((void *) ptrImm);
return loadViewTypeElement(vt, srcAddr, out);
}
Register ptrReg = ToRegister(ptr);
Address srcAddr(ptrReg, 0);
if (mir->skipBoundsCheck())
return loadViewTypeElement(vt, srcAddr, out);
OutOfLineLoadTypedArrayOutOfBounds *ool = new OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out));
if (!addOutOfLineCode(ool))
return false;
CodeOffsetLabel cmp = masm.cmplWithPatch(ptr, Imm32(0));
CodeOffsetLabel cmp = masm.cmplWithPatch(ptrReg, Imm32(0));
masm.j(Assembler::AboveOrEqual, ool->entry());
Address srcAddr(ptr, 0);
if (vt == ArrayBufferView::TYPE_FLOAT32) {
FloatRegister dest = ToFloatRegister(out);
uint32_t before = masm.size();
@ -490,7 +519,7 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, AnyRegister(dest), cmp.offset()));
}
uint32_t before = masm.size();
loadViewTypeElement(vt, srcAddr, out);
loadNonFloat32ViewTypeElement(vt, srcAddr, out);
uint32_t after = masm.size();
masm.bind(ool->rejoin());
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), cmp.offset()));
@ -509,23 +538,42 @@ CodeGeneratorX86::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArra
return true;
}
template<typename T>
void
CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
const Address &dstAddr)
CodeGeneratorX86::storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
const T &dstAddr)
{
switch (vt) {
case ArrayBufferView::TYPE_INT8: masm.movbWithPatch(ToRegister(value), dstAddr); break;
case ArrayBufferView::TYPE_INT8:
case ArrayBufferView::TYPE_UINT8_CLAMPED:
case ArrayBufferView::TYPE_UINT8: masm.movbWithPatch(ToRegister(value), dstAddr); break;
case ArrayBufferView::TYPE_INT16: masm.movwWithPatch(ToRegister(value), dstAddr); break;
case ArrayBufferView::TYPE_INT16:
case ArrayBufferView::TYPE_UINT16: masm.movwWithPatch(ToRegister(value), dstAddr); break;
case ArrayBufferView::TYPE_INT32: masm.movlWithPatch(ToRegister(value), dstAddr); break;
case ArrayBufferView::TYPE_INT32:
case ArrayBufferView::TYPE_UINT32: masm.movlWithPatch(ToRegister(value), dstAddr); break;
case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(ToFloatRegister(value), dstAddr); break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}
}
template<typename T>
bool
CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
const T &dstAddr)
{
if (vt == ArrayBufferView::TYPE_FLOAT32) {
masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
uint32_t before = masm.size();
masm.movssWithPatch(ScratchFloatReg, dstAddr);
uint32_t after = masm.size();
return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
}
uint32_t before = masm.size();
storeNonFloat32ViewTypeElement(vt, value, dstAddr);
uint32_t after = masm.size();
return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
}
bool
CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins)
{
@ -546,7 +594,7 @@ CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStati
masm.bind(&rejoin);
return true;
}
storeViewTypeElement(vt, value, dstAddr);
storeNonFloat32ViewTypeElement(vt, value, dstAddr);
masm.bind(&rejoin);
return true;
}
@ -554,20 +602,29 @@ CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStati
bool
CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
{
// This is identical to StoreTypedArrayElementStatic, except that the
// array's base and length are not known ahead of time and can be patched
// later on.
MAsmJSStoreHeap *mir = ins->mir();
ArrayBufferView::ViewType vt = mir->viewType();
Register ptr = ToRegister(ins->ptr());
const LAllocation *value = ins->value();
const LAllocation *ptr = ins->ptr();
CodeOffsetLabel cmp = masm.cmplWithPatch(ptr, Imm32(0));
if (ptr->isConstant()) {
JS_ASSERT(mir->skipBoundsCheck());
int32_t ptrImm = ptr->toConstant()->toInt32();
JS_ASSERT(ptrImm >= 0);
AbsoluteAddress dstAddr((void *) ptrImm);
return storeViewTypeElement(vt, value, dstAddr);
}
Register ptrReg = ToRegister(ptr);
Address dstAddr(ptrReg, 0);
if (mir->skipBoundsCheck())
return storeViewTypeElement(vt, value, dstAddr);
CodeOffsetLabel cmp = masm.cmplWithPatch(ptrReg, Imm32(0));
Label rejoin;
masm.j(Assembler::AboveOrEqual, &rejoin);
Address dstAddr(ptr, 0);
if (vt == ArrayBufferView::TYPE_FLOAT32) {
masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
uint32_t before = masm.size();
@ -577,7 +634,7 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, cmp.offset()));
}
uint32_t before = masm.size();
storeViewTypeElement(vt, value, dstAddr);
storeNonFloat32ViewTypeElement(vt, value, dstAddr);
uint32_t after = masm.size();
masm.bind(&rejoin);
return gen->noteHeapAccess(AsmJSHeapAccess(before, after, cmp.offset()));

View File

@ -28,10 +28,18 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared
ValueOperand ToOutValue(LInstruction *ins);
ValueOperand ToTempValue(LInstruction *ins, size_t pos);
void loadViewTypeElement(ArrayBufferView::ViewType vt, const Address &srcAddr,
template<typename T>
bool loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
const LDefinition *out);
void storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
const Address &dstAddr);
template<typename T>
void loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
const LDefinition *out);
template<typename T>
bool storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
const T &dstAddr);
template<typename T>
void storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
const T &dstAddr);
void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
const Register &elements, const LAllocation *index);

View File

@ -216,10 +216,53 @@ LIRGeneratorX86::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins)
return define(lir, ins);
}
bool
LIRGeneratorX86::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
{
MDefinition *ptr = ins->ptr();
LAllocation ptrAlloc;
JS_ASSERT(ptr->type() == MIRType_Int32);
// For the x86 it is best to keep the 'ptr' in a register if a bounds check is needed.
if (ptr->isConstant() && ins->skipBoundsCheck()) {
int32_t ptrValue = ptr->toConstant()->value().toInt32();
// A bounds check is only skipped for a positive index.
JS_ASSERT(ptrValue >= 0);
ptrAlloc = LAllocation(ptr->toConstant()->vp());
} else {
ptrAlloc = useRegisterAtStart(ptr);
}
LAsmJSLoadHeap *lir = new LAsmJSLoadHeap(ptrAlloc);
return define(lir, ins);
}
bool
LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
{
MDefinition *ptr = ins->ptr();
LAsmJSStoreHeap *lir;
JS_ASSERT(ptr->type() == MIRType_Int32);
if (ptr->isConstant() && ins->skipBoundsCheck()) {
int32_t ptrValue = ptr->toConstant()->value().toInt32();
JS_ASSERT(ptrValue >= 0);
LAllocation ptrAlloc = LAllocation(ptr->toConstant()->vp());
switch (ins->viewType()) {
case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8:
// See comment below.
lir = new LAsmJSStoreHeap(ptrAlloc, useFixed(ins->value(), eax));
break;
case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16:
case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32:
case ArrayBufferView::TYPE_FLOAT32: case ArrayBufferView::TYPE_FLOAT64:
// See comment below.
lir = new LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value()));
break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}
return add(lir, ins);
}
switch (ins->viewType()) {
case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8:
// It's a trap! On x86, the 1-byte store can only use one of
@ -227,16 +270,14 @@ LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
// gives us one of {edi,esi,ebp,esp}, we're out of luck. (The formatter
// will assert on us.) Ideally, we'd just ask the register allocator to
// give us one of {al,bl,cl,dl}. For now, just useFixed(al).
lir = new LAsmJSStoreHeap(useRegister(ins->ptr()),
useFixed(ins->value(), eax));
lir = new LAsmJSStoreHeap(useRegister(ins->ptr()), useFixed(ins->value(), eax));
break;
case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16:
case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32:
case ArrayBufferView::TYPE_FLOAT32: case ArrayBufferView::TYPE_FLOAT64:
// For now, don't allow constants. The immediate operand affects
// instruction layout which affects patching.
lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()),
useRegisterAtStart(ins->value()));
// For now, don't allow constant values. The immediate operand
// affects instruction layout which affects patching.
lir = new LAsmJSStoreHeap(useRegisterAtStart(ptr), useRegisterAtStart(ins->value()));
break;
default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
}

View File

@ -43,6 +43,7 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins);
bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);

View File

@ -650,6 +650,13 @@ WorkerThread::handleIonWorkload(WorkerThreadState &state)
DebugOnly<jit::ExecutionMode> executionMode = ionBuilder->info().executionMode();
JS_ASSERT(GetIonScript(ionBuilder->script(), executionMode) == ION_COMPILING_SCRIPT);
#if JS_TRACE_LOGGING
AutoTraceLog logger(TraceLogging::getLogger(TraceLogging::ION_BACKGROUND_COMPILER),
TraceLogging::ION_COMPILE_START,
TraceLogging::ION_COMPILE_STOP,
ionBuilder->script());
#endif
state.unlock();
{
jit::IonContext ictx(runtime, ionBuilder->script()->compartment(), &ionBuilder->temp());

View File

@ -166,6 +166,8 @@ XPC_MSG_DEF(NS_ERROR_DNS_LOOKUP_QUEUE_FULL , "The DNS lookup queue is f
XPC_MSG_DEF(NS_ERROR_UNKNOWN_PROXY_HOST , "The lookup of the proxy hostname failed")
XPC_MSG_DEF(NS_ERROR_UNKNOWN_SOCKET_TYPE , "The specified socket type does not exist")
XPC_MSG_DEF(NS_ERROR_SOCKET_CREATE_FAILED , "The specified socket type could not be created")
XPC_MSG_DEF(NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED , "The specified socket address type is not supported")
XPC_MSG_DEF(NS_ERROR_SOCKET_ADDRESS_IN_USE , "Some other socket is already using the specified address.")
XPC_MSG_DEF(NS_ERROR_CACHE_KEY_NOT_FOUND , "Cache key could not be found")
XPC_MSG_DEF(NS_ERROR_CACHE_DATA_IS_STREAM , "Cache data is a stream")
XPC_MSG_DEF(NS_ERROR_CACHE_DATA_IS_NOT_STREAM , "Cache data is not a stream")

View File

@ -29,6 +29,7 @@
#include "nsViewportFrame.h"
#include "nsSVGTextFrame2.h"
#include "nsSVGTextPathFrame.h"
#include "StickyScrollContainer.h"
#include "nsIRootBox.h"
#include "nsIDOMMutationEvent.h"
#include "nsContentUtils.h"
@ -326,7 +327,7 @@ RestyleManager::RecomputePosition(nsIFrame* aFrame)
aFrame->SchedulePaint();
// For relative positioning, we can simply update the frame rect
if (display->mPosition == NS_STYLE_POSITION_RELATIVE) {
if (display->IsRelativelyPositionedStyle()) {
switch (display->mDisplay) {
case NS_STYLE_DISPLAY_TABLE_CAPTION:
case NS_STYLE_DISPLAY_TABLE_CELL:
@ -345,17 +346,27 @@ RestyleManager::RecomputePosition(nsIFrame* aFrame)
}
nsIFrame* cb = aFrame->GetContainingBlock();
const nsSize size = cb->GetContentRectRelativeToSelf().Size();
nsPoint position = aFrame->GetNormalPosition();
nsMargin newOffsets;
// Move the frame
nsHTMLReflowState::ComputeRelativeOffsets(
cb->StyleVisibility()->mDirection,
aFrame, size.width, size.height, newOffsets);
NS_ASSERTION(newOffsets.left == -newOffsets.right &&
newOffsets.top == -newOffsets.bottom,
"ComputeRelativeOffsets should return valid results");
if (display->mPosition == NS_STYLE_POSITION_STICKY) {
StickyScrollContainer::ComputeStickyOffsets(aFrame);
} else {
MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
"Unexpected type of positioning");
const nsSize size = cb->GetContentRectRelativeToSelf().Size();
nsHTMLReflowState::ComputeRelativeOffsets(
cb->StyleVisibility()->mDirection,
aFrame, size.width, size.height, newOffsets);
NS_ASSERTION(newOffsets.left == -newOffsets.right &&
newOffsets.top == -newOffsets.bottom,
"ComputeRelativeOffsets should return valid results");
}
nsPoint position = aFrame->GetNormalPosition();
// This handles both relative and sticky positioning.
nsHTMLReflowState::ApplyRelativePositioning(aFrame, newOffsets, &position);
aFrame->SetPosition(position);

View File

@ -29,6 +29,10 @@ class OverflowChangedTracker
{
public:
OverflowChangedTracker() :
mSubtreeRoot(nullptr)
{}
~OverflowChangedTracker()
{
NS_ASSERTION(mEntryList.empty(), "Need to flush before destroying!");
@ -67,6 +71,15 @@ public:
}
}
/**
* Set the subtree root to limit overflow updates. This must be set if and
* only if currently reflowing aSubtreeRoot, to ensure overflow changes will
* still propagate correctly.
*/
void SetSubtreeRoot(const nsIFrame* aSubtreeRoot) {
mSubtreeRoot = aSubtreeRoot;
}
/**
* Update the overflow of all added frames, and clear the entry list.
*
@ -100,7 +113,7 @@ public:
}
if (updateParent) {
nsIFrame *parent = frame->GetParent();
if (parent) {
if (parent && parent != mSubtreeRoot) {
if (!mEntryList.contains(Entry(parent, entry->mDepth - 1, false))) {
mEntryList.insert(new Entry(parent, entry->mDepth - 1, false));
}
@ -165,6 +178,9 @@ private:
/* A list of frames to process, sorted by their depth in the frame tree */
SplayTree<Entry, Entry> mEntryList;
/* Don't update overflow of this frame or its ancestors. */
const nsIFrame* mSubtreeRoot;
};
class RestyleTracker {

View File

@ -93,6 +93,7 @@ using mozilla::image::ImageOps;
using mozilla::image::Orientation;
#define FLEXBOX_ENABLED_PREF_NAME "layout.css.flexbox.enabled"
#define STICKY_ENABLED_PREF_NAME "layout.css.sticky.enabled"
#ifdef DEBUG
// TODO: remove, see bug 598468.
@ -120,6 +121,11 @@ static int32_t sIndexOfInlineFlexInDisplayTable;
// This tracks whether those ^^ indices have been initialized
static bool sAreFlexKeywordIndicesInitialized = false;
// This is an index into kPositionKTable. It will be initialized
// the first time that StickyEnabledPrefChangeCallback() is invoked.
static int32_t sIndexOfStickyInPositionTable;
static bool sIsStickyKeywordIndexInitialized = false;
typedef nsDataHashtable<nsUint64HashKey, nsIContent*> ContentMap;
static ContentMap* sContentMap = nullptr;
static ContentMap& GetContentMap() {
@ -170,6 +176,38 @@ FlexboxEnabledPrefChangeCallback(const char* aPrefName, void* aClosure)
return 0;
}
// When the pref "layout.css.sticky.enabled" changes, this function is invoked
// to let us update kPositionKTable, to selectively disable or restore the
// entry for "sticky" in that table.
static int
StickyEnabledPrefChangeCallback(const char* aPrefName, void* aClosure)
{
MOZ_ASSERT(strncmp(aPrefName, STICKY_ENABLED_PREF_NAME,
NS_ARRAY_LENGTH(STICKY_ENABLED_PREF_NAME)) == 0,
"We only registered this callback for a single pref, so it "
"should only be called for that pref");
bool isStickyEnabled =
Preferences::GetBool(STICKY_ENABLED_PREF_NAME, false);
if (!sIsStickyKeywordIndexInitialized) {
// First run: find the position of "sticky" in kPositionKTable.
sIndexOfStickyInPositionTable =
nsCSSProps::FindIndexOfKeyword(eCSSKeyword_sticky,
nsCSSProps::kPositionKTable);
MOZ_ASSERT(sIndexOfStickyInPositionTable >= 0,
"Couldn't find sticky in kPositionKTable");
sIsStickyKeywordIndexInitialized = true;
}
// OK -- now, stomp on or restore the "sticky" entry in kPositionKTable,
// depending on whether the sticky pref is enabled vs. disabled.
nsCSSProps::kPositionKTable[sIndexOfStickyInPositionTable] =
isStickyEnabled ? eCSSKeyword_sticky : eCSSKeyword_UNKNOWN;
return 0;
}
template <class AnimationsOrTransitions>
static AnimationsOrTransitions*
HasAnimationOrTransition(nsIContent* aContent,
@ -5002,6 +5040,9 @@ nsLayoutUtils::Initialize()
Preferences::RegisterCallback(FlexboxEnabledPrefChangeCallback,
FLEXBOX_ENABLED_PREF_NAME);
FlexboxEnabledPrefChangeCallback(FLEXBOX_ENABLED_PREF_NAME, nullptr);
Preferences::RegisterCallback(StickyEnabledPrefChangeCallback,
STICKY_ENABLED_PREF_NAME);
StickyEnabledPrefChangeCallback(STICKY_ENABLED_PREF_NAME, nullptr);
}
/* static */
@ -5015,6 +5056,8 @@ nsLayoutUtils::Shutdown()
Preferences::UnregisterCallback(FlexboxEnabledPrefChangeCallback,
FLEXBOX_ENABLED_PREF_NAME);
Preferences::UnregisterCallback(StickyEnabledPrefChangeCallback,
STICKY_ENABLED_PREF_NAME);
}
/* static */

View File

@ -0,0 +1,252 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sts=2 et sw=2 tw=80: */
/* 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/. */
/**
* compute sticky positioning, both during reflow and when the scrolling
* container scrolls
*/
#include "StickyScrollContainer.h"
#include "nsIFrame.h"
#include "nsIScrollableFrame.h"
#include "nsLayoutUtils.h"
#include "RestyleTracker.h"
using namespace mozilla::css;
namespace mozilla {
void DestroyStickyScrollContainer(void* aPropertyValue)
{
delete static_cast<StickyScrollContainer*>(aPropertyValue);
}
NS_DECLARE_FRAME_PROPERTY(StickyScrollContainerProperty,
DestroyStickyScrollContainer)
StickyScrollContainer::StickyScrollContainer(nsIScrollableFrame* aScrollFrame)
: mScrollFrame(aScrollFrame)
, mScrollPosition()
{
mScrollFrame->AddScrollPositionListener(this);
}
StickyScrollContainer::~StickyScrollContainer()
{
mScrollFrame->RemoveScrollPositionListener(this);
}
// static
StickyScrollContainer*
StickyScrollContainer::StickyScrollContainerForFrame(nsIFrame* aFrame)
{
nsIScrollableFrame* scrollFrame =
nsLayoutUtils::GetNearestScrollableFrame(aFrame->GetParent(),
nsLayoutUtils::SCROLLABLE_SAME_DOC |
nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
NS_ASSERTION(scrollFrame, "Need a scrolling container");
FrameProperties props = static_cast<nsIFrame*>(do_QueryFrame(scrollFrame))->
Properties();
StickyScrollContainer* s = static_cast<StickyScrollContainer*>
(props.Get(StickyScrollContainerProperty()));
if (!s) {
s = new StickyScrollContainer(scrollFrame);
props.Set(StickyScrollContainerProperty(), s);
}
return s;
}
// static
StickyScrollContainer*
StickyScrollContainer::GetStickyScrollContainerForScrollFrame(nsIFrame* aFrame)
{
FrameProperties props = aFrame->Properties();
return static_cast<StickyScrollContainer*>
(props.Get(StickyScrollContainerProperty()));
}
static nscoord
ComputeStickySideOffset(Side aSide, const nsStyleSides& aOffset,
nscoord aPercentBasis)
{
if (eStyleUnit_Auto == aOffset.GetUnit(aSide)) {
return NS_AUTOOFFSET;
} else {
return nsLayoutUtils::ComputeCBDependentValue(aPercentBasis,
aOffset.Get(aSide));
}
}
// static
void
StickyScrollContainer::ComputeStickyOffsets(nsIFrame* aFrame)
{
nsIScrollableFrame* scrollableFrame =
nsLayoutUtils::GetNearestScrollableFrame(aFrame->GetParent(),
nsLayoutUtils::SCROLLABLE_SAME_DOC |
nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
if (!scrollableFrame) {
// Not sure how this would happen, but bail if it does.
NS_ERROR("Couldn't find a scrollable frame");
return;
}
nsSize scrollContainerSize = scrollableFrame->GetScrolledFrame()->
GetContentRectRelativeToSelf().Size();
nsMargin computedOffsets;
const nsStylePosition* position = aFrame->StylePosition();
computedOffsets.left = ComputeStickySideOffset(eSideLeft, position->mOffset,
scrollContainerSize.width);
computedOffsets.right = ComputeStickySideOffset(eSideRight, position->mOffset,
scrollContainerSize.width);
computedOffsets.top = ComputeStickySideOffset(eSideTop, position->mOffset,
scrollContainerSize.height);
computedOffsets.bottom = ComputeStickySideOffset(eSideBottom, position->mOffset,
scrollContainerSize.height);
// Store the offset
FrameProperties props = aFrame->Properties();
nsMargin* offsets = static_cast<nsMargin*>
(props.Get(nsIFrame::ComputedStickyOffsetProperty()));
if (offsets) {
*offsets = computedOffsets;
} else {
props.Set(nsIFrame::ComputedStickyOffsetProperty(),
new nsMargin(computedOffsets));
}
}
void
StickyScrollContainer::ComputeStickyLimits(nsIFrame* aFrame, nsRect* aStick,
nsRect* aContain) const
{
aStick->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX);
aContain->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX);
const nsMargin* computedOffsets = static_cast<nsMargin*>(
aFrame->Properties().Get(nsIFrame::ComputedStickyOffsetProperty()));
if (!computedOffsets) {
// We haven't reflowed the scroll frame yet, so offsets haven't been
// computed. Bail.
return;
}
nsIFrame* scrolledFrame = mScrollFrame->GetScrolledFrame();
nsIFrame* cbFrame = aFrame->GetContainingBlock();
NS_ASSERTION(cbFrame == scrolledFrame ||
nsLayoutUtils::IsProperAncestorFrame(scrolledFrame, cbFrame),
"Scroll frame should be an ancestor of the containing block");
nsRect rect = aFrame->GetRect();
nsMargin margin = aFrame->GetUsedMargin();
// Containing block limits
if (cbFrame != scrolledFrame) {
nsMargin cbBorderPadding = cbFrame->GetUsedBorderAndPadding();
aContain->SetRect(nsPoint(cbBorderPadding.left, cbBorderPadding.top) -
aFrame->GetParent()->GetOffsetTo(cbFrame),
cbFrame->GetContentRectRelativeToSelf().Size() -
rect.Size());
aContain->Deflate(margin);
}
nsMargin sfPadding = scrolledFrame->GetUsedPadding();
nsPoint sfOffset = aFrame->GetParent()->GetOffsetTo(scrolledFrame);
// Top
if (computedOffsets->top != NS_AUTOOFFSET) {
aStick->SetTopEdge(mScrollPosition.y + sfPadding.top +
computedOffsets->top - sfOffset.y);
}
nsSize sfSize = scrolledFrame->GetContentRectRelativeToSelf().Size();
// Bottom
if (computedOffsets->bottom != NS_AUTOOFFSET &&
(computedOffsets->top == NS_AUTOOFFSET ||
rect.height <= sfSize.height - computedOffsets->TopBottom())) {
aStick->SetBottomEdge(mScrollPosition.y + sfPadding.top + sfSize.height -
computedOffsets->bottom - rect.height - sfOffset.y);
}
uint8_t direction = cbFrame->StyleVisibility()->mDirection;
// Left
if (computedOffsets->left != NS_AUTOOFFSET &&
(computedOffsets->right == NS_AUTOOFFSET ||
direction == NS_STYLE_DIRECTION_LTR ||
rect.width <= sfSize.width - computedOffsets->LeftRight())) {
aStick->SetLeftEdge(mScrollPosition.x + sfPadding.left +
computedOffsets->left - sfOffset.x);
}
// Right
if (computedOffsets->right != NS_AUTOOFFSET &&
(computedOffsets->left == NS_AUTOOFFSET ||
direction == NS_STYLE_DIRECTION_RTL ||
rect.width <= sfSize.width - computedOffsets->LeftRight())) {
aStick->SetRightEdge(mScrollPosition.x + sfPadding.left + sfSize.width -
computedOffsets->right - rect.width - sfOffset.x);
}
}
nsPoint
StickyScrollContainer::ComputePosition(nsIFrame* aFrame) const
{
nsRect stick;
nsRect contain;
ComputeStickyLimits(aFrame, &stick, &contain);
nsPoint position = aFrame->GetNormalPosition();
// For each sticky direction (top, bottom, left, right), move the frame along
// the appropriate axis, based on the scroll position, but limit this to keep
// the element's margin box within the containing block.
position.y = std::max(position.y, std::min(stick.y, contain.YMost()));
position.y = std::min(position.y, std::max(stick.YMost(), contain.y));
position.x = std::max(position.x, std::min(stick.x, contain.XMost()));
position.x = std::min(position.x, std::max(stick.XMost(), contain.x));
return position;
}
void
StickyScrollContainer::UpdatePositions(nsPoint aScrollPosition,
nsIFrame* aSubtreeRoot)
{
NS_ASSERTION(!aSubtreeRoot || aSubtreeRoot == do_QueryFrame(mScrollFrame),
"If reflowing, should be reflowing the scroll frame");
mScrollPosition = aScrollPosition;
OverflowChangedTracker oct;
oct.SetSubtreeRoot(aSubtreeRoot);
for (nsTArray<nsIFrame*>::size_type i = 0; i < mFrames.Length(); i++) {
nsIFrame* f = mFrames[i];
if (aSubtreeRoot) {
// Reflowing the scroll frame, so recompute offsets.
ComputeStickyOffsets(f);
}
f->SetPosition(ComputePosition(f));
oct.AddFrame(f);
}
oct.Flush();
}
void
StickyScrollContainer::ScrollPositionWillChange(nscoord aX, nscoord aY)
{
}
void
StickyScrollContainer::ScrollPositionDidChange(nscoord aX, nscoord aY)
{
UpdatePositions(nsPoint(aX, aY), nullptr);
}
} // namespace mozilla

View File

@ -0,0 +1,90 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sts=2 et sw=2 tw=80: */
/* 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/. */
/**
* compute sticky positioning, both during reflow and when the scrolling
* container scrolls
*/
#ifndef StickyScrollContainer_h
#define StickyScrollContainer_h
#include "nsPoint.h"
#include "nsTArray.h"
#include "nsIScrollPositionListener.h"
class nsRect;
class nsIFrame;
class nsIScrollableFrame;
namespace mozilla {
class StickyScrollContainer MOZ_FINAL : public nsIScrollPositionListener
{
public:
/**
* Find the StickyScrollContainer associated with the scroll container of
* the given frame, creating it if necessary.
*/
static StickyScrollContainer* StickyScrollContainerForFrame(nsIFrame* aFrame);
/**
* Find the StickyScrollContainer associated with the given scroll frame,
* if it exists.
*/
static StickyScrollContainer* GetStickyScrollContainerForScrollFrame(nsIFrame* aScrollFrame);
void AddFrame(nsIFrame* aFrame) {
mFrames.AppendElement(aFrame);
}
void RemoveFrame(nsIFrame* aFrame) {
mFrames.RemoveElement(aFrame);
}
// Compute the offsets for a sticky position element
static void ComputeStickyOffsets(nsIFrame* aFrame);
/**
* Compute the position of a sticky positioned frame, based on information
* stored in its properties along with our scroll frame and scroll position.
*/
nsPoint ComputePosition(nsIFrame* aFrame) const;
/**
* Compute and set the position of all sticky frames, given the current
* scroll position of the scroll frame. If not in reflow, aSubtreeRoot should
* be null; otherwise, overflow-area updates will be limited to not affect
* aSubtreeRoot or its ancestors.
*/
void UpdatePositions(nsPoint aScrollPosition, nsIFrame* aSubtreeRoot);
// nsIScrollPositionListener
virtual void ScrollPositionWillChange(nscoord aX, nscoord aY) MOZ_OVERRIDE;
virtual void ScrollPositionDidChange(nscoord aX, nscoord aY) MOZ_OVERRIDE;
private:
StickyScrollContainer(nsIScrollableFrame* aScrollFrame);
~StickyScrollContainer();
/**
* Compute two rectangles that determine sticky positioning: |aStick|, based
* on the scroll container, and |aContain|, based on the containing block.
* Sticky positioning keeps the frame position (its upper-left corner) always
* within |aContain| and secondarily within |aStick|.
*/
void ComputeStickyLimits(nsIFrame* aFrame, nsRect* aStick,
nsRect* aContain) const;
friend void DestroyStickyScrollContainer(void* aPropertyValue);
nsIScrollableFrame* const mScrollFrame;
nsTArray<nsIFrame*> mFrames;
nsPoint mScrollPosition;
};
} // namespace mozilla
#endif /* StickyScrollContainer_h */

View File

@ -45,6 +45,7 @@ EXPORTS.mozilla.layout += [
CPP_SOURCES += [
'FrameChildList.cpp',
'ScrollbarActivity.cpp',
'StickyScrollContainer.cpp',
'TextOverflow.cpp',
'nsAbsoluteContainingBlock.cpp',
'nsBRFrame.cpp',

View File

@ -73,6 +73,7 @@
#include "gfxContext.h"
#include "nsRenderingContext.h"
#include "nsAbsoluteContainingBlock.h"
#include "StickyScrollContainer.h"
#include "nsFontInflationData.h"
#include "gfxASurface.h"
#include "nsRegion.h"
@ -510,6 +511,9 @@ nsFrame::Init(nsIContent* aContent,
// property, so we can set this bit here and then ignore it.
mState |= NS_FRAME_MAY_BE_TRANSFORMED;
}
if (disp->mPosition == NS_STYLE_POSITION_STICKY) {
StickyScrollContainer::StickyScrollContainerForFrame(this)->AddFrame(this);
}
if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) || !GetParent()
#ifdef DEBUG
@ -588,6 +592,11 @@ nsFrame::DestroyFrom(nsIFrame* aDestructRoot)
nsSVGEffects::InvalidateDirectRenderingObservers(this);
if (StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY) {
StickyScrollContainer::StickyScrollContainerForFrame(this)->
RemoveFrame(this);
}
// Get the view pointer now before the frame properties disappear
// when we call NotifyDestroyingFrame()
nsView* view = GetView();
@ -2089,7 +2098,8 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
bool isPositioned = disp->IsPositioned(child);
bool isStackingContext =
(isPositioned && pos->mZIndex.GetUnit() == eStyleUnit_Integer) ||
(isPositioned && (disp->mPosition == NS_STYLE_POSITION_STICKY ||
pos->mZIndex.GetUnit() == eStyleUnit_Integer)) ||
isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) ||

View File

@ -48,6 +48,7 @@
#include "nsThemeConstants.h"
#include "nsSVGIntegrationUtils.h"
#include "nsIScrollPositionListener.h"
#include "StickyScrollContainer.h"
#include <algorithm>
#include <cstdlib> // for std::abs(int/long)
#include <cmath> // for std::abs(float/double)
@ -827,6 +828,7 @@ nsHTMLScrollFrame::Reflow(nsPresContext* aPresContext,
state.mContentsOverflowAreas + mInner.mScrolledFrame->GetPosition());
}
mInner.UpdateSticky();
FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus);
if (!InInitialReflow() && !mInner.mHadNonInitialReflow) {
@ -3544,6 +3546,8 @@ nsXULScrollFrame::Layout(nsBoxLayoutState& aState)
mInner.mHadNonInitialReflow = true;
}
mInner.UpdateSticky();
// Set up overflow areas for block frames for the benefit of
// text-overflow.
nsIFrame* f = mInner.mScrolledFrame->GetContentInsertionFrame();
@ -3713,6 +3717,17 @@ nsGfxScrollFrameInner::UpdateOverflow()
return mOuter->nsContainerFrame::UpdateOverflow();
}
void
nsGfxScrollFrameInner::UpdateSticky()
{
StickyScrollContainer* ssc = StickyScrollContainer::
GetStickyScrollContainerForScrollFrame(mOuter);
if (ssc) {
nsIScrollableFrame* scrollFrame = do_QueryFrame(mOuter);
ssc->UpdatePositions(scrollFrame->GetScrollPosition(), mOuter);
}
}
void
nsGfxScrollFrameInner::AdjustScrollbarRectForResizer(
nsIFrame* aFrame, nsPresContext* aPresContext,

View File

@ -276,6 +276,8 @@ public:
bool UpdateOverflow();
void UpdateSticky();
// adjust the scrollbar rectangle aRect to account for any visible resizer.
// aHasResizer specifies if there is a content resizer, however this method
// will also check if a widget resizer is present as well.

View File

@ -23,6 +23,7 @@
#include "nsLayoutUtils.h"
#include "mozilla/Preferences.h"
#include "nsFontInflationData.h"
#include "StickyScrollContainer.h"
#include <algorithm>
#ifdef DEBUG
@ -32,6 +33,7 @@
#endif
using namespace mozilla;
using namespace mozilla::css;
using namespace mozilla::layout;
enum eNormalLineHeightControl {
@ -842,6 +844,9 @@ nsHTMLReflowState::ApplyRelativePositioning(nsIFrame* aFrame,
const nsStyleDisplay* display = aFrame->StyleDisplay();
if (NS_STYLE_POSITION_RELATIVE == display->mPosition) {
*aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top);
} else if (NS_STYLE_POSITION_STICKY == display->mPosition) {
*aPosition = StickyScrollContainer::StickyScrollContainerForFrame(aFrame)->
ComputePosition(aFrame);
}
}
@ -1940,8 +1945,11 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext,
// Compute our offsets if the element is relatively positioned. We need
// the correct containing block width and height here, which is why we need
// to do it after all the quirks-n-such above.
if (mStyleDisplay->IsRelativelyPositioned(frame)) {
// to do it after all the quirks-n-such above. (If the element is sticky
// positioned, we need to wait until the scroll container knows its size,
// so we compute offsets from StickyScrollContainer::UpdatePositions.)
if (mStyleDisplay->IsRelativelyPositioned(frame) &&
NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) {
uint8_t direction = NS_STYLE_DIRECTION_LTR;
if (cbrs && NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) {
direction = NS_STYLE_DIRECTION_RTL;

View File

@ -927,6 +927,7 @@ public:
NS_DECLARE_FRAME_PROPERTY(IBSplitSpecialPrevSibling, nullptr)
NS_DECLARE_FRAME_PROPERTY(NormalPositionProperty, DestroyPoint)
NS_DECLARE_FRAME_PROPERTY(ComputedStickyOffsetProperty, DestroyMargin)
NS_DECLARE_FRAME_PROPERTY(OutlineInnerRectProperty, DestroyRect)
NS_DECLARE_FRAME_PROPERTY(PreEffectsBBoxProperty, DestroyRect)

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
height: 100px;
overflow: hidden;
}
#sticky {
position: relative;
height: 10px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div style="height: 20px"></div>
<div id="sticky"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - bottom, normal position</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="bottom-1-ref.html">
<meta name="assert" content="Bottom-sticky with a small amount of content above: should stay in its normal position">
<style>
#scroll {
height: 100px;
overflow: hidden;
}
#sticky {
position: sticky;
bottom: 10px;
height: 10px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div style="height: 20px"></div>
<div id="sticky"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
height: 100px;
overflow: hidden;
}
#sticky {
position: relative;
height: 10px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div style="height: 80px"></div>
<div id="sticky"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - bottom, normal position</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="bottom-2-ref.html">
<meta name="assert" content="Bottom-sticky with some content above: should stay in its normal position (but will stick beyond this point)">
<style>
#scroll {
height: 100px;
overflow: hidden;
}
#sticky {
position: sticky;
bottom: 10px;
height: 10px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div style="height: 80px"></div>
<div id="sticky"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - bottom, stuck</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="bottom-2-ref.html">
<meta name="assert" content="Bottom-sticky with content above: should stick at the bottom of the scroll container">
<style>
#scroll {
height: 100px;
overflow: hidden;
}
#sticky {
position: sticky;
bottom: 10px;
height: 10px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div style="height: 90px"></div>
<div id="sticky"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<!-- -->
<html>
<head>
<title>CSS Test: Sticky Positioning - bottom, stuck</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="bottom-2-ref.html">
<meta name="assert" content="Bottom-sticky with content above: should stick at the bottom of the scroll container">
<style>
#scroll {
height: 100px;
overflow: hidden;
}
#sticky {
position: sticky;
bottom: 10px;
height: 10px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div style="height: 200px"></div>
<div id="sticky"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
height: 100px;
overflow: hidden;
}
#contain {
height: 200px;
background-color: gray;
}
#sticky {
position: relative;
height: 10px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div style="height: 20px"></div>
<div id="contain">
<div style="height: 60px"></div>
<div id="sticky"></div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<!-- -->
<html>
<head>
<title>CSS Test: Sticky Positioning - bottom, stuck</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="bottom-3-ref.html">
<meta name="assert" content="Bottom-sticky with content above and a containing block: should stick to the bottom of the scroll container">
<style>
#scroll {
height: 100px;
overflow: hidden;
}
#contain {
height: 200px;
background-color: gray;
}
#sticky {
position: sticky;
bottom: 10px;
height: 10px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div style="height: 20px"></div>
<div id="contain">
<div style="height: 100px"></div>
<div id="sticky"></div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
height: 100px;
}
#contain {
height: 5px;
background-color: gray;
padding: 10px 0 0;
border-width: 10px 0 0;
border-style: solid;
border-color: #333;
}
#sticky {
height: 3px;
margin-top: 2px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div style="height: 75px"></div>
<div id="contain">
<div id="sticky"></div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - bottom, contained</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="bottom-3-ref.html">
<meta name="assert" content="Bottom-sticky with too much content above: should stay within the containing block">
<style>
#scroll {
height: 100px;
overflow: hidden;
}
#contain {
height: 200px;
background-color: gray;
padding: 10px 0;
border-width: 10px 0;
border-style: solid;
border-color: #333;
}
#sticky {
position: sticky;
bottom: 10px;
height: 10px;
margin-top: 2px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div style="height: 75px"></div>
<div id="contain">
<div style="height: 100px"></div>
<div id="sticky"></div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
body {
height: 200px;
margin: 0;
}
#sticky {
position: relative;
top: 100px;
height: 10px;
background-color: black;
}
#absolute {
position: absolute;
top: 5px;
left: 5px;
width: 10px;
height: 10px;
background-color: blue;
}
</style>
<body>
<div id="sticky">
<div id="absolute"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - absolute containing block</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="containing-block-1-ref.html">
<meta name="assert" content="Sticky positioned elements should establish a containing block for absolutely positioned descendants">
<style>
body {
height: 200px;
margin: 0;
}
#sticky {
position: sticky;
top: 100px;
height: 10px;
background-color: black;
}
#absolute {
position: absolute;
top: 5px;
left: 5px;
width: 10px;
height: 10px;
background-color: blue;
}
</style>
<body>
<div id="sticky">
<div id="absolute"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
body {
height: 100px;
margin: 0;
/* Without this, we do multiple passes of reflow, and
* the sticky element ends up positioned correctly.
*/
overflow-y: scroll;
}
#sticky {
position: relative;
top: 10px;
height: 10px;
margin-bottom: 10px;
background-color: black;
}
</style>
<body>
<div id="sticky"></div>
</body>
</html>

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - initial reflow</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="initial-1-ref.html">
<meta name="assert" content="Sticky positioning should be calculated correctly on initial page reflow">
<style>
body {
height: 100px;
margin: 0;
/* Without this, we do multiple passes of reflow, and
* the sticky element ends up positioned correctly.
*/
overflow-y: scroll;
}
#sticky {
position: sticky;
top: 10px;
height: 10px;
margin-bottom: 10px;
background-color: black;
}
</style>
<body>
<div id="sticky"></div>
</body>
</html>

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
width: 100px;
height: 100px;
overflow: hidden;
direction: rtl;
}
#inner {
width: 200px;
height: 200px;
direction: ltr;
}
#sticky {
position: relative;
left: 110px;
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div id="inner">
<div id="sticky">
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - initial scroll position</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="initial-scroll-1-ref.html">
<meta name="assert" content="When the initial scroll position of the scroll container is not (0, 0), a sticky element inside it should be positioned correctly">
<style>
#scroll {
width: 100px;
height: 100px;
overflow: hidden;
direction: rtl;
}
#inner {
width: 200px;
height: 200px;
direction: ltr;
}
#sticky {
position: sticky;
left: 10px;
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div id="inner">
<div id="sticky">
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
width: 100px;
height: 100px;
overflow: hidden;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
.fill {
width: 100px;
height: 1px;
}
#contain {
width: 200px;
height: 10px;
background-color: gray;
}
#sticky {
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div class="fill" style="width: 20px"></div
><div id="contain">
<div id="sticky"></div>
</div
><div class="fill"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - left, normal position</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="left-1-ref.html">
<meta name="flags" content="dom">
<meta name="assert" content="Sticky-left with not enough scrolling: should be in its normal position">
<style>
#scroll {
width: 100px;
height: 100px;
overflow: hidden;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
.fill {
width: 100px;
height: 1px;
}
#contain {
width: 200px;
height: 10px;
background-color: gray;
}
#sticky {
position: sticky;
left: 10px;
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div class="fill"></div
><div id="contain">
<div id="sticky"></div>
</div
><div class="fill"></div>
</div>
<script type="text/javascript">
document.getElementById("scroll").scrollLeft = 80;
</script>
</body>
</html>

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
width: 100px;
height: 100px;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
#contain {
width: 100%;
height: 10px;
background-color: gray;
}
#sticky {
position: relative;
left: 10px;
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div id="contain">
<div id="sticky"></div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - left, stuck</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="left-2-ref.html">
<meta name="flags" content="dom">
<meta name="assert" content="Sticky-left with scrolling: should stick to the left edge of the scrolling container">
<style>
#scroll {
width: 100px;
height: 100px;
overflow: hidden;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
.fill {
width: 100px;
height: 1px;
}
#contain {
width: 200px;
height: 10px;
background-color: gray;
}
#sticky {
position: sticky;
left: 10px;
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div class="fill"></div
><div id="contain">
<div id="sticky"></div>
</div
><div class="fill"></div>
</div>
<script type="text/javascript">
document.getElementById("scroll").scrollLeft = 100;
</script>
</body>
</html>

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
width: 100px;
height: 100px;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
.fill {
width: 100px;
height: 1px;
}
#contain {
width: 5px;
height: 10px;
background-color: gray;
}
#sticky {
width: 5px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div id="contain">
<div id="sticky"></div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - left, contained</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="left-3-ref.html">
<meta name="flags" content="dom">
<meta name="assert" content="Sticky-left with too much scrolling: should stay within the containing block">
<style>
#scroll {
width: 100px;
height: 100px;
overflow: hidden;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
.fill {
width: 100px;
height: 1px;
}
#contain {
width: 200px;
height: 10px;
background-color: gray;
}
#sticky {
position: sticky;
left: 10px;
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div class="fill"></div
><div id="contain">
<div id="sticky"></div>
</div
><div class="fill"></div>
</div>
<script type="text/javascript">
document.getElementById("scroll").scrollLeft = 295;
</script>
</body>
</html>

View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
width: 100px;
height: 100px;
overflow: hidden;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
.fill {
width: 100px;
height: 1px;
}
#contain {
width: 200px;
height: 10px;
background-color: gray;
}
#sticky {
position: relative;
right: 20px;
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div class="fill"></div
><div id="sticky"></div
><div class="fill"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,46 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - left+right, at left</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="left-right-1-ref.html">
<meta name="assert" content="Left+right sticky, scrolled to the left: should act as right-sticky">
<style>
#scroll {
width: 100px;
height: 100px;
overflow: hidden;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
.fill {
width: 100px;
height: 1px;
}
#contain {
width: 200px;
height: 10px;
background-color: gray;
}
#sticky {
position: sticky;
left: 10px;
right: 10px;
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div class="fill"></div
><div id="sticky"></div
><div class="fill"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
width: 100px;
height: 100px;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
.fill {
width: 100px;
height: 1px;
}
#sticky {
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div class="fill" style="width: 50px"></div
><div id="sticky"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - left+right, at middle</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="left-right-2-ref.html">
<meta name="flags" content="dom">
<meta name="assert" content="Left+right sticky, scrolled to the middle: should stay in its normal position">
<style>
#scroll {
width: 100px;
height: 100px;
overflow: hidden;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
.fill {
width: 100px;
height: 1px;
}
#contain {
width: 200px;
height: 10px;
background-color: gray;
}
#sticky {
position: sticky;
left: 10px;
right: 10px;
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div class="fill"></div
><div id="sticky"></div
><div class="fill"></div>
</div>
<script type="text/javascript">
document.getElementById("scroll").scrollLeft = 50;
</script>
</body>
</html>

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
width: 100px;
height: 100px;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
.fill {
width: 100px;
height: 1px;
}
#sticky {
position: relative;
left: 10px;
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div id="sticky"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - left+right, at right</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="left-right-3-ref.html">
<meta name="flags" content="dom">
<meta name="assert" content="Left+right sticky, scrolled to the right: should act as left-sticky">
<style>
#scroll {
width: 100px;
height: 100px;
overflow: hidden;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
.fill {
width: 100px;
height: 1px;
}
#contain {
width: 200px;
height: 10px;
background-color: gray;
}
#sticky {
position: sticky;
left: 10px;
right: 10px;
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div class="fill"></div
><div id="sticky"></div
><div class="fill"></div>
</div>
<script type="text/javascript">
document.getElementById("scroll").scrollLeft = 110;
</script>
</body>
</html>

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
height: 100px;
}
#contain {
height: 20px;
background-color: gray;
}
#sticky {
position: relative;
top: 5px;
height: 10px;
margin-bottom: 5px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div id="contain">
<div id="sticky"></div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - element margin</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="margin-1-ref.html">
<meta name="flags" content="dom">
<meta name="assert" content="Sticky-top with margin-bottom on the sticky element: the element's margin box should stay within the containing block">
<style>
#scroll {
height: 100px;
overflow: hidden;
}
#contain {
height: 300px;
background-color: gray;
}
#sticky {
position: sticky;
top: 10px;
height: 10px;
margin-bottom: 5px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div style="height: 20px"></div>
<div id="contain">
<div id="sticky"></div>
</div>
<div style="height: 100px"></div>
</div>
<script type="application/javascript">
document.getElementById("scroll").scrollTop = 300;
</script>
</body>
</html>

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
height: 100px;
overflow: hidden;
}
#sticky {
height: 81px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div style="height: 95px"></div>
<div id="sticky"></div>
<div style="height: 100px"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - top+bottom, overconstrained</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="overconstrained-1-ref.html">
<meta name="assert" content="Top+bottom-sticky with a tall element: should not act as bottom-sticky">
<style>
#scroll {
height: 100px;
overflow: hidden;
}
#sticky {
position: sticky;
top: 10px;
bottom: 10px;
height: 81px;
background-color: black;
}
</style>
</head>
<body>
<div id="scroll">
<div style="height: 95px"></div>
<div id="sticky"></div>
<div style="height: 100px"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
width: 100px;
height: 100px;
overflow: hidden;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
.fill {
width: 99px;
height: 1px;
}
#contain {
width: 200px;
height: 10px;
background-color: gray;
}
#sticky {
width: 81px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div class="fill"></div
><div id="sticky"></div
><div class="fill"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,46 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - left+right, overconstrained</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="overconstrained-2-ref.html">
<meta name="assert" content="Left+right-sticky with a wide element in an LTR container: should not act as right-sticky">
<style>
#scroll {
width: 100px;
height: 100px;
overflow: hidden;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
.fill {
width: 99px;
height: 1px;
}
#contain {
width: 200px;
height: 10px;
background-color: gray;
}
#sticky {
position: sticky;
left: 10px;
right: 10px;
width: 81px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div class="fill"></div
><div id="sticky"></div
><div class="fill"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
width: 100px;
height: 100px;
border: 1px solid black;
white-space: nowrap;
}
#scroll div {
display: inline-block;
}
#sticky {
width: 1px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div id="sticky"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - left+right, overconstrained</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="overconstrained-3-ref.html">
<meta name="flags" content="dom">
<meta name="assert" content="Left+right-sticky with a wide element in an RTL container: should not act as left-sticky">
<style>
#scroll {
width: 100px;
height: 100px;
overflow: hidden;
border: 1px solid black;
white-space: nowrap;
direction: rtl;
}
#scroll div {
display: inline-block;
}
.fill {
width: 99px;
height: 1px;
}
#contain {
width: 200px;
height: 10px;
background-color: gray;
}
#sticky {
position: sticky;
left: 10px;
right: 10px;
width: 81px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div class="fill"></div
><div id="sticky"></div
><div class="fill"></div>
</div>
<script type="text/javascript">
document.getElementById("scroll").scrollLeft = 179;
</script>
</body>
</html>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
body {
margin: 0;
}
#sticky {
height: 10px;
margin-bottom: 10px;
background-color: black;
}
</style>
<body>
<div id="sticky"></div>
</body>
</html>

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - containment and normal position</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="overcontain-1-ref.html">
<meta name="assert" content="Keeping the element's margin box within the containing block's content box should never move the element back past its normal position">
<style>
body {
margin: 0;
}
#sticky {
position: sticky;
top: 10px;
height: 10px;
margin-bottom: 10px;
background-color: black;
}
</style>
<body>
<div id="sticky"></div>
</body>
</html>

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
body {
overflow: scroll;
}
#scroll {
width: 130px;
height: 80px;
overflow: scroll;
border: 10px solid black;
padding: 10px;
}
#inner {
position: relative;
width: 100%;
height: 100%;
}
#sticky {
position: absolute;
width: 10px;
height: 10px;
top: 50%;
left: 50%;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div id="inner">
<div id="sticky">
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - percentage offsets</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="padding-1-ref.html">
<meta name="assert" content="Percentage offsets should be resolved against the content box of the scrolling container">
<style>
body {
/* Make sure it works on the first pass of reflow */
overflow: scroll;
}
#scroll {
width: 130px;
height: 80px;
overflow: scroll;
border: 10px solid black;
padding: 10px;
}
#sticky {
position: sticky;
top: 50%;
left: 50%;
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div id="sticky">
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
#scroll {
width: 80px;
height: 80px;
overflow: hidden;
border: 10px solid black;
padding: 10px;
}
#sticky {
position: relative;
width: 10px;
height: 10px;
top: 20px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div id="sticky">
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - offsets reference</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="padding-2-ref.html">
<meta name="flags" content="dom">
<meta name="assert" content="Offsets should position the element with respect to the content box of the scrolling container">
<style>
#scroll {
width: 80px;
height: 80px;
overflow: hidden;
border: 10px solid black;
padding: 10px;
}
#sticky {
position: sticky;
width: 10px;
height: 10px;
top: 20px;
background-color: black;
}
.fill {
width: 1000px;
height: 1000px;
}
</style>
<body>
<div id="scroll">
<div class="fill"></div>
<div id="sticky"></div>
<div class="fill"></div>
</div>
<script type="application/javascript">
document.getElementById('scroll').scrollTop = 1100;
</script>
</body>
</html>

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
body {
font-size: 0px;
}
#scroll {
width: 80px;
height: 80px;
overflow: scroll;
border: 10px solid black;
padding: 10px;
}
#inner {
position: relative;
width: 100%;
height: 100%;
}
#sticky {
position: absolute;
width: 10px;
height: 10px;
bottom: 20px;
right: 20px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div id="inner">
<div style="width: 100px; height: 100px"></div>
<div id="sticky"></div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<title>CSS Test: Sticky Positioning - offsets reference, bottom/right</title>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<link rel="match" href="padding-3-ref.html">
<meta name="assert" content="Bottom/right offsets should position the element with respect to the content box of the scrolling container">
<style>
body {
font-size: 0px;
}
#scroll {
width: 80px;
height: 80px;
overflow: scroll;
border: 10px solid black;
padding: 10px;
white-space: nowrap;
}
#sticky {
position: sticky;
bottom: 20px;
right: 20px;
display: inline-block;
width: 10px;
height: 10px;
background-color: black;
}
</style>
<body>
<div id="scroll">
<div style="height: 90px; width: 100px"></div>
<div style="display: inline-block; width: 90px; height: 10px"></div
><div id="sticky"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
body {
margin: 0;
line-height: 1;
}
</style>
</head>
<body>
<p>
"position: sticky" is:
<div>disabled</div>
</p>
</body>
</html>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<html>
<head>
<link rel="author" title="Corey Ford" href="mailto:cford@mozilla.com">
<style>
body {
margin: 0;
line-height: 1;
}
</style>
</head>
<body>
<p>
"position: sticky" is:
<div>enabled</div>
</p>
</body>
</html>

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