mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 20:05:49 +00:00
Merge m-c to fx-team.
This commit is contained in:
commit
759170e174
@ -442,8 +442,6 @@ nsPrincipal::GetExtendedOrigin(nsACString& aExtendedOrigin)
|
||||
NS_IMETHODIMP
|
||||
nsPrincipal::GetAppStatus(uint16_t* aAppStatus)
|
||||
{
|
||||
MOZ_ASSERT(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
|
||||
|
||||
*aAppStatus = GetAppStatus();
|
||||
return NS_OK;
|
||||
}
|
||||
@ -569,8 +567,8 @@ nsPrincipal::Write(nsIObjectOutputStream* aStream)
|
||||
uint16_t
|
||||
nsPrincipal::GetAppStatus()
|
||||
{
|
||||
MOZ_ASSERT(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
|
||||
|
||||
NS_WARN_IF_FALSE(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
|
||||
"Asking for app status on a principal with an unknown app id");
|
||||
// Installed apps have a valid app id (not NO_APP_ID or UNKNOWN_APP_ID)
|
||||
// and they are not inside a mozbrowser.
|
||||
if (mAppId == nsIScriptSecurityManager::NO_APP_ID ||
|
||||
|
@ -1953,15 +1953,6 @@ nsDocument::Init()
|
||||
mRadioGroups.Init();
|
||||
mCustomPrototypes.Init();
|
||||
|
||||
// If after creation the owner js global is not set for a document
|
||||
// we use the default compartment for this document, instead of creating
|
||||
// wrapper in some random compartment when the document is exposed to js
|
||||
// via some events.
|
||||
nsCOMPtr<nsIGlobalObject> global = xpc::GetJunkScopeGlobal();
|
||||
NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
|
||||
mScopeObject = do_GetWeakReference(global);
|
||||
MOZ_ASSERT(mScopeObject);
|
||||
|
||||
// Force initialization.
|
||||
nsINode::nsSlots* slots = Slots();
|
||||
|
||||
@ -1992,6 +1983,15 @@ nsDocument::Init()
|
||||
|
||||
NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
|
||||
|
||||
// If after creation the owner js global is not set for a document
|
||||
// we use the default compartment for this document, instead of creating
|
||||
// wrapper in some random compartment when the document is exposed to js
|
||||
// via some events.
|
||||
nsCOMPtr<nsIGlobalObject> global = xpc::GetJunkScopeGlobal();
|
||||
NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
|
||||
mScopeObject = do_GetWeakReference(global);
|
||||
MOZ_ASSERT(mScopeObject);
|
||||
|
||||
mScriptLoader = new nsScriptLoader(this);
|
||||
|
||||
mImageTracker.Init();
|
||||
@ -2545,37 +2545,26 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
}
|
||||
|
||||
// Figure out if we need to apply an app default CSP or a CSP from an app manifest
|
||||
bool applyAppDefaultCSP = false;
|
||||
bool applyAppManifestCSP = false;
|
||||
|
||||
nsIPrincipal* principal = NodePrincipal();
|
||||
|
||||
bool unknownAppId;
|
||||
uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
|
||||
nsAutoString appManifestCSP;
|
||||
if (NS_SUCCEEDED(principal->GetUnknownAppId(&unknownAppId)) &&
|
||||
!unknownAppId &&
|
||||
NS_SUCCEEDED(principal->GetAppStatus(&appStatus))) {
|
||||
applyAppDefaultCSP = ( appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED ||
|
||||
appStatus == nsIPrincipal::APP_STATUS_CERTIFIED);
|
||||
uint16_t appStatus = principal->GetAppStatus();
|
||||
bool applyAppDefaultCSP = appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED ||
|
||||
appStatus == nsIPrincipal::APP_STATUS_CERTIFIED;
|
||||
bool applyAppManifestCSP = false;
|
||||
|
||||
if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) {
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
|
||||
if (appsService) {
|
||||
uint32_t appId = 0;
|
||||
if (NS_SUCCEEDED(principal->GetAppId(&appId))) {
|
||||
appsService->GetCSPByLocalId(appId, appManifestCSP);
|
||||
if (!appManifestCSP.IsEmpty()) {
|
||||
applyAppManifestCSP = true;
|
||||
}
|
||||
nsAutoString appManifestCSP;
|
||||
if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) {
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
|
||||
if (appsService) {
|
||||
uint32_t appId = 0;
|
||||
if (NS_SUCCEEDED(principal->GetAppId(&appId))) {
|
||||
appsService->GetCSPByLocalId(appId, appManifestCSP);
|
||||
if (!appManifestCSP.IsEmpty()) {
|
||||
applyAppManifestCSP = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef PR_LOGGING
|
||||
else
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to get app status from principal"));
|
||||
#endif
|
||||
|
||||
// If there's no CSP to apply, go ahead and return early
|
||||
if (!applyAppDefaultCSP &&
|
||||
|
@ -245,6 +245,7 @@ nsSMILCSSProperty::IsPropertyAnimatable(nsCSSProperty aPropID)
|
||||
case eCSSProperty_stroke_opacity:
|
||||
case eCSSProperty_stroke_width:
|
||||
case eCSSProperty_text_anchor:
|
||||
case eCSSProperty_text_blink:
|
||||
case eCSSProperty_text_decoration:
|
||||
case eCSSProperty_text_decoration_line:
|
||||
case eCSSProperty_text_rendering:
|
||||
|
@ -3540,6 +3540,14 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
||||
JS::Rooted<JSObject*> global(cx);
|
||||
bool defineOnXray = xpc::WrapperFactory::IsXrayWrapper(obj);
|
||||
if (defineOnXray) {
|
||||
// Check whether to define this property on the Xray first. This allows
|
||||
// consumers to opt in to defining on the xray even if they don't want
|
||||
// to define on the underlying global.
|
||||
if (name_struct->mConstructorEnabled &&
|
||||
!(*name_struct->mConstructorEnabled)(cx, obj)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
global = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
|
||||
if (!global) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
@ -3549,36 +3557,36 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
||||
global = obj;
|
||||
}
|
||||
|
||||
// Check whether our constructor is enabled after we unwrap Xrays, since
|
||||
// we don't want to define an interface on the Xray if it's disabled in
|
||||
// the target global, even if it's enabled in the Xray's global.
|
||||
if (name_struct->mConstructorEnabled &&
|
||||
!(*name_struct->mConstructorEnabled)(cx, global)) {
|
||||
// Check whether to define on the global too. Note that at this point cx
|
||||
// is in the compartment of global even if we were coming in via an Xray.
|
||||
bool defineOnGlobal = !name_struct->mConstructorEnabled ||
|
||||
(*name_struct->mConstructorEnabled)(cx, global);
|
||||
|
||||
if (!defineOnGlobal && !defineOnXray) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool enabled;
|
||||
JS::Rooted<JSObject*> interfaceObject(cx, define(cx, global, id, &enabled));
|
||||
if (enabled) {
|
||||
if (!interfaceObject) {
|
||||
JS::Rooted<JSObject*> interfaceObject(cx, define(cx, global, id,
|
||||
defineOnGlobal));
|
||||
if (!interfaceObject) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (defineOnXray) {
|
||||
// This really should be handled by the Xray for the window.
|
||||
ac.destroy();
|
||||
if (!JS_WrapObject(cx, interfaceObject.address()) ||
|
||||
!JS_DefinePropertyById(cx, obj, id,
|
||||
JS::ObjectValue(*interfaceObject), JS_PropertyStub,
|
||||
JS_StrictPropertyStub, 0)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (defineOnXray) {
|
||||
// This really should be handled by the Xray for the window.
|
||||
ac.destroy();
|
||||
if (!JS_WrapObject(cx, interfaceObject.address()) ||
|
||||
!JS_DefinePropertyById(cx, obj, id,
|
||||
JS::ObjectValue(*interfaceObject), JS_PropertyStub,
|
||||
JS_StrictPropertyStub, 0)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
*did_resolve = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*did_resolve = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -371,7 +371,7 @@ CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
JS::Handle<JSObject*> proto,
|
||||
const NativeProperties* properties,
|
||||
const NativeProperties* chromeOnlyProperties,
|
||||
const char* name)
|
||||
const char* name, bool defineOnGlobal)
|
||||
{
|
||||
JS::Rooted<JSObject*> constructor(cx);
|
||||
if (constructorClass) {
|
||||
@ -455,7 +455,7 @@ CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!DefineConstructor(cx, global, name, constructor)) {
|
||||
if (defineOnGlobal && !DefineConstructor(cx, global, name, constructor)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -471,8 +471,9 @@ CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
JS::ObjectValue(*proto), JS_PropertyStub,
|
||||
JS_StrictPropertyStub,
|
||||
JSPROP_PERMANENT | JSPROP_READONLY) ||
|
||||
!DefineConstructor(cx, global, namedConstructors->mName,
|
||||
namedConstructor)) {
|
||||
(defineOnGlobal &&
|
||||
!DefineConstructor(cx, global, namedConstructors->mName,
|
||||
namedConstructor))) {
|
||||
return nullptr;
|
||||
}
|
||||
js::SetReservedSlot(constructor, namedConstructorSlot++,
|
||||
@ -562,7 +563,7 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
JS::Heap<JSObject*>* constructorCache, const DOMClass* domClass,
|
||||
const NativeProperties* properties,
|
||||
const NativeProperties* chromeOnlyProperties,
|
||||
const char* name)
|
||||
const char* name, bool defineOnGlobal)
|
||||
{
|
||||
MOZ_ASSERT(protoClass || constructorClass || constructor,
|
||||
"Need at least one class or a constructor!");
|
||||
@ -612,7 +613,8 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
interface = CreateInterfaceObject(cx, global, constructorProto,
|
||||
constructorClass, constructor,
|
||||
ctorNargs, namedConstructors, proto,
|
||||
properties, chromeOnlyProperties, name);
|
||||
properties, chromeOnlyProperties, name,
|
||||
defineOnGlobal);
|
||||
if (!interface) {
|
||||
if (protoCache) {
|
||||
// If we fail we need to make sure to clear the value of protoCache we
|
||||
|
@ -364,6 +364,12 @@ struct NamedConstructor
|
||||
* on objects in chrome compartments. This must be null if the
|
||||
* interface doesn't have any ChromeOnly properties or if the
|
||||
* object is being created in non-chrome compartment.
|
||||
* defineOnGlobal controls whether properties should be defined on the given
|
||||
* global for the interface object (if any) and named
|
||||
* constructors (if any) for this interface. This can be
|
||||
* false in situations where we want the properties to only
|
||||
* appear on privileged Xrays but not on the unprivileged
|
||||
* underlying global.
|
||||
*
|
||||
* At least one of protoClass, constructorClass or constructor should be
|
||||
* non-null. If constructorClass or constructor are non-null, the resulting
|
||||
@ -380,7 +386,7 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
|
||||
JS::Heap<JSObject*>* constructorCache, const DOMClass* domClass,
|
||||
const NativeProperties* regularProperties,
|
||||
const NativeProperties* chromeOnlyProperties,
|
||||
const char* name);
|
||||
const char* name, bool defineOnGlobal);
|
||||
|
||||
/*
|
||||
* Define the unforgeable attributes on an object.
|
||||
|
@ -597,6 +597,9 @@ class CGHeaders(CGWrapper):
|
||||
dictionary, if passed, to decide what to do with interface types.
|
||||
"""
|
||||
assert not descriptor or not dictionary
|
||||
if t.nullable() and dictionary:
|
||||
# Need to make sure that Nullable as a dictionary member works
|
||||
declareIncludes.add("mozilla/dom/Nullable.h")
|
||||
unrolled = t.unroll()
|
||||
if unrolled.isUnion():
|
||||
# UnionConversions.h includes UnionTypes.h
|
||||
@ -604,7 +607,11 @@ class CGHeaders(CGWrapper):
|
||||
elif unrolled.isInterface():
|
||||
if unrolled.isSpiderMonkeyInterface():
|
||||
bindingHeaders.add("jsfriendapi.h")
|
||||
bindingHeaders.add("mozilla/dom/TypedArray.h")
|
||||
if dictionary:
|
||||
headerSet = declareIncludes
|
||||
else:
|
||||
headerSet = bindingHeaders
|
||||
headerSet.add("mozilla/dom/TypedArray.h")
|
||||
else:
|
||||
providers = getRelevantProviders(descriptor, dictionary,
|
||||
config)
|
||||
@ -1657,7 +1664,8 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
||||
def __init__(self, descriptor, properties):
|
||||
args = [Argument('JSContext*', 'aCx'),
|
||||
Argument('JS::Handle<JSObject*>', 'aGlobal'),
|
||||
Argument('JS::Heap<JSObject*>*', 'protoAndIfaceArray')]
|
||||
Argument('JS::Heap<JSObject*>*', 'aProtoAndIfaceArray'),
|
||||
Argument('bool', 'aDefineOnGlobal')]
|
||||
CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args)
|
||||
self.properties = properties
|
||||
def definition_body(self):
|
||||
@ -1784,13 +1792,13 @@ if (!unforgeableHolder) {
|
||||
|
||||
if needInterfacePrototypeObject:
|
||||
protoClass = "&PrototypeClass.mBase"
|
||||
protoCache = "&protoAndIfaceArray[prototypes::id::%s]" % self.descriptor.name
|
||||
protoCache = "&aProtoAndIfaceArray[prototypes::id::%s]" % self.descriptor.name
|
||||
else:
|
||||
protoClass = "nullptr"
|
||||
protoCache = "nullptr"
|
||||
if needInterfaceObject:
|
||||
interfaceClass = "&InterfaceObjectClass.mBase"
|
||||
interfaceCache = "&protoAndIfaceArray[constructors::id::%s]" % self.descriptor.name
|
||||
interfaceCache = "&aProtoAndIfaceArray[constructors::id::%s]" % self.descriptor.name
|
||||
else:
|
||||
# We don't have slots to store the named constructors.
|
||||
assert len(self.descriptor.interface.namedConstructors) == 0
|
||||
@ -1821,7 +1829,7 @@ if (!unforgeableHolder) {
|
||||
" %s,\n"
|
||||
" %s,\n"
|
||||
" %s,\n"
|
||||
" %s);" % (
|
||||
" %s, aDefineOnGlobal);" % (
|
||||
protoClass, protoCache,
|
||||
interfaceClass, constructHookHolder, constructArgs,
|
||||
namedConstructors,
|
||||
@ -1833,7 +1841,7 @@ if (!unforgeableHolder) {
|
||||
if UseHolderForUnforgeable(self.descriptor):
|
||||
assert needInterfacePrototypeObject
|
||||
setUnforgeableHolder = CGGeneric(
|
||||
"JSObject* proto = protoAndIfaceArray[prototypes::id::%s];\n"
|
||||
"JSObject* proto = aProtoAndIfaceArray[prototypes::id::%s];\n"
|
||||
"if (proto) {\n"
|
||||
" js::SetReservedSlot(proto, DOM_INTERFACE_PROTO_SLOTS_BASE,\n"
|
||||
" JS::ObjectValue(*unforgeableHolder));\n"
|
||||
@ -1851,9 +1859,9 @@ class CGGetPerInterfaceObject(CGAbstractMethod):
|
||||
A method for getting a per-interface object (a prototype object or interface
|
||||
constructor object).
|
||||
"""
|
||||
def __init__(self, descriptor, name, idPrefix=""):
|
||||
def __init__(self, descriptor, name, idPrefix="", extraArgs=[]):
|
||||
args = [Argument('JSContext*', 'aCx'),
|
||||
Argument('JS::Handle<JSObject*>', 'aGlobal')]
|
||||
Argument('JS::Handle<JSObject*>', 'aGlobal')] + extraArgs
|
||||
CGAbstractMethod.__init__(self, descriptor, name,
|
||||
'JS::Handle<JSObject*>', args, inline=True)
|
||||
self.id = idPrefix + "id::" + self.descriptor.name
|
||||
@ -1867,7 +1875,7 @@ class CGGetPerInterfaceObject(CGAbstractMethod):
|
||||
/* Check to see whether the interface objects are already installed */
|
||||
JS::Heap<JSObject*>* protoAndIfaceArray = GetProtoAndIfaceArray(aGlobal);
|
||||
if (!protoAndIfaceArray[%s]) {
|
||||
CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceArray);
|
||||
CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceArray, aDefineOnGlobal);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1890,15 +1898,18 @@ class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
|
||||
def definition_body(self):
|
||||
return """
|
||||
/* Get the interface prototype object for this class. This will create the
|
||||
object as needed. */""" + CGGetPerInterfaceObject.definition_body(self)
|
||||
object as needed. */
|
||||
bool aDefineOnGlobal = true;""" + CGGetPerInterfaceObject.definition_body(self)
|
||||
|
||||
class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
|
||||
"""
|
||||
A method for getting the interface constructor object.
|
||||
"""
|
||||
def __init__(self, descriptor):
|
||||
CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject",
|
||||
"constructors::")
|
||||
CGGetPerInterfaceObject.__init__(
|
||||
self, descriptor, "GetConstructorObject",
|
||||
"constructors::",
|
||||
extraArgs=[Argument("bool", "aDefineOnGlobal", "true")])
|
||||
def definition_body(self):
|
||||
return """
|
||||
/* Get the interface object for this class. This will create the object as
|
||||
@ -1913,7 +1924,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod):
|
||||
args = [Argument('JSContext*', 'aCx'),
|
||||
Argument('JS::Handle<JSObject*>', 'aGlobal'),
|
||||
Argument('JS::Handle<jsid>', 'id'),
|
||||
Argument('bool*', 'aEnabled')]
|
||||
Argument('bool', 'aDefineOnGlobal')]
|
||||
CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'JSObject*', args)
|
||||
|
||||
def declare(self):
|
||||
@ -1928,7 +1939,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod):
|
||||
|
||||
def definition_body(self):
|
||||
if len(self.descriptor.interface.namedConstructors) > 0:
|
||||
getConstructor = """ JSObject* interfaceObject = GetConstructorObject(aCx, aGlobal);
|
||||
getConstructor = """ JSObject* interfaceObject = GetConstructorObject(aCx, aGlobal, aDefineOnGlobal);
|
||||
if (!interfaceObject) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -1940,10 +1951,8 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod):
|
||||
}
|
||||
return interfaceObject;"""
|
||||
else:
|
||||
getConstructor = " return GetConstructorObject(aCx, aGlobal);"
|
||||
return (""" *aEnabled = true;
|
||||
|
||||
""" + getConstructor)
|
||||
getConstructor = " return GetConstructorObject(aCx, aGlobal, aDefineOnGlobal);"
|
||||
return getConstructor
|
||||
|
||||
class CGConstructorEnabledViaPrefEnabled(CGAbstractMethod):
|
||||
"""
|
||||
@ -6098,6 +6107,10 @@ ${methods}
|
||||
private:
|
||||
friend class ${structName}Argument;
|
||||
|
||||
// Disallow copy-construction and assignment
|
||||
${structName}(const ${structName}&) MOZ_DELETE;
|
||||
void operator=(const ${structName}&) MOZ_DELETE;
|
||||
|
||||
${destructors}
|
||||
|
||||
enum Type {
|
||||
@ -6221,6 +6234,10 @@ ${methods}
|
||||
JS::MutableHandle<JS::Value> rval) const;
|
||||
|
||||
private:
|
||||
// Disallow copy-construction and assignment
|
||||
${structName}ReturnValue(const ${structName}ReturnValue&) MOZ_DELETE;
|
||||
void operator=(const ${structName}ReturnValue&) MOZ_DELETE;
|
||||
|
||||
enum Type {
|
||||
eUninitialized,
|
||||
${enumValues}
|
||||
|
@ -1971,7 +1971,8 @@ function isSimpleModifiableElement(node) {
|
||||
if (["A", "FONT", "S", "SPAN", "STRIKE", "U"].indexOf(node.tagName) != -1
|
||||
&& node.hasAttribute("style")
|
||||
&& (node.style.length == 1
|
||||
|| (node.style.length == 3
|
||||
|| (node.style.length == 4
|
||||
&& "MozTextBlink" in node.style
|
||||
&& "MozTextDecorationColor" in node.style
|
||||
&& "MozTextDecorationLine" in node.style
|
||||
&& "MozTextDecorationStyle" in node.style)
|
||||
|
@ -817,24 +817,25 @@ RetrieveTransaction.prototype = Object.create(CancellableTransaction.prototype,
|
||||
this.registerRunCallback(callback);
|
||||
|
||||
this.retryCount = 0;
|
||||
let that = this;
|
||||
this.retrieve((function retryCallback(mmsStatus, msg) {
|
||||
let retryCallback = (function (mmsStatus, msg) {
|
||||
if (MMS.MMS_PDU_STATUS_DEFERRED == mmsStatus &&
|
||||
that.retryCount < PREF_RETRIEVAL_RETRY_COUNT) {
|
||||
if (that.timer == null) {
|
||||
that.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this.retryCount < PREF_RETRIEVAL_RETRY_COUNT) {
|
||||
let time = PREF_RETRIEVAL_RETRY_INTERVALS[this.retryCount];
|
||||
if (DEBUG) debug("Fail to retrieve. Will retry after: " + time);
|
||||
|
||||
if (this.timer == null) {
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
}
|
||||
|
||||
that.timer.initWithCallback((function (){
|
||||
this.retrieve(retryCallback);
|
||||
}).bind(that),
|
||||
PREF_RETRIEVAL_RETRY_INTERVALS[that.retryCount],
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
that.retryCount++;
|
||||
this.timer.initWithCallback(this.retrieve.bind(this, retryCallback),
|
||||
time, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
this.retryCount++;
|
||||
return;
|
||||
}
|
||||
this.runCallbackIfValid(mmsStatus, msg);
|
||||
}).bind(this));
|
||||
}).bind(this);
|
||||
|
||||
this.retrieve(retryCallback);
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
@ -1043,6 +1044,10 @@ SendTransaction.prototype = Object.create(CancellableTransaction.prototype, {
|
||||
if ((MMS.MMS_PDU_ERROR_TRANSIENT_FAILURE == mmsStatus ||
|
||||
MMS.MMS_PDU_ERROR_PERMANENT_FAILURE == mmsStatus) &&
|
||||
this.retryCount < PREF_SEND_RETRY_COUNT) {
|
||||
if (DEBUG) {
|
||||
debug("Fail to send. Will retry after: " + PREF_SEND_RETRY_INTERVAL);
|
||||
}
|
||||
|
||||
if (this.timer == null) {
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
}
|
||||
@ -1400,7 +1405,7 @@ MmsService.prototype = {
|
||||
// Retrieved fail after retry, so we update the delivery status in DB and
|
||||
// notify this domMessage that error happen.
|
||||
gMobileMessageDatabaseService
|
||||
.setMessageDeliveryByMessageId(id,
|
||||
.setMessageDeliveryByMessageId(savableMessage.id,
|
||||
null,
|
||||
null,
|
||||
DELIVERY_STATUS_ERROR,
|
||||
|
@ -13,4 +13,9 @@ LIBRARY_NAME = dompromise_s
|
||||
LIBXUL_LIBRARY = 1
|
||||
FAIL_ON_WARNINGS := 1
|
||||
|
||||
LOCAL_INCLUDES += \
|
||||
-I$(topsrcdir)/dom/workers \
|
||||
-I$(topsrcdir)/dom/base \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include "PromiseCallback.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "nsJSPrincipals.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -110,6 +112,26 @@ Promise::PrefEnabled()
|
||||
return Preferences::GetBool("dom.promise.enabled", false);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Promise::EnabledForScope(JSContext* aCx, JSObject* /* unused */)
|
||||
{
|
||||
// Enable if the pref is enabled or if we're chrome or if we're a
|
||||
// certified app.
|
||||
if (PrefEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note that we have no concept of a certified app in workers.
|
||||
// XXXbz well, why not?
|
||||
if (!NS_IsMainThread()) {
|
||||
return workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker();
|
||||
}
|
||||
|
||||
nsIPrincipal* prin = nsContentUtils::GetSubjectPrincipal();
|
||||
return nsContentUtils::IsSystemPrincipal(prin) ||
|
||||
prin->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
|
||||
}
|
||||
|
||||
static void
|
||||
EnterCompartment(Maybe<JSAutoCompartment>& aAc, JSContext* aCx,
|
||||
const Optional<JS::Handle<JS::Value> >& aValue)
|
||||
@ -125,8 +147,6 @@ EnterCompartment(Maybe<JSAutoCompartment>& aAc, JSContext* aCx,
|
||||
Promise::Constructor(const GlobalObject& aGlobal, JSContext* aCx,
|
||||
PromiseInit& aInit, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(PrefEnabled());
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.Get());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
@ -155,8 +175,6 @@ Promise::Constructor(const GlobalObject& aGlobal, JSContext* aCx,
|
||||
Promise::Resolve(const GlobalObject& aGlobal, JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(PrefEnabled());
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.Get());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
@ -174,8 +192,6 @@ Promise::Resolve(const GlobalObject& aGlobal, JSContext* aCx,
|
||||
Promise::Reject(const GlobalObject& aGlobal, JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(PrefEnabled());
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.Get());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
|
@ -41,6 +41,7 @@ public:
|
||||
~Promise();
|
||||
|
||||
static bool PrefEnabled();
|
||||
static bool EnabledForScope(JSContext* aCx, JSObject* /* unused */);
|
||||
|
||||
// WebIDL
|
||||
|
||||
|
@ -12647,10 +12647,11 @@ let ICCContactHelper = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if contact has additional properties (email, anr, ...etc) need
|
||||
// to be updated as well.
|
||||
// Check if contact has additional properties (email, anr, ...etc) that
|
||||
// need to be updated as well.
|
||||
if ((field === USIM_PBR_EMAIL && !contact.email) ||
|
||||
(field === USIM_PBR_ANR0 && !contact.anr[0])) {
|
||||
(field === USIM_PBR_ANR0 && (!Array.isArray(contact.anr) ||
|
||||
!contact.anr[0]))) {
|
||||
updateField();
|
||||
return;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
* http://dom.spec.whatwg.org/#promises
|
||||
*/
|
||||
|
||||
[Func="mozilla::dom::Promise::EnabledForScope"]
|
||||
interface PromiseResolver {
|
||||
// TODO bug 875289 - void fulfill(optional any value);
|
||||
void resolve(optional any value);
|
||||
@ -16,12 +17,18 @@ interface PromiseResolver {
|
||||
callback PromiseInit = void (PromiseResolver resolver);
|
||||
callback AnyCallback = any (optional any value);
|
||||
|
||||
[PrefControlled, Constructor(PromiseInit init)]
|
||||
[Func="mozilla::dom::Promise::EnabledForScope", Constructor(PromiseInit init)]
|
||||
interface Promise {
|
||||
// TODO bug 875289 - static Promise fulfill(any value);
|
||||
[Creator, Throws]
|
||||
|
||||
// Disable the static methods when the interface object is supposed to be
|
||||
// disabled, just in case some code decides to walk over to .constructor from
|
||||
// the proto of a promise object or someone screws up and manages to create a
|
||||
// Promise object in this scope without having resolved the interface object
|
||||
// first.
|
||||
[Creator, Throws, Func="mozilla::dom::Promise::EnabledForScope"]
|
||||
static Promise resolve(any value); // same as any(value)
|
||||
[Creator, Throws]
|
||||
[Creator, Throws, Func="mozilla::dom::Promise::EnabledForScope"]
|
||||
static Promise reject(any value);
|
||||
|
||||
[Creator]
|
||||
|
@ -856,6 +856,10 @@ class SkipRoot
|
||||
void init(js::ContextFriendFields *cx, const T *ptr, size_t count) {}
|
||||
|
||||
public:
|
||||
~SkipRoot() {
|
||||
// An empty destructor is needed to avoid warnings from clang about
|
||||
// unused local variables of this type.
|
||||
}
|
||||
|
||||
#endif /* DEBUG && JSGC_ROOT_ANALYSIS */
|
||||
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include "jsatominlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using mozilla::DebugOnly;
|
||||
|
||||
using namespace js;
|
||||
|
||||
/*
|
||||
@ -1552,7 +1554,7 @@ StructType::layout(JSContext *cx, HandleObject structType, HandleObject fields)
|
||||
|
||||
// If the field type is a BinaryType and we can't get its bytes, we have a problem.
|
||||
RootedValue fieldTypeBytes(cx);
|
||||
bool r = JSObject::getProperty(cx, fieldType, fieldType, cx->names().bytes, &fieldTypeBytes);
|
||||
DebugOnly<bool> r = JSObject::getProperty(cx, fieldType, fieldType, cx->names().bytes, &fieldTypeBytes);
|
||||
JS_ASSERT(r);
|
||||
|
||||
JS_ASSERT(fieldTypeBytes.isInt32());
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
#include "jsgcinlines.h"
|
||||
|
||||
#include "vm/ObjectImpl-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
|
@ -140,6 +140,44 @@ ion::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
|
||||
return retval;
|
||||
}
|
||||
|
||||
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
|
||||
const IonFrameIterator &frame)
|
||||
: IonFrameIterator(activations),
|
||||
machine_(frame.machineState())
|
||||
{
|
||||
returnAddressToFp_ = frame.returnAddressToFp();
|
||||
topIonScript_ = frame.ionScript();
|
||||
const OsiIndex *osiIndex = frame.osiIndex();
|
||||
|
||||
current_ = (uint8_t *) frame.fp();
|
||||
type_ = IonFrame_OptimizedJS;
|
||||
topFrameSize_ = frame.frameSize();
|
||||
snapshotOffset_ = osiIndex->snapshotOffset();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ion::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame,
|
||||
const ExceptionBailoutInfo &excInfo,
|
||||
BaselineBailoutInfo **bailoutInfo)
|
||||
{
|
||||
JS_ASSERT(cx->isExceptionPending());
|
||||
|
||||
cx->mainThread().ionTop = NULL;
|
||||
JitActivationIterator jitActivations(cx->runtime());
|
||||
IonBailoutIterator iter(jitActivations, frame.frame());
|
||||
JitActivation *activation = jitActivations.activation()->asJit();
|
||||
|
||||
*bailoutInfo = NULL;
|
||||
uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo, &excInfo);
|
||||
JS_ASSERT(retval == BAILOUT_RETURN_OK ||
|
||||
retval == BAILOUT_RETURN_FATAL_ERROR ||
|
||||
retval == BAILOUT_RETURN_OVERRECURSED);
|
||||
|
||||
JS_ASSERT((retval == BAILOUT_RETURN_OK) == (*bailoutInfo != NULL));
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Initialize the decl env Object, call object, and any arguments obj of the current frame.
|
||||
bool
|
||||
ion::EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp)
|
||||
@ -156,19 +194,26 @@ ion::EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp)
|
||||
bool
|
||||
ion::CheckFrequentBailouts(JSContext *cx, JSScript *script)
|
||||
{
|
||||
// Invalidate if this script keeps bailing out without invalidation. Next time
|
||||
// we compile this script LICM will be disabled.
|
||||
if (script->hasIonScript()) {
|
||||
// Invalidate if this script keeps bailing out without invalidation. Next time
|
||||
// we compile this script LICM will be disabled.
|
||||
IonScript *ionScript = script->ionScript();
|
||||
|
||||
if (script->hasIonScript() &&
|
||||
script->ionScript()->numBailouts() >= js_IonOptions.frequentBailoutThreshold &&
|
||||
!script->hadFrequentBailouts)
|
||||
{
|
||||
script->hadFrequentBailouts = true;
|
||||
if (ionScript->numBailouts() >= js_IonOptions.frequentBailoutThreshold &&
|
||||
!script->hadFrequentBailouts)
|
||||
{
|
||||
script->hadFrequentBailouts = true;
|
||||
|
||||
IonSpew(IonSpew_Invalidate, "Invalidating due to too many bailouts");
|
||||
IonSpew(IonSpew_Invalidate, "Invalidating due to too many bailouts");
|
||||
|
||||
if (!Invalidate(cx, script))
|
||||
return false;
|
||||
if (!Invalidate(cx, script))
|
||||
return false;
|
||||
} else {
|
||||
// If we keep bailing out to handle exceptions, invalidate and
|
||||
// forbid compilation.
|
||||
if (ionScript->numExceptionBailouts() >= js_IonOptions.exceptionBailoutThreshold)
|
||||
ForbidCompilation(cx, script);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -125,6 +125,7 @@ class IonBailoutIterator : public IonFrameIterator
|
||||
public:
|
||||
IonBailoutIterator(const JitActivationIterator &activations, BailoutStack *sp);
|
||||
IonBailoutIterator(const JitActivationIterator &activations, InvalidationBailoutStack *sp);
|
||||
IonBailoutIterator(const JitActivationIterator &activations, const IonFrameIterator &frame);
|
||||
|
||||
SnapshotOffset snapshotOffset() const {
|
||||
JS_ASSERT(topIonScript_);
|
||||
@ -157,6 +158,19 @@ uint32_t Bailout(BailoutStack *sp, BaselineBailoutInfo **info);
|
||||
uint32_t InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
|
||||
BaselineBailoutInfo **info);
|
||||
|
||||
struct ExceptionBailoutInfo
|
||||
{
|
||||
size_t frameNo;
|
||||
jsbytecode *resumePC;
|
||||
size_t numExprSlots;
|
||||
};
|
||||
|
||||
// Called from the exception handler to enter a catch or finally block.
|
||||
// Returns a BAILOUT_* error code.
|
||||
uint32_t ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame,
|
||||
const ExceptionBailoutInfo &excInfo,
|
||||
BaselineBailoutInfo **bailoutInfo);
|
||||
|
||||
uint32_t FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo);
|
||||
|
||||
bool CheckFrequentBailouts(JSContext *cx, JSScript *script);
|
||||
|
@ -464,9 +464,16 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
|
||||
HandleFunction fun, HandleScript script, IonScript *ionScript,
|
||||
SnapshotIterator &iter, bool invalidate, BaselineStackBuilder &builder,
|
||||
AutoValueVector &startFrameFormals, MutableHandleFunction nextCallee,
|
||||
jsbytecode **callPC)
|
||||
jsbytecode **callPC, const ExceptionBailoutInfo *excInfo)
|
||||
{
|
||||
uint32_t exprStackSlots = iter.slots() - (script->nfixed + CountArgSlots(script, fun));
|
||||
// If excInfo is non-NULL, we are bailing out to a catch or finally block
|
||||
// and this is the frame where we will resume. Usually the expression stack
|
||||
// should be empty in this case but there can be iterators on the stack.
|
||||
uint32_t exprStackSlots;
|
||||
if (excInfo)
|
||||
exprStackSlots = excInfo->numExprSlots;
|
||||
else
|
||||
exprStackSlots = iter.slots() - (script->nfixed + CountArgSlots(script, fun));
|
||||
|
||||
builder.resetFramePushed();
|
||||
|
||||
@ -641,10 +648,13 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the PC
|
||||
jsbytecode *pc = script->code + iter.pcOffset();
|
||||
// Get the pc. If we are handling an exception, resume at the pc of the
|
||||
// catch or finally block.
|
||||
jsbytecode *pc = excInfo ? excInfo->resumePC : script->code + iter.pcOffset();
|
||||
bool resumeAfter = excInfo ? false : iter.resumeAfter();
|
||||
|
||||
JSOp op = JSOp(*pc);
|
||||
bool resumeAfter = iter.resumeAfter();
|
||||
JS_ASSERT_IF(excInfo, op == JSOP_ENTERBLOCK);
|
||||
|
||||
// Fixup inlined JSOP_FUNCALL, JSOP_FUNAPPLY, and accessors on the caller side.
|
||||
// On the caller side this must represent like the function wasn't inlined.
|
||||
@ -808,8 +818,9 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
|
||||
BailoutKindString(bailoutKind));
|
||||
#endif
|
||||
|
||||
// If this was the last inline frame, then unpacking is almost done.
|
||||
if (!iter.moreFrames()) {
|
||||
// If this was the last inline frame, or we are bailing out to a catch or
|
||||
// finally block in this frame, then unpacking is almost done.
|
||||
if (!iter.moreFrames() || excInfo) {
|
||||
// Last frame, so PC for call to next frame is set to NULL.
|
||||
*callPC = NULL;
|
||||
|
||||
@ -1167,7 +1178,8 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
|
||||
|
||||
uint32_t
|
||||
ion::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter,
|
||||
bool invalidate, BaselineBailoutInfo **bailoutInfo)
|
||||
bool invalidate, BaselineBailoutInfo **bailoutInfo,
|
||||
const ExceptionBailoutInfo *excInfo)
|
||||
{
|
||||
JS_ASSERT(bailoutInfo != NULL);
|
||||
JS_ASSERT(*bailoutInfo == NULL);
|
||||
@ -1214,10 +1226,17 @@ ion::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
|
||||
IonSpew(IonSpew_BaselineBailouts, "Bailing to baseline %s:%u (IonScript=%p) (FrameType=%d)",
|
||||
iter.script()->filename(), iter.script()->lineno, (void *) iter.ionScript(),
|
||||
(int) prevFrameType);
|
||||
|
||||
if (excInfo)
|
||||
IonSpew(IonSpew_BaselineBailouts, "Resuming in catch or finally block");
|
||||
|
||||
IonSpew(IonSpew_BaselineBailouts, " Reading from snapshot offset %u size %u",
|
||||
iter.snapshotOffset(), iter.ionScript()->snapshotsSize());
|
||||
|
||||
iter.ionScript()->incNumBailouts();
|
||||
if (excInfo)
|
||||
iter.ionScript()->incNumExceptionBailouts();
|
||||
else
|
||||
iter.ionScript()->incNumBailouts();
|
||||
iter.script()->updateBaselineOrIonRaw();
|
||||
|
||||
// Allocate buffer to hold stack replacement data.
|
||||
@ -1242,7 +1261,7 @@ ion::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
|
||||
IonSpew(IonSpew_BaselineBailouts, " Not constructing!");
|
||||
|
||||
IonSpew(IonSpew_BaselineBailouts, " Restoring frames:");
|
||||
int frameNo = 0;
|
||||
size_t frameNo = 0;
|
||||
|
||||
// Reconstruct baseline frames using the builder.
|
||||
RootedScript caller(cx);
|
||||
@ -1250,6 +1269,7 @@ ion::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
|
||||
RootedFunction fun(cx, callee);
|
||||
RootedScript scr(cx, iter.script());
|
||||
AutoValueVector startFrameFormals(cx);
|
||||
|
||||
while (true) {
|
||||
#if JS_TRACE_LOGGING
|
||||
if (frameNo > 0) {
|
||||
@ -1258,11 +1278,16 @@ ion::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
|
||||
}
|
||||
#endif
|
||||
IonSpew(IonSpew_BaselineBailouts, " FrameNo %d", frameNo);
|
||||
|
||||
// If we are bailing out to a catch or finally block in this frame,
|
||||
// pass excInfo to InitFromBailout and don't unpack any other frames.
|
||||
bool handleException = (excInfo && excInfo->frameNo == frameNo);
|
||||
|
||||
jsbytecode *callPC = NULL;
|
||||
RootedFunction nextCallee(cx, NULL);
|
||||
if (!InitFromBailout(cx, caller, callerPC, fun, scr, iter.ionScript(),
|
||||
snapIter, invalidate, builder, startFrameFormals,
|
||||
&nextCallee, &callPC))
|
||||
&nextCallee, &callPC, handleException ? excInfo : NULL))
|
||||
{
|
||||
return BAILOUT_RETURN_FATAL_ERROR;
|
||||
}
|
||||
@ -1272,6 +1297,9 @@ ion::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
|
||||
break;
|
||||
}
|
||||
|
||||
if (handleException)
|
||||
break;
|
||||
|
||||
JS_ASSERT(nextCallee);
|
||||
JS_ASSERT(callPC);
|
||||
caller = scr;
|
||||
|
@ -27,7 +27,7 @@ BaselineCompiler::BaselineCompiler(JSContext *cx, HandleScript script)
|
||||
bool
|
||||
BaselineCompiler::init()
|
||||
{
|
||||
if (!analysis_.init())
|
||||
if (!analysis_.init(cx))
|
||||
return false;
|
||||
|
||||
if (!labels_.init(script->length))
|
||||
@ -465,6 +465,13 @@ BaselineCompiler::emitUseCountIncrement()
|
||||
masm.add32(Imm32(1), countReg);
|
||||
masm.store32(countReg, useCountAddr);
|
||||
|
||||
// If this is a loop inside a catch or finally block, increment the use
|
||||
// count but don't attempt OSR (Ion only compiles the try block).
|
||||
if (analysis_.info(pc).loopEntryInCatchOrFinally) {
|
||||
JS_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY);
|
||||
return true;
|
||||
}
|
||||
|
||||
Label skipCall;
|
||||
|
||||
uint32_t minUses = UsesBeforeIonRecompile(script, pc);
|
||||
|
@ -326,7 +326,8 @@ struct BaselineBailoutInfo
|
||||
|
||||
uint32_t
|
||||
BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter,
|
||||
bool invalidate, BaselineBailoutInfo **bailoutInfo);
|
||||
bool invalidate, BaselineBailoutInfo **bailoutInfo,
|
||||
const ExceptionBailoutInfo *exceptionInfo = NULL);
|
||||
|
||||
// Mark baseline scripts on the stack as active, so that they are not discarded
|
||||
// during GC.
|
||||
|
@ -19,8 +19,25 @@ BytecodeAnalysis::BytecodeAnalysis(JSScript *script)
|
||||
{
|
||||
}
|
||||
|
||||
// Bytecode range containing only catch or finally code.
|
||||
struct CatchFinallyRange
|
||||
{
|
||||
uint32_t start; // Inclusive.
|
||||
uint32_t end; // Exclusive.
|
||||
|
||||
CatchFinallyRange(uint32_t start, uint32_t end)
|
||||
: start(start), end(end)
|
||||
{
|
||||
JS_ASSERT(end > start);
|
||||
}
|
||||
|
||||
bool contains(uint32_t offset) const {
|
||||
return start <= offset && offset < end;
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
BytecodeAnalysis::init()
|
||||
BytecodeAnalysis::init(JSContext *cx)
|
||||
{
|
||||
if (!infos_.growByUninitialized(script_->length))
|
||||
return false;
|
||||
@ -31,6 +48,8 @@ BytecodeAnalysis::init()
|
||||
mozilla::PodZero(infos_.begin(), infos_.length());
|
||||
infos_[0].init(/*stackDepth=*/0);
|
||||
|
||||
Vector<CatchFinallyRange, 0, IonAllocPolicy> catchFinallyRanges;
|
||||
|
||||
for (jsbytecode *pc = script_->code; pc < end; pc += GetBytecodeLength(pc)) {
|
||||
JSOp op = JSOp(*pc);
|
||||
unsigned offset = pc - script_->code;
|
||||
@ -59,7 +78,8 @@ BytecodeAnalysis::init()
|
||||
// If stack depth exceeds max allowed by analysis, fail fast.
|
||||
JS_ASSERT(stackDepth <= BytecodeInfo::MAX_STACK_DEPTH);
|
||||
|
||||
if (op == JSOP_TABLESWITCH) {
|
||||
switch (op) {
|
||||
case JSOP_TABLESWITCH: {
|
||||
unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
|
||||
jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
|
||||
int32_t low = GET_JUMP_OFFSET(pc2);
|
||||
@ -78,7 +98,10 @@ BytecodeAnalysis::init()
|
||||
}
|
||||
pc2 += JUMP_OFFSET_LEN;
|
||||
}
|
||||
} else if (op == JSOP_TRY) {
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_TRY: {
|
||||
JSTryNote *tn = script_->trynotes()->vector;
|
||||
JSTryNote *tnlimit = tn + script_->trynotes()->length;
|
||||
for (; tn < tnlimit; tn++) {
|
||||
@ -92,6 +115,37 @@ BytecodeAnalysis::init()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the pc of the last instruction in the try block. It's a JSOP_GOTO to
|
||||
// jump over the catch/finally blocks.
|
||||
jssrcnote *sn = js_GetSrcNote(cx, script_, pc);
|
||||
JS_ASSERT(SN_TYPE(sn) == SRC_TRY);
|
||||
|
||||
jsbytecode *endOfTry = pc + js_GetSrcNoteOffset(sn, 0);
|
||||
JS_ASSERT(JSOp(*endOfTry) == JSOP_GOTO);
|
||||
|
||||
jsbytecode *afterTry = endOfTry + GET_JUMP_OFFSET(endOfTry);
|
||||
JS_ASSERT(afterTry > endOfTry);
|
||||
|
||||
// Pop CatchFinallyRanges that are no longer needed.
|
||||
while (!catchFinallyRanges.empty() && catchFinallyRanges.back().end <= offset)
|
||||
catchFinallyRanges.popBack();
|
||||
|
||||
CatchFinallyRange range(endOfTry - script_->code, afterTry - script_->code);
|
||||
if (!catchFinallyRanges.append(range))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_LOOPENTRY:
|
||||
for (size_t i = 0; i < catchFinallyRanges.length(); i++) {
|
||||
if (catchFinallyRanges[i].contains(offset))
|
||||
infos_[offset].loopEntryInCatchOrFinally = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
bool jump = IsJumpOpcode(op);
|
||||
|
@ -25,6 +25,9 @@ struct BytecodeInfo
|
||||
bool jumpFallthrough : 1;
|
||||
bool fallthrough : 1;
|
||||
|
||||
// If true, this is a JSOP_LOOPENTRY op inside a catch or finally block.
|
||||
bool loopEntryInCatchOrFinally : 1;
|
||||
|
||||
void init(unsigned depth) {
|
||||
JS_ASSERT(depth <= MAX_STACK_DEPTH);
|
||||
JS_ASSERT_IF(initialized, stackDepth == depth);
|
||||
@ -41,7 +44,7 @@ class BytecodeAnalysis
|
||||
public:
|
||||
explicit BytecodeAnalysis(JSScript *script);
|
||||
|
||||
bool init();
|
||||
bool init(JSContext *cx);
|
||||
|
||||
BytecodeInfo &info(jsbytecode *pc) {
|
||||
JS_ASSERT(infos_[pc - script_->code].initialized);
|
||||
|
@ -568,6 +568,7 @@ IonScript::IonScript()
|
||||
invalidateEpilogueOffset_(0),
|
||||
invalidateEpilogueDataOffset_(0),
|
||||
numBailouts_(0),
|
||||
numExceptionBailouts_(0),
|
||||
hasUncompiledCallTarget_(false),
|
||||
hasSPSInstrumentation_(false),
|
||||
runtimeData_(0),
|
||||
@ -995,8 +996,14 @@ OptimizeMIR(MIRGenerator *mir)
|
||||
if (mir->shouldCancel("Dominator Tree"))
|
||||
return false;
|
||||
|
||||
// This must occur before any code elimination.
|
||||
if (!EliminatePhis(mir, graph, AggressiveObservability))
|
||||
// Aggressive phi elimination must occur before any code elimination. If the
|
||||
// script contains a try-statement, we only compiled the try block and not
|
||||
// the catch or finally blocks, so in this case it's also invalid to use
|
||||
// aggressive phi elimination.
|
||||
Observability observability = graph.hasTryBlock()
|
||||
? ConservativeObservability
|
||||
: AggressiveObservability;
|
||||
if (!EliminatePhis(mir, graph, observability))
|
||||
return false;
|
||||
IonSpewPass("Eliminate phis");
|
||||
AssertGraphCoherency(graph);
|
||||
@ -1380,6 +1387,10 @@ IonCompile(JSContext *cx, JSScript *script,
|
||||
if (!script->ensureRanAnalysis(cx))
|
||||
return AbortReason_Alloc;
|
||||
|
||||
// Try-finally is not yet supported.
|
||||
if (script->analysis()->hasTryFinally())
|
||||
return AbortReason_Disable;
|
||||
|
||||
LifoAlloc *alloc = cx->new_<LifoAlloc>(BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
|
||||
if (!alloc)
|
||||
return AbortReason_Alloc;
|
||||
|
@ -124,6 +124,15 @@ struct IonOptions
|
||||
// Default: 10
|
||||
uint32_t frequentBailoutThreshold;
|
||||
|
||||
// Number of exception bailouts (resuming into catch/finally block) before
|
||||
// we invalidate and forbid Ion compilation.
|
||||
//
|
||||
// Default: 10
|
||||
uint32_t exceptionBailoutThreshold;
|
||||
|
||||
// Whether Ion should compile try-catch statements.
|
||||
bool compileTryCatch;
|
||||
|
||||
// How many actual arguments are accepted on the C stack.
|
||||
//
|
||||
// Default: 4,096
|
||||
@ -205,6 +214,8 @@ struct IonOptions
|
||||
usesBeforeInliningFactor(.125),
|
||||
osrPcMismatchesBeforeRecompile(6000),
|
||||
frequentBailoutThreshold(10),
|
||||
exceptionBailoutThreshold(10),
|
||||
compileTryCatch(false),
|
||||
maxStackArgs(4096),
|
||||
maxInlineDepth(3),
|
||||
smallFunctionMaxInlineDepth(10),
|
||||
|
@ -56,6 +56,12 @@ ion::SplitCriticalEdges(MIRGraph &graph)
|
||||
bool
|
||||
ion::EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph)
|
||||
{
|
||||
// If we are compiling try blocks, locals and arguments may be observable
|
||||
// from catch or finally blocks (which Ion does not compile). For now just
|
||||
// disable the pass in this case.
|
||||
if (graph.hasTryBlock())
|
||||
return true;
|
||||
|
||||
for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) {
|
||||
if (mir->shouldCancel("Eliminate Dead Resume Point Operands (main loop)"))
|
||||
return false;
|
||||
|
@ -1162,6 +1162,9 @@ IonBuilder::inspectOpcode(JSOp op)
|
||||
case JSOP_IFEQ:
|
||||
return jsop_ifeq(JSOP_IFEQ);
|
||||
|
||||
case JSOP_TRY:
|
||||
return jsop_try();
|
||||
|
||||
case JSOP_CONDSWITCH:
|
||||
return jsop_condswitch();
|
||||
|
||||
@ -1633,6 +1636,9 @@ IonBuilder::processCfgEntry(CFGState &state)
|
||||
case CFGState::LABEL:
|
||||
return processLabelEnd(state);
|
||||
|
||||
case CFGState::TRY:
|
||||
return processTryEnd(state);
|
||||
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("unknown cfgstate");
|
||||
}
|
||||
@ -2175,6 +2181,30 @@ IonBuilder::processLabelEnd(CFGState &state)
|
||||
return ControlStatus_Joined;
|
||||
}
|
||||
|
||||
IonBuilder::ControlStatus
|
||||
IonBuilder::processTryEnd(CFGState &state)
|
||||
{
|
||||
JS_ASSERT(state.state == CFGState::TRY);
|
||||
|
||||
if (!state.try_.successor) {
|
||||
JS_ASSERT(!current);
|
||||
return ControlStatus_Ended;
|
||||
}
|
||||
|
||||
if (current) {
|
||||
current->end(MGoto::New(state.try_.successor));
|
||||
|
||||
if (!state.try_.successor->addPredecessor(current))
|
||||
return ControlStatus_Error;
|
||||
}
|
||||
|
||||
// Start parsing the code after this try-catch statement.
|
||||
setCurrentAndSpecializePhis(state.try_.successor);
|
||||
graph().moveBlockToEnd(current);
|
||||
pc = current->pc();
|
||||
return ControlStatus_Joined;
|
||||
}
|
||||
|
||||
IonBuilder::ControlStatus
|
||||
IonBuilder::processBreak(JSOp op, jssrcnote *sn)
|
||||
{
|
||||
@ -2861,6 +2891,16 @@ IonBuilder::CFGState::Label(jsbytecode *exitpc)
|
||||
return state;
|
||||
}
|
||||
|
||||
IonBuilder::CFGState
|
||||
IonBuilder::CFGState::Try(jsbytecode *exitpc, MBasicBlock *successor)
|
||||
{
|
||||
CFGState state;
|
||||
state.state = TRY;
|
||||
state.stopAt = exitpc;
|
||||
state.try_.successor = successor;
|
||||
return state;
|
||||
}
|
||||
|
||||
IonBuilder::ControlStatus
|
||||
IonBuilder::processCondSwitchCase(CFGState &state)
|
||||
{
|
||||
@ -3180,6 +3220,82 @@ IonBuilder::jsop_ifeq(JSOp op)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_try()
|
||||
{
|
||||
JS_ASSERT(JSOp(*pc) == JSOP_TRY);
|
||||
|
||||
if (!js_IonOptions.compileTryCatch)
|
||||
return abort("Try-catch support disabled");
|
||||
|
||||
// Try-finally is not yet supported.
|
||||
JS_ASSERT(script()->analysis()->hasTryFinally());
|
||||
|
||||
graph().setHasTryBlock();
|
||||
|
||||
jssrcnote *sn = info().getNote(cx, pc);
|
||||
JS_ASSERT(SN_TYPE(sn) == SRC_TRY);
|
||||
|
||||
// Get the pc of the last instruction in the try block. It's a JSOP_GOTO to
|
||||
// jump over the catch block.
|
||||
jsbytecode *endpc = pc + js_GetSrcNoteOffset(sn, 0);
|
||||
JS_ASSERT(JSOp(*endpc) == JSOP_GOTO);
|
||||
JS_ASSERT(GetJumpOffset(endpc) > 0);
|
||||
|
||||
jsbytecode *afterTry = endpc + GetJumpOffset(endpc);
|
||||
|
||||
// If controlflow in the try body is terminated (by a return or throw
|
||||
// statement), the code after the try-statement may still be reachable
|
||||
// via the catch block (which we don't compile) and OSR can enter it.
|
||||
// For example:
|
||||
//
|
||||
// try {
|
||||
// throw 3;
|
||||
// } catch(e) { }
|
||||
//
|
||||
// for (var i=0; i<1000; i++) {}
|
||||
//
|
||||
// To handle this, we create two blocks: one for the try block and one
|
||||
// for the code following the try-catch statement. Both blocks are
|
||||
// connected to the graph with an MTest instruction that always jumps to
|
||||
// the try block. This ensures the successor block always has a predecessor
|
||||
// and later passes will optimize this MTest to a no-op.
|
||||
//
|
||||
// If the code after the try block is unreachable (control flow in both the
|
||||
// try and catch blocks is terminated), only create the try block, to avoid
|
||||
// parsing unreachable code.
|
||||
|
||||
MBasicBlock *tryBlock = newBlock(current, GetNextPc(pc));
|
||||
if (!tryBlock)
|
||||
return false;
|
||||
|
||||
MBasicBlock *successor;
|
||||
if (script()->analysis()->maybeCode(afterTry)) {
|
||||
successor = newBlock(current, afterTry);
|
||||
if (!successor)
|
||||
return false;
|
||||
|
||||
// Add MTest(true, tryBlock, successorBlock).
|
||||
MConstant *true_ = MConstant::New(BooleanValue(true));
|
||||
current->add(true_);
|
||||
current->end(MTest::New(true_, tryBlock, successor));
|
||||
} else {
|
||||
successor = NULL;
|
||||
current->end(MGoto::New(tryBlock));
|
||||
}
|
||||
|
||||
if (!cfgStack_.append(CFGState::Try(endpc, successor)))
|
||||
return false;
|
||||
|
||||
// The baseline compiler should not attempt to enter the catch block
|
||||
// via OSR.
|
||||
JS_ASSERT(info().osrPc() < endpc || info().osrPc() >= afterTry);
|
||||
|
||||
// Start parsing the try block.
|
||||
setCurrentAndSpecializePhis(tryBlock);
|
||||
return true;
|
||||
}
|
||||
|
||||
IonBuilder::ControlStatus
|
||||
IonBuilder::processReturn(JSOp op)
|
||||
{
|
||||
@ -3220,6 +3336,35 @@ IonBuilder::processThrow()
|
||||
{
|
||||
MDefinition *def = current->pop();
|
||||
|
||||
if (graph().hasTryBlock()) {
|
||||
// MThrow is not marked as effectful. This means when it throws and we
|
||||
// are inside a try block, we could use an earlier resume point and this
|
||||
// resume point may not be up-to-date, for example:
|
||||
//
|
||||
// (function() {
|
||||
// try {
|
||||
// var x = 1;
|
||||
// foo(); // resume point
|
||||
// x = 2;
|
||||
// throw foo;
|
||||
// } catch(e) {
|
||||
// print(x);
|
||||
// }
|
||||
// ])();
|
||||
//
|
||||
// If we use the resume point after the call, this will print 1 instead
|
||||
// of 2. To fix this, we create a resume point right before the MThrow.
|
||||
//
|
||||
// Note that this is not a problem for instructions other than MThrow
|
||||
// because they are either marked as effectful (have their own resume
|
||||
// point) or cannot throw a catchable exception.
|
||||
MNop *ins = MNop::New();
|
||||
current->add(ins);
|
||||
|
||||
if (!resumeAfter(ins))
|
||||
return ControlStatus_Error;
|
||||
}
|
||||
|
||||
MThrow *ins = MThrow::New(def);
|
||||
current->end(ins);
|
||||
|
||||
@ -6372,9 +6517,7 @@ IonBuilder::jsop_getelem()
|
||||
bool cacheable = obj->mightBeType(MIRType_Object) && !obj->mightBeType(MIRType_String) &&
|
||||
(index->mightBeType(MIRType_Int32) || index->mightBeType(MIRType_String));
|
||||
|
||||
bool nonNativeGetElement =
|
||||
script()->analysis()->getCode(pc).nonNativeGetElement ||
|
||||
inspector->hasSeenNonNativeGetElement(pc);
|
||||
bool nonNativeGetElement = inspector->hasSeenNonNativeGetElement(pc);
|
||||
|
||||
// Turn off cacheing if the element is int32 and we've seen non-native objects as the target
|
||||
// of this getelem.
|
||||
|
@ -87,7 +87,8 @@ class IonBuilder : public MIRGenerator
|
||||
COND_SWITCH_CASE, // switch() { case X: ... }
|
||||
COND_SWITCH_BODY, // switch() { case ...: X }
|
||||
AND_OR, // && x, || x
|
||||
LABEL // label: x
|
||||
LABEL, // label: x
|
||||
TRY // try { x } catch(e) { }
|
||||
};
|
||||
|
||||
State state; // Current state of this control structure.
|
||||
@ -169,6 +170,9 @@ class IonBuilder : public MIRGenerator
|
||||
struct {
|
||||
DeferredEdge *breaks;
|
||||
} label;
|
||||
struct {
|
||||
MBasicBlock *successor;
|
||||
} try_;
|
||||
};
|
||||
|
||||
inline bool isLoop() const {
|
||||
@ -192,6 +196,7 @@ class IonBuilder : public MIRGenerator
|
||||
static CFGState TableSwitch(jsbytecode *exitpc, MTableSwitch *ins);
|
||||
static CFGState CondSwitch(jsbytecode *exitpc, jsbytecode *defaultTarget);
|
||||
static CFGState Label(jsbytecode *exitpc);
|
||||
static CFGState Try(jsbytecode *exitpc, MBasicBlock *successor);
|
||||
};
|
||||
|
||||
static int CmpSuccessors(const void *a, const void *b);
|
||||
@ -249,6 +254,7 @@ class IonBuilder : public MIRGenerator
|
||||
ControlStatus processSwitchEnd(DeferredEdge *breaks, jsbytecode *exitpc);
|
||||
ControlStatus processAndOrEnd(CFGState &state);
|
||||
ControlStatus processLabelEnd(CFGState &state);
|
||||
ControlStatus processTryEnd(CFGState &state);
|
||||
ControlStatus processReturn(JSOp op);
|
||||
ControlStatus processThrow();
|
||||
ControlStatus processContinue(JSOp op);
|
||||
@ -381,6 +387,7 @@ class IonBuilder : public MIRGenerator
|
||||
bool jsop_call(uint32_t argc, bool constructing);
|
||||
bool jsop_eval(uint32_t argc);
|
||||
bool jsop_ifeq(JSOp op);
|
||||
bool jsop_try();
|
||||
bool jsop_label();
|
||||
bool jsop_condswitch();
|
||||
bool jsop_andor(JSOp op);
|
||||
|
@ -192,6 +192,10 @@ struct IonScript
|
||||
// Number of times this script bailed out without invalidation.
|
||||
uint32_t numBailouts_;
|
||||
|
||||
// Number of times this scripted bailed out to enter a catch or
|
||||
// finally block.
|
||||
uint32_t numExceptionBailouts_;
|
||||
|
||||
// Flag set when it is likely that one of our (transitive) call
|
||||
// targets is not compiled. Used in ForkJoin.cpp to decide when
|
||||
// we should add call targets to the worklist.
|
||||
@ -410,6 +414,12 @@ struct IonScript
|
||||
bool bailoutExpected() const {
|
||||
return numBailouts_ > 0;
|
||||
}
|
||||
void incNumExceptionBailouts() {
|
||||
numExceptionBailouts_++;
|
||||
}
|
||||
uint32_t numExceptionBailouts() const {
|
||||
return numExceptionBailouts_;
|
||||
}
|
||||
void setHasUncompiledCallTarget() {
|
||||
hasUncompiledCallTarget_ = true;
|
||||
}
|
||||
|
@ -480,6 +480,15 @@ class InlineFrameIteratorMaybeGC
|
||||
|
||||
void resetOn(const IonFrameIterator *iter);
|
||||
|
||||
const IonFrameIterator &frame() const {
|
||||
return *frame_;
|
||||
}
|
||||
|
||||
// Inline frame number, 0 for the outermost (non-inlined) frame.
|
||||
size_t frameNo() const {
|
||||
return start_.frameCount() - framesRead_;
|
||||
}
|
||||
|
||||
private:
|
||||
InlineFrameIteratorMaybeGC() MOZ_DELETE;
|
||||
InlineFrameIteratorMaybeGC(const InlineFrameIteratorMaybeGC &iter) MOZ_DELETE;
|
||||
|
@ -350,7 +350,8 @@ CloseLiveIterator(JSContext *cx, const InlineFrameIterator &frame, uint32_t loca
|
||||
}
|
||||
|
||||
static void
|
||||
CloseLiveIterators(JSContext *cx, const InlineFrameIterator &frame)
|
||||
HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromException *rfe,
|
||||
bool *overrecursed)
|
||||
{
|
||||
RootedScript script(cx, frame.script());
|
||||
jsbytecode *pc = frame.pc();
|
||||
@ -368,20 +369,63 @@ CloseLiveIterators(JSContext *cx, const InlineFrameIterator &frame)
|
||||
if (pcOffset >= tn->start + tn->length)
|
||||
continue;
|
||||
|
||||
if (tn->kind != JSTRY_ITER)
|
||||
continue;
|
||||
switch (tn->kind) {
|
||||
case JSTRY_ITER: {
|
||||
JS_ASSERT(JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER);
|
||||
JS_ASSERT(tn->stackDepth > 0);
|
||||
|
||||
JS_ASSERT(JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER);
|
||||
JS_ASSERT(tn->stackDepth > 0);
|
||||
uint32_t localSlot = tn->stackDepth;
|
||||
CloseLiveIterator(cx, frame, localSlot);
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t localSlot = tn->stackDepth;
|
||||
CloseLiveIterator(cx, frame, localSlot);
|
||||
case JSTRY_LOOP:
|
||||
break;
|
||||
|
||||
case JSTRY_CATCH:
|
||||
if (cx->isExceptionPending()) {
|
||||
// Bailout at the start of the catch block.
|
||||
jsbytecode *catchPC = script->main() + tn->start + tn->length;
|
||||
|
||||
ExceptionBailoutInfo excInfo;
|
||||
excInfo.frameNo = frame.frameNo();
|
||||
excInfo.resumePC = catchPC;
|
||||
excInfo.numExprSlots = tn->stackDepth;
|
||||
|
||||
BaselineBailoutInfo *info = NULL;
|
||||
uint32_t retval = ExceptionHandlerBailout(cx, frame, excInfo, &info);
|
||||
|
||||
if (retval == BAILOUT_RETURN_OK) {
|
||||
JS_ASSERT(info);
|
||||
rfe->kind = ResumeFromException::RESUME_BAILOUT;
|
||||
rfe->target = cx->runtime()->ionRuntime()->getBailoutTail()->raw();
|
||||
rfe->bailoutInfo = info;
|
||||
return;
|
||||
}
|
||||
|
||||
// Bailout failed. If there was a fatal error, clear the
|
||||
// exception to turn this into an uncatchable error. If the
|
||||
// overrecursion check failed, continue popping all inline
|
||||
// frames and have the caller report an overrecursion error.
|
||||
JS_ASSERT(!info);
|
||||
cx->clearPendingException();
|
||||
|
||||
if (retval == BAILOUT_RETURN_OVERRECURSED)
|
||||
*overrecursed = true;
|
||||
else
|
||||
JS_ASSERT(retval == BAILOUT_RETURN_FATAL_ERROR);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Unexpected try note");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
HandleException(JSContext *cx, const IonFrameIterator &frame, ResumeFromException *rfe,
|
||||
bool *calledDebugEpilogue)
|
||||
HandleExceptionBaseline(JSContext *cx, const IonFrameIterator &frame, ResumeFromException *rfe,
|
||||
bool *calledDebugEpilogue)
|
||||
{
|
||||
JS_ASSERT(frame.isBaselineJS());
|
||||
JS_ASSERT(!*calledDebugEpilogue);
|
||||
@ -511,12 +555,15 @@ HandleException(ResumeFromException *rfe)
|
||||
|
||||
IonFrameIterator iter(cx->mainThread().ionTop);
|
||||
while (!iter.isEntry()) {
|
||||
bool overrecursed = false;
|
||||
if (iter.isOptimizedJS()) {
|
||||
// Search each inlined frame for live iterator objects, and close
|
||||
// them.
|
||||
InlineFrameIterator frames(cx, &iter);
|
||||
for (;;) {
|
||||
CloseLiveIterators(cx, frames);
|
||||
HandleExceptionIon(cx, frames, rfe, &overrecursed);
|
||||
if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME)
|
||||
return;
|
||||
|
||||
// When profiling, each frame popped needs a notification that
|
||||
// the function has exited, so invoke the probe that a function
|
||||
@ -536,7 +583,7 @@ HandleException(ResumeFromException *rfe)
|
||||
// It's invalid to call DebugEpilogue twice for the same frame.
|
||||
bool calledDebugEpilogue = false;
|
||||
|
||||
HandleException(cx, iter, rfe, &calledDebugEpilogue);
|
||||
HandleExceptionBaseline(cx, iter, rfe, &calledDebugEpilogue);
|
||||
if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME)
|
||||
return;
|
||||
|
||||
@ -575,6 +622,11 @@ HandleException(ResumeFromException *rfe)
|
||||
EnsureExitFrame(current);
|
||||
cx->mainThread().ionTop = (uint8_t *)current;
|
||||
}
|
||||
|
||||
if (overrecursed) {
|
||||
// We hit an overrecursion error during bailout. Report it now.
|
||||
js_ReportOverRecursed(cx);
|
||||
}
|
||||
}
|
||||
|
||||
rfe->stackPointer = iter.fp();
|
||||
|
@ -255,6 +255,8 @@ class FrameSizeClass
|
||||
}
|
||||
};
|
||||
|
||||
struct BaselineBailoutInfo;
|
||||
|
||||
// Data needed to recover from an exception.
|
||||
struct ResumeFromException
|
||||
{
|
||||
@ -262,6 +264,7 @@ struct ResumeFromException
|
||||
static const uint32_t RESUME_CATCH = 1;
|
||||
static const uint32_t RESUME_FINALLY = 2;
|
||||
static const uint32_t RESUME_FORCED_RETURN = 3;
|
||||
static const uint32_t RESUME_BAILOUT = 4;
|
||||
|
||||
uint8_t *framePointer;
|
||||
uint8_t *stackPointer;
|
||||
@ -270,6 +273,8 @@ struct ResumeFromException
|
||||
|
||||
// Value to push when resuming into a |finally| block.
|
||||
Value exception;
|
||||
|
||||
BaselineBailoutInfo *bailoutInfo;
|
||||
};
|
||||
|
||||
void HandleException(ResumeFromException *rfe);
|
||||
|
@ -545,6 +545,7 @@ class MIRGraph
|
||||
Vector<JSScript *, 4, IonAllocPolicy> scripts_;
|
||||
|
||||
size_t numBlocks_;
|
||||
bool hasTryBlock_;
|
||||
|
||||
public:
|
||||
MIRGraph(TempAllocator *alloc)
|
||||
@ -554,7 +555,8 @@ class MIRGraph
|
||||
idGen_(0),
|
||||
osrBlock_(NULL),
|
||||
osrStart_(NULL),
|
||||
numBlocks_(0)
|
||||
numBlocks_(0),
|
||||
hasTryBlock_(false)
|
||||
{ }
|
||||
|
||||
template <typename T>
|
||||
@ -677,6 +679,13 @@ class MIRGraph
|
||||
return scripts_.begin();
|
||||
}
|
||||
|
||||
bool hasTryBlock() const {
|
||||
return hasTryBlock_;
|
||||
}
|
||||
void setHasTryBlock() {
|
||||
hasTryBlock_ = true;
|
||||
}
|
||||
|
||||
// The per-thread context. So as not to modify the calling convention for
|
||||
// parallel code, we obtain the current slice from thread-local storage.
|
||||
// This helper method will lazilly insert an MForkJoinSlice instruction in
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
#include "ion/Bailouts.h"
|
||||
#include "ion/BaselineFrame.h"
|
||||
#include "ion/MoveEmitter.h"
|
||||
|
||||
@ -3330,12 +3331,14 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail()
|
||||
Label catch_;
|
||||
Label finally;
|
||||
Label return_;
|
||||
Label bailout;
|
||||
|
||||
ma_ldr(Operand(sp, offsetof(ResumeFromException, kind)), r0);
|
||||
branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
|
||||
branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_CATCH), &catch_);
|
||||
branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FINALLY), &finally);
|
||||
branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_);
|
||||
branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout);
|
||||
|
||||
breakpoint(); // Invalid kind.
|
||||
|
||||
@ -3380,6 +3383,14 @@ MacroAssemblerARMCompat::handleFailureWithHandlerTail()
|
||||
ma_mov(r11, sp);
|
||||
pop(r11);
|
||||
ret();
|
||||
|
||||
// If we are bailing out to baseline to handle an exception, jump to
|
||||
// the bailout tail stub.
|
||||
bind(&bailout);
|
||||
ma_ldr(Operand(sp, offsetof(ResumeFromException, bailoutInfo)), r2);
|
||||
ma_mov(Imm32(BAILOUT_RETURN_OK), r0);
|
||||
ma_ldr(Operand(sp, offsetof(ResumeFromException, target)), r1);
|
||||
jump(r1);
|
||||
}
|
||||
|
||||
Assembler::Condition
|
||||
|
@ -376,9 +376,10 @@ CodeGeneratorX86Shared::visitMinMaxD(LMinMaxD *ins)
|
||||
{
|
||||
FloatRegister first = ToFloatRegister(ins->first());
|
||||
FloatRegister second = ToFloatRegister(ins->second());
|
||||
#ifdef DEBUG
|
||||
FloatRegister output = ToFloatRegister(ins->output());
|
||||
|
||||
JS_ASSERT(first == output);
|
||||
#endif
|
||||
|
||||
Label done, nan, minMaxInst;
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "mozilla/Casting.h"
|
||||
|
||||
#include "ion/Bailouts.h"
|
||||
#include "ion/BaselineFrame.h"
|
||||
#include "ion/IonFrames.h"
|
||||
#include "ion/MoveEmitter.h"
|
||||
@ -253,12 +254,14 @@ MacroAssemblerX64::handleFailureWithHandlerTail()
|
||||
Label catch_;
|
||||
Label finally;
|
||||
Label return_;
|
||||
Label bailout;
|
||||
|
||||
loadPtr(Address(rsp, offsetof(ResumeFromException, kind)), rax);
|
||||
branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
|
||||
branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_CATCH), &catch_);
|
||||
branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FINALLY), &finally);
|
||||
branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_);
|
||||
branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout);
|
||||
|
||||
breakpoint(); // Invalid kind.
|
||||
|
||||
@ -300,6 +303,13 @@ MacroAssemblerX64::handleFailureWithHandlerTail()
|
||||
movq(rbp, rsp);
|
||||
pop(rbp);
|
||||
ret();
|
||||
|
||||
// If we are bailing out to baseline to handle an exception, jump to
|
||||
// the bailout tail stub.
|
||||
bind(&bailout);
|
||||
movq(Operand(esp, offsetof(ResumeFromException, bailoutInfo)), r9);
|
||||
movl(Imm32(BAILOUT_RETURN_OK), rax);
|
||||
jmp(Operand(rsp, offsetof(ResumeFromException, target)));
|
||||
}
|
||||
|
||||
Assembler::Condition
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "mozilla/Casting.h"
|
||||
|
||||
#include "ion/Bailouts.h"
|
||||
#include "ion/BaselineFrame.h"
|
||||
#include "ion/IonFrames.h"
|
||||
#include "ion/MoveEmitter.h"
|
||||
@ -225,12 +226,14 @@ MacroAssemblerX86::handleFailureWithHandlerTail()
|
||||
Label catch_;
|
||||
Label finally;
|
||||
Label return_;
|
||||
Label bailout;
|
||||
|
||||
loadPtr(Address(esp, offsetof(ResumeFromException, kind)), eax);
|
||||
branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
|
||||
branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_CATCH), &catch_);
|
||||
branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_FINALLY), &finally);
|
||||
branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_);
|
||||
branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout);
|
||||
|
||||
breakpoint(); // Invalid kind.
|
||||
|
||||
@ -272,6 +275,13 @@ MacroAssemblerX86::handleFailureWithHandlerTail()
|
||||
movl(ebp, esp);
|
||||
pop(ebp);
|
||||
ret();
|
||||
|
||||
// If we are bailing out to baseline to handle an exception, jump to
|
||||
// the bailout tail stub.
|
||||
bind(&bailout);
|
||||
movl(Operand(esp, offsetof(ResumeFromException, bailoutInfo)), ecx);
|
||||
movl(Imm32(BAILOUT_RETURN_OK), eax);
|
||||
jmp(Operand(esp, offsetof(ResumeFromException, target)));
|
||||
}
|
||||
|
||||
void
|
||||
|
12
js/src/jit-test/tests/ion/try-catch-1.js
Normal file
12
js/src/jit-test/tests/ion/try-catch-1.js
Normal file
@ -0,0 +1,12 @@
|
||||
function F() {
|
||||
try {
|
||||
var T = {};
|
||||
throw 12;
|
||||
} catch (e) {
|
||||
// Don't throw.
|
||||
T.x = 5;
|
||||
}
|
||||
}
|
||||
F();
|
||||
F();
|
||||
F();
|
14
js/src/jit-test/tests/ion/try-catch-2.js
Normal file
14
js/src/jit-test/tests/ion/try-catch-2.js
Normal file
@ -0,0 +1,14 @@
|
||||
// Control flow does not reach end of try block, code after try statement is
|
||||
// reachable by catch block.
|
||||
function f() {
|
||||
try {
|
||||
throw 3;
|
||||
} catch(e) {
|
||||
}
|
||||
|
||||
var res = 0;
|
||||
for (var i=0; i<40; i++)
|
||||
res += 2;
|
||||
return res;
|
||||
}
|
||||
assertEq(f(), 80);
|
27
js/src/jit-test/tests/ion/try-catch-3.js
Normal file
27
js/src/jit-test/tests/ion/try-catch-3.js
Normal file
@ -0,0 +1,27 @@
|
||||
// Don't fail if code after try statement is unreachable.
|
||||
function f() {
|
||||
try {
|
||||
throw 1;
|
||||
} catch(e) {
|
||||
throw 5;
|
||||
}
|
||||
|
||||
// Unreachable.
|
||||
assertEq(0, 2);
|
||||
var res = 0;
|
||||
for (var i=0; i<10; i++)
|
||||
res += 2;
|
||||
return res;
|
||||
}
|
||||
|
||||
var c = 0;
|
||||
|
||||
for (var i=0; i<5; i++) {
|
||||
try {
|
||||
f();
|
||||
assertEq(0, 1);
|
||||
} catch(e) {
|
||||
c += e;
|
||||
}
|
||||
}
|
||||
assertEq(c, 25);
|
15
js/src/jit-test/tests/ion/try-catch-4.js
Normal file
15
js/src/jit-test/tests/ion/try-catch-4.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Entering catch blocks via OSR is not possible (because the catch block
|
||||
// is not compiled by Ion). Don't crash.
|
||||
function f() {
|
||||
var res = 0;
|
||||
try {
|
||||
throw 1;
|
||||
} catch(e) {
|
||||
for (var i=0; i<10; i++) {
|
||||
res += 3;
|
||||
}
|
||||
}
|
||||
|
||||
assertEq(res, 30);
|
||||
}
|
||||
f();
|
@ -104,8 +104,6 @@ class Bytecode
|
||||
* hints about the script for use during compilation.
|
||||
*/
|
||||
bool arrayWriteHole: 1; /* SETELEM which has written to an array hole. */
|
||||
bool getStringElement:1; /* GETELEM which has accessed string properties. */
|
||||
bool nonNativeGetElement:1; /* GETELEM on a non-native, non-array object. */
|
||||
bool accessGetter: 1; /* Property read on a shape with a getter hook. */
|
||||
|
||||
/* Stack depth before this opcode. */
|
||||
|
@ -433,6 +433,10 @@ class AutoLockForExclusiveAccess
|
||||
AutoLockForExclusiveAccess(JSRuntime *rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
~AutoLockForExclusiveAccess() {
|
||||
// An empty destructor is needed to avoid warnings from clang about
|
||||
// unused local variables of this type.
|
||||
}
|
||||
#endif // JS_THREADSAFE
|
||||
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
|
@ -507,7 +507,7 @@ Class JSFunction::class_ = {
|
||||
fun_trace
|
||||
};
|
||||
|
||||
JS_FRIEND_DATA(Class* const) js::FunctionClassPtr = &JSFunction::class_;
|
||||
Class* const js::FunctionClassPtr = &JSFunction::class_;
|
||||
|
||||
/* Find the body of a function (not including braces). */
|
||||
static bool
|
||||
|
@ -80,7 +80,7 @@ Class JSObject::class_ = {
|
||||
JS_ConvertStub
|
||||
};
|
||||
|
||||
JS_FRIEND_DATA(Class* const) js::ObjectClassPtr = &JSObject::class_;
|
||||
Class* const js::ObjectClassPtr = &JSObject::class_;
|
||||
|
||||
JS_FRIEND_API(JSObject *)
|
||||
JS_ObjectToInnerObject(JSContext *cx, JSObject *objArg)
|
||||
|
@ -3133,7 +3133,7 @@ Class js::ObjectProxyObject::class_ = {
|
||||
}
|
||||
};
|
||||
|
||||
JS_FRIEND_DATA(Class* const) js::ObjectProxyClassPtr = &ObjectProxyObject::class_;
|
||||
Class* const js::ObjectProxyClassPtr = &ObjectProxyObject::class_;
|
||||
|
||||
Class js::OuterWindowProxyObject::class_ = {
|
||||
"Proxy",
|
||||
@ -3192,7 +3192,7 @@ Class js::OuterWindowProxyObject::class_ = {
|
||||
}
|
||||
};
|
||||
|
||||
JS_FRIEND_DATA(Class* const) js::OuterWindowProxyClassPtr = &OuterWindowProxyObject::class_;
|
||||
Class* const js::OuterWindowProxyClassPtr = &OuterWindowProxyObject::class_;
|
||||
|
||||
static bool
|
||||
proxy_Call(JSContext *cx, unsigned argc, Value *vp)
|
||||
@ -3263,7 +3263,7 @@ Class js::FunctionProxyObject::class_ = {
|
||||
}
|
||||
};
|
||||
|
||||
JS_FRIEND_DATA(Class* const) js::FunctionProxyClassPtr = &FunctionProxyObject::class_;
|
||||
Class* const js::FunctionProxyClassPtr = &FunctionProxyObject::class_;
|
||||
|
||||
/* static */ ProxyObject *
|
||||
ProxyObject::New(JSContext *cx, BaseProxyHandler *handler, HandleValue priv, TaggedProto proto_,
|
||||
|
@ -2009,6 +2009,12 @@ JSScript::isShortRunning()
|
||||
!analysis()->hasFunctionCalls();
|
||||
}
|
||||
|
||||
js::GlobalObject&
|
||||
JSScript::uninlinedGlobal() const
|
||||
{
|
||||
return global();
|
||||
}
|
||||
|
||||
bool
|
||||
JSScript::enclosingScriptsCompiledSuccessfully() const
|
||||
{
|
||||
|
@ -809,6 +809,7 @@ class JSScript : public js::gc::Cell
|
||||
inline void clearPropertyReadTypes();
|
||||
|
||||
inline js::GlobalObject &global() const;
|
||||
js::GlobalObject &uninlinedGlobal() const;
|
||||
|
||||
/* See StaticScopeIter comment. */
|
||||
JSObject *enclosingStaticScope() const {
|
||||
|
@ -289,8 +289,10 @@ class AutoUnlockWorkerThreadState
|
||||
/* Pause any threads that are running jobs off thread during GC activity. */
|
||||
class AutoPauseWorkersForGC
|
||||
{
|
||||
#ifdef JS_WORKER_THREADS
|
||||
JSRuntime *runtime;
|
||||
bool needsUnpause;
|
||||
#endif
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
|
||||
public:
|
||||
|
@ -5141,6 +5141,9 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
|
||||
if (op->getBoolOption("ion-eager"))
|
||||
ion::js_IonOptions.setEagerCompilation();
|
||||
|
||||
if (op->getBoolOption("ion-compile-try-catch"))
|
||||
ion::js_IonOptions.compileTryCatch = true;
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
if (const char *str = op->getStringOption("ion-parallel-compile")) {
|
||||
if (strcmp(str, "on") == 0) {
|
||||
@ -5386,6 +5389,7 @@ main(int argc, char **argv, char **envp)
|
||||
" backtracking: Priority based backtracking register allocation\n"
|
||||
" stupid: Simple block local register allocation")
|
||||
|| !op.addBoolOption('\0', "ion-eager", "Always ion-compile methods (implies --baseline-eager)")
|
||||
|| !op.addBoolOption('\0', "ion-compile-try-catch", "Ion-compile try-catch statements")
|
||||
#ifdef JS_THREADSAFE
|
||||
|| !op.addStringOption('\0', "ion-parallel-compile", "on/off",
|
||||
"Compile scripts off thread (default: off)")
|
||||
|
@ -677,7 +677,7 @@ void
|
||||
Debugger::onNewScript(JSContext *cx, HandleScript script, GlobalObject *compileAndGoGlobal)
|
||||
{
|
||||
JS_ASSERT_IF(script->compileAndGo, compileAndGoGlobal);
|
||||
JS_ASSERT_IF(script->compileAndGo, compileAndGoGlobal == &script->global());
|
||||
JS_ASSERT_IF(script->compileAndGo, compileAndGoGlobal == &script->uninlinedGlobal());
|
||||
// We early return in slowPathOnNewScript for self-hosted scripts, so we can
|
||||
// ignore those in our assertion here.
|
||||
JS_ASSERT_IF(!script->compartment()->options().invisibleToDebugger &&
|
||||
|
@ -357,20 +357,8 @@ GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *objArg, bool wasObje
|
||||
HandleValue rref, MutableHandleValue res)
|
||||
{
|
||||
do {
|
||||
// Don't call GetPcScript (needed for analysis) from inside Ion since it's expensive.
|
||||
bool analyze = cx->currentlyRunningInInterpreter();
|
||||
|
||||
uint32_t index;
|
||||
if (IsDefinitelyIndex(rref, &index)) {
|
||||
if (analyze && !objArg->isNative() && !objArg->is<TypedArrayObject>()) {
|
||||
JSScript *script = NULL;
|
||||
jsbytecode *pc = NULL;
|
||||
types::TypeScript::GetPcScript(cx, &script, &pc);
|
||||
|
||||
if (script->hasAnalysis())
|
||||
script->analysis()->getCode(pc).nonNativeGetElement = true;
|
||||
}
|
||||
|
||||
if (JSObject::getElementNoGC(cx, objArg, objArg, index, res.address()))
|
||||
break;
|
||||
|
||||
@ -381,22 +369,6 @@ GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *objArg, bool wasObje
|
||||
break;
|
||||
}
|
||||
|
||||
if (analyze) {
|
||||
JSScript *script = NULL;
|
||||
jsbytecode *pc = NULL;
|
||||
types::TypeScript::GetPcScript(cx, &script, &pc);
|
||||
|
||||
if (script->hasAnalysis()) {
|
||||
script->analysis()->getCode(pc).getStringElement = true;
|
||||
|
||||
if (!objArg->is<ArrayObject>() && !objArg->isNative() &&
|
||||
!objArg->is<TypedArrayObject>())
|
||||
{
|
||||
script->analysis()->getCode(pc).nonNativeGetElement = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ValueMightBeSpecial(rref)) {
|
||||
RootedObject obj(cx, objArg);
|
||||
Rooted<SpecialId> special(cx);
|
||||
|
@ -1263,17 +1263,10 @@ SetObjectElementOperation(JSContext *cx, Handle<JSObject*> obj, HandleId id, con
|
||||
uint32_t length = obj->getDenseInitializedLength();
|
||||
int32_t i = JSID_TO_INT(id);
|
||||
if ((uint32_t)i >= length) {
|
||||
// In an Ion activation, GetPcScript won't work. For non-baseline activations,
|
||||
// that's ok, because optimized ion doesn't generate analysis info. However,
|
||||
// baseline must generate this information, so it passes the script and pc in
|
||||
// as arguments.
|
||||
if (script || cx->currentlyRunningInInterpreter()) {
|
||||
JS_ASSERT(!!script == !!pc);
|
||||
if (!script)
|
||||
types::TypeScript::GetPcScript(cx, script.address(), &pc);
|
||||
|
||||
if (script->hasAnalysis())
|
||||
script->analysis()->getCode(pc).arrayWriteHole = true;
|
||||
// Annotate script if provided with information (e.g. baseline)
|
||||
if (script && script->hasAnalysis()) {
|
||||
JS_ASSERT(pc);
|
||||
script->analysis()->getCode(pc).arrayWriteHole = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -186,7 +186,10 @@ ThreadPoolWorker::terminate()
|
||||
// them down when requested.
|
||||
|
||||
ThreadPool::ThreadPool(JSRuntime *rt)
|
||||
: runtime_(rt),
|
||||
:
|
||||
#if defined(JS_THREADSAFE) || defined(DEBUG)
|
||||
runtime_(rt),
|
||||
#endif
|
||||
numWorkers_(0), // updated during init()
|
||||
nextId_(0)
|
||||
{
|
||||
|
@ -70,7 +70,9 @@ class ThreadPool
|
||||
friend class ThreadPoolWorker;
|
||||
|
||||
// Initialized at startup only:
|
||||
#if defined(JS_THREADSAFE) || defined(DEBUG)
|
||||
JSRuntime *const runtime_;
|
||||
#endif
|
||||
js::Vector<ThreadPoolWorker*, 8, SystemAllocPolicy> workers_;
|
||||
|
||||
// Number of workers we will start, when we actually start them
|
||||
|
@ -457,7 +457,7 @@ inline bool IsDOMProxy(JSObject *obj)
|
||||
|
||||
typedef JSObject*
|
||||
(*DefineInterface)(JSContext *cx, JS::Handle<JSObject*> global,
|
||||
JS::Handle<jsid> id, bool *enabled);
|
||||
JS::Handle<jsid> id, bool defineOnGlobal);
|
||||
|
||||
typedef JSObject*
|
||||
(*ConstructNavigatorProperty)(JSContext *cx, JS::Handle<JSObject*> naviObj);
|
||||
|
@ -682,7 +682,35 @@ Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const
|
||||
return;
|
||||
}
|
||||
|
||||
AppendValueToString(eCSSProperty_text_decoration_line, aValue);
|
||||
const nsCSSValue *textBlink =
|
||||
data->ValueFor(eCSSProperty_text_blink);
|
||||
const nsCSSValue *decorationLine =
|
||||
data->ValueFor(eCSSProperty_text_decoration_line);
|
||||
|
||||
NS_ABORT_IF_FALSE(textBlink->GetUnit() == eCSSUnit_Enumerated,
|
||||
nsPrintfCString("bad text-blink unit %d",
|
||||
textBlink->GetUnit()).get());
|
||||
NS_ABORT_IF_FALSE(decorationLine->GetUnit() == eCSSUnit_Enumerated,
|
||||
nsPrintfCString("bad text-decoration-line unit %d",
|
||||
decorationLine->GetUnit()).get());
|
||||
|
||||
bool blinkNone = (textBlink->GetIntValue() == NS_STYLE_TEXT_BLINK_NONE);
|
||||
bool lineNone =
|
||||
(decorationLine->GetIntValue() == NS_STYLE_TEXT_DECORATION_LINE_NONE);
|
||||
|
||||
if (blinkNone && lineNone) {
|
||||
AppendValueToString(eCSSProperty_text_decoration_line, aValue);
|
||||
} else {
|
||||
if (!blinkNone) {
|
||||
AppendValueToString(eCSSProperty_text_blink, aValue);
|
||||
}
|
||||
if (!lineNone) {
|
||||
if (!aValue.IsEmpty()) {
|
||||
aValue.Append(PRUnichar(' '));
|
||||
}
|
||||
AppendValueToString(eCSSProperty_text_decoration_line, aValue);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case eCSSProperty_transition: {
|
||||
|
@ -9555,7 +9555,7 @@ CSSParserImpl::ParseTextDecoration()
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCSSValue line, style, color;
|
||||
nsCSSValue blink, line, style, color;
|
||||
switch (value.GetUnit()) {
|
||||
case eCSSUnit_Enumerated: {
|
||||
// We shouldn't accept decoration line style and color via
|
||||
@ -9567,6 +9567,7 @@ CSSParserImpl::ParseTextDecoration()
|
||||
|
||||
int32_t intValue = value.GetIntValue();
|
||||
if (intValue == eDecorationNone) {
|
||||
blink.SetIntValue(NS_STYLE_TEXT_BLINK_NONE, eCSSUnit_Enumerated);
|
||||
line.SetIntValue(NS_STYLE_TEXT_DECORATION_LINE_NONE,
|
||||
eCSSUnit_Enumerated);
|
||||
break;
|
||||
@ -9588,14 +9589,18 @@ CSSParserImpl::ParseTextDecoration()
|
||||
intValue |= newValue;
|
||||
}
|
||||
|
||||
line.SetIntValue(intValue, eCSSUnit_Enumerated);
|
||||
blink.SetIntValue((intValue & eDecorationBlink) != 0 ?
|
||||
NS_STYLE_TEXT_BLINK_BLINK : NS_STYLE_TEXT_BLINK_NONE,
|
||||
eCSSUnit_Enumerated);
|
||||
line.SetIntValue((intValue & ~eDecorationBlink), eCSSUnit_Enumerated);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
line = color = style = value;
|
||||
blink = line = color = style = value;
|
||||
break;
|
||||
}
|
||||
|
||||
AppendValue(eCSSProperty_text_blink, blink);
|
||||
AppendValue(eCSSProperty_text_decoration_line, line);
|
||||
AppendValue(eCSSProperty_text_decoration_color, color);
|
||||
AppendValue(eCSSProperty_text_decoration_style, style);
|
||||
@ -9613,7 +9618,7 @@ CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue)
|
||||
// look for more keywords
|
||||
nsCSSValue keyword;
|
||||
int32_t index;
|
||||
for (index = 0; index < 3; index++) {
|
||||
for (index = 0; index < 2; index++) {
|
||||
if (ParseEnum(keyword, nsCSSProps::kTextDecorationLineKTable)) {
|
||||
int32_t newValue = keyword.GetIntValue();
|
||||
if (newValue == NS_STYLE_TEXT_DECORATION_LINE_NONE ||
|
||||
|
@ -2745,6 +2745,18 @@ CSS_PROP_SHORTHAND(
|
||||
TextDecoration,
|
||||
CSS_PROPERTY_PARSE_FUNCTION,
|
||||
"")
|
||||
CSS_PROP_TEXTRESET(
|
||||
-moz-text-blink,
|
||||
text_blink,
|
||||
CSS_PROP_DOMPROP_PREFIXED(TextBlink),
|
||||
CSS_PROPERTY_PARSE_VALUE |
|
||||
CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE |
|
||||
CSS_PROPERTY_APPLIES_TO_PLACEHOLDER,
|
||||
"",
|
||||
VARIANT_HK,
|
||||
kTextBlinkKTable,
|
||||
offsetof(nsStyleTextReset, mTextBlink),
|
||||
eStyleAnimType_EnumU8)
|
||||
CSS_PROP_TEXTRESET(
|
||||
-moz-text-decoration-color,
|
||||
text_decoration_color,
|
||||
|
@ -1435,12 +1435,17 @@ const int32_t nsCSSProps::kTextAlignLastKTable[] = {
|
||||
eCSSKeyword_UNKNOWN,-1
|
||||
};
|
||||
|
||||
const int32_t nsCSSProps::kTextBlinkKTable[] = {
|
||||
eCSSKeyword_none, NS_STYLE_TEXT_BLINK_NONE,
|
||||
eCSSKeyword_blink, NS_STYLE_TEXT_BLINK_BLINK,
|
||||
eCSSKeyword_UNKNOWN,-1
|
||||
};
|
||||
|
||||
const int32_t nsCSSProps::kTextDecorationLineKTable[] = {
|
||||
eCSSKeyword_none, NS_STYLE_TEXT_DECORATION_LINE_NONE,
|
||||
eCSSKeyword_underline, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
|
||||
eCSSKeyword_overline, NS_STYLE_TEXT_DECORATION_LINE_OVERLINE,
|
||||
eCSSKeyword_line_through, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
|
||||
eCSSKeyword_blink, NS_STYLE_TEXT_DECORATION_LINE_BLINK,
|
||||
eCSSKeyword__moz_anchor_decoration, NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS,
|
||||
eCSSKeyword_UNKNOWN,-1
|
||||
};
|
||||
@ -2359,6 +2364,7 @@ static const nsCSSProperty gPaddingEndSubpropTable[] = {
|
||||
};
|
||||
|
||||
static const nsCSSProperty gTextDecorationSubpropTable[] = {
|
||||
eCSSProperty_text_blink,
|
||||
eCSSProperty_text_decoration_color,
|
||||
eCSSProperty_text_decoration_line,
|
||||
eCSSProperty_text_decoration_style,
|
||||
|
@ -529,6 +529,7 @@ public:
|
||||
static const int32_t kTableLayoutKTable[];
|
||||
static const int32_t kTextAlignKTable[];
|
||||
static const int32_t kTextAlignLastKTable[];
|
||||
static const int32_t kTextBlinkKTable[];
|
||||
static const int32_t kTextDecorationLineKTable[];
|
||||
static const int32_t kTextDecorationStyleKTable[];
|
||||
static const int32_t kTextOverflowKTable[];
|
||||
|
@ -2644,6 +2644,18 @@ nsComputedDOMStyle::DoGetTextAlignLast()
|
||||
return val;
|
||||
}
|
||||
|
||||
CSSValue*
|
||||
nsComputedDOMStyle::DoGetMozTextBlink()
|
||||
{
|
||||
nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
|
||||
|
||||
val->SetIdent(
|
||||
nsCSSProps::ValueToKeywordEnum(StyleTextReset()->mTextBlink,
|
||||
nsCSSProps::kTextBlinkKTable));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
CSSValue*
|
||||
nsComputedDOMStyle::DoGetTextDecoration()
|
||||
{
|
||||
@ -2674,14 +2686,25 @@ nsComputedDOMStyle::DoGetTextDecoration()
|
||||
// don't want these to appear in the computed style.
|
||||
line &= ~(NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS |
|
||||
NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL);
|
||||
uint8_t blink = textReset->mTextBlink;
|
||||
|
||||
if (line == NS_STYLE_TEXT_DECORATION_LINE_NONE) {
|
||||
if (blink == NS_STYLE_TEXT_BLINK_NONE &&
|
||||
line == NS_STYLE_TEXT_DECORATION_LINE_NONE) {
|
||||
val->SetIdent(eCSSKeyword_none);
|
||||
} else {
|
||||
nsAutoString str;
|
||||
nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_text_decoration_line,
|
||||
line, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
|
||||
NS_STYLE_TEXT_DECORATION_LINE_BLINK, str);
|
||||
if (line != NS_STYLE_TEXT_DECORATION_LINE_NONE) {
|
||||
nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_text_decoration_line,
|
||||
line, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
|
||||
NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, str);
|
||||
}
|
||||
if (blink != NS_STYLE_TEXT_BLINK_NONE) {
|
||||
if (!str.IsEmpty()) {
|
||||
str.Append(PRUnichar(' '));
|
||||
}
|
||||
nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_text_blink, blink,
|
||||
NS_STYLE_TEXT_BLINK_BLINK, NS_STYLE_TEXT_BLINK_BLINK, str);
|
||||
}
|
||||
val->SetString(str);
|
||||
}
|
||||
|
||||
@ -2722,7 +2745,7 @@ nsComputedDOMStyle::DoGetTextDecorationLine()
|
||||
NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL);
|
||||
nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_text_decoration_line,
|
||||
intValue, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
|
||||
NS_STYLE_TEXT_DECORATION_LINE_BLINK, decorationLineString);
|
||||
NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, decorationLineString);
|
||||
val->SetString(decorationLineString);
|
||||
}
|
||||
|
||||
@ -5117,6 +5140,7 @@ nsComputedDOMStyle::GetQueryablePropertyMap(uint32_t* aLength)
|
||||
COMPUTED_STYLE_MAP_ENTRY(stack_sizing, StackSizing),
|
||||
COMPUTED_STYLE_MAP_ENTRY(_moz_tab_size, TabSize),
|
||||
COMPUTED_STYLE_MAP_ENTRY(text_align_last, TextAlignLast),
|
||||
COMPUTED_STYLE_MAP_ENTRY(text_blink, MozTextBlink),
|
||||
COMPUTED_STYLE_MAP_ENTRY(text_decoration_color, TextDecorationColor),
|
||||
COMPUTED_STYLE_MAP_ENTRY(text_decoration_line, TextDecorationLine),
|
||||
COMPUTED_STYLE_MAP_ENTRY(text_decoration_style, TextDecorationStyle),
|
||||
|
@ -307,6 +307,7 @@ private:
|
||||
mozilla::dom::CSSValue* DoGetLineHeight();
|
||||
mozilla::dom::CSSValue* DoGetTextAlign();
|
||||
mozilla::dom::CSSValue* DoGetTextAlignLast();
|
||||
mozilla::dom::CSSValue* DoGetMozTextBlink();
|
||||
mozilla::dom::CSSValue* DoGetTextDecoration();
|
||||
mozilla::dom::CSSValue* DoGetTextDecorationColor();
|
||||
mozilla::dom::CSSValue* DoGetTextDecorationLine();
|
||||
|
@ -4012,6 +4012,11 @@ nsRuleNode::ComputeTextResetData(void* aStartStruct,
|
||||
}
|
||||
}
|
||||
|
||||
// text-blink: enum, inherit, initial
|
||||
SetDiscrete(*aRuleData->ValueForTextBlink(), text->mTextBlink,
|
||||
canStoreInRuleTree, SETDSC_ENUMERATED, parentText->mTextBlink,
|
||||
NS_STYLE_TEXT_BLINK_NONE, 0, 0, 0, 0);
|
||||
|
||||
// text-decoration-line: enum (bit field), inherit, initial
|
||||
const nsCSSValue* decorationLineValue =
|
||||
aRuleData->ValueForTextDecorationLine();
|
||||
|
@ -659,6 +659,10 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) {
|
||||
// Note: make sure that the largest NS_STYLE_TEXT_ALIGN_* value is smaller than
|
||||
// the smallest NS_STYLE_VERTICAL_ALIGN_* value below!
|
||||
|
||||
// See nsStyleText
|
||||
#define NS_STYLE_TEXT_BLINK_NONE 0
|
||||
#define NS_STYLE_TEXT_BLINK_BLINK 1
|
||||
|
||||
// See nsStyleText, nsStyleFont
|
||||
#define NS_STYLE_TEXT_DECORATION_LINE_NONE 0
|
||||
#define NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE NS_FONT_DECORATION_UNDERLINE
|
||||
|
@ -2826,6 +2826,7 @@ nsStyleTextReset::nsStyleTextReset(void)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsStyleTextReset);
|
||||
mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_BASELINE, eStyleUnit_Enumerated);
|
||||
mTextBlink = NS_STYLE_TEXT_BLINK_NONE;
|
||||
mTextDecorationLine = NS_STYLE_TEXT_DECORATION_LINE_NONE;
|
||||
mTextDecorationColor = NS_RGB(0,0,0);
|
||||
mTextDecorationStyle =
|
||||
|
@ -1281,6 +1281,7 @@ struct nsStyleTextReset {
|
||||
nsStyleCoord mVerticalAlign; // [reset] coord, percent, calc, enum (see nsStyleConsts.h)
|
||||
nsStyleTextOverflow mTextOverflow; // [reset] enum, string
|
||||
|
||||
uint8_t mTextBlink; // [reset] see nsStyleConsts.h
|
||||
uint8_t mTextDecorationLine; // [reset] see nsStyleConsts.h
|
||||
uint8_t mUnicodeBidi; // [reset] see nsStyleConsts.h
|
||||
protected:
|
||||
|
@ -3089,11 +3089,19 @@ var gCSSProperties = {
|
||||
other_values: [ "center", "justify", "start", "end", "left", "right" ],
|
||||
invalid_values: []
|
||||
},
|
||||
"-moz-text-blink": {
|
||||
domProp: "MozTextBlink",
|
||||
inherited: false,
|
||||
type: CSS_TYPE_LONGHAND,
|
||||
initial_values: [ "none" ],
|
||||
other_values: [ "blink" ],
|
||||
invalid_values: [ "underline", "overline", "line-through", "none underline", "underline blink", "blink underline" ]
|
||||
},
|
||||
"text-decoration": {
|
||||
domProp: "textDecoration",
|
||||
inherited: false,
|
||||
type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
|
||||
subproperties: [ "-moz-text-decoration-color", "-moz-text-decoration-line", "-moz-text-decoration-style" ],
|
||||
subproperties: [ "-moz-text-blink", "-moz-text-decoration-color", "-moz-text-decoration-line", "-moz-text-decoration-style" ],
|
||||
initial_values: [ "none" ],
|
||||
other_values: [ "underline", "overline", "line-through", "blink", "blink line-through underline", "underline overline line-through blink", "-moz-anchor-decoration", "blink -moz-anchor-decoration" ],
|
||||
invalid_values: [ "none none", "underline none", "none underline", "blink none", "none blink", "line-through blink line-through", "underline overline line-through blink none", "underline overline line-throuh blink blink",
|
||||
@ -3113,7 +3121,7 @@ var gCSSProperties = {
|
||||
inherited: false,
|
||||
type: CSS_TYPE_LONGHAND,
|
||||
initial_values: [ "none" ],
|
||||
other_values: [ "underline", "overline", "line-through", "blink", "blink line-through underline", "underline overline line-through blink", "-moz-anchor-decoration", "blink -moz-anchor-decoration" ],
|
||||
other_values: [ "underline", "overline", "line-through", "line-through underline", "underline overline line-through", "-moz-anchor-decoration", "-moz-anchor-decoration" ],
|
||||
invalid_values: [ "none none", "underline none", "none underline", "line-through blink line-through", "underline overline line-through blink none", "underline overline line-throuh blink blink" ]
|
||||
},
|
||||
"-moz-text-decoration-style": {
|
||||
|
@ -35,100 +35,106 @@ var tests = [
|
||||
// When only text-decoration was specified, text-decoration should look like
|
||||
// a longhand property.
|
||||
{ decoration: "none",
|
||||
line: null, color: null, style: null,
|
||||
blink: null, line: null, color: null, style: null,
|
||||
expectedValue: "none", expectedCSSValue: "none" },
|
||||
{ decoration: "underline",
|
||||
line: null, color: null, style: null,
|
||||
blink: null, line: null, color: null, style: null,
|
||||
expectedValue: "underline", expectedCSSValue: "underline" },
|
||||
{ decoration: "overline",
|
||||
line: null, color: null, style: null,
|
||||
blink: null, line: null, color: null, style: null,
|
||||
expectedValue: "overline", expectedCSSValue: "overline" },
|
||||
{ decoration: "line-through",
|
||||
line: null, color: null, style: null,
|
||||
blink: null, line: null, color: null, style: null,
|
||||
expectedValue: "line-through", expectedCSSValue: "line-through" },
|
||||
{ decoration: "blink",
|
||||
line: null, color: null, style: null,
|
||||
blink: null, line: null, color: null, style: null,
|
||||
expectedValue: "blink", expectedCSSValue: "blink" },
|
||||
{ decoration: "underline overline",
|
||||
line: null, color: null, style: null,
|
||||
blink: null, line: null, color: null, style: null,
|
||||
expectedValue: "underline overline",
|
||||
expectedCSSValue: "underline overline" },
|
||||
{ decoration: "underline line-through",
|
||||
line: null, color: null, style: null,
|
||||
blink: null, line: null, color: null, style: null,
|
||||
expectedValue: "underline line-through",
|
||||
expectedCSSValue: "underline line-through" },
|
||||
{ decoration: "blink underline",
|
||||
line: null, color: null, style: null,
|
||||
blink: null, line: null, color: null, style: null,
|
||||
expectedValue: "underline blink",
|
||||
expectedCSSValue: "underline blink" },
|
||||
{ decoration: "underline blink",
|
||||
line: null, color: null, style: null,
|
||||
blink: null, line: null, color: null, style: null,
|
||||
expectedValue: "underline blink",
|
||||
expectedCSSValue: "underline blink" },
|
||||
|
||||
// When only text-decoration-line or text-blink was specified,
|
||||
// text-decoration should look like a longhand property.
|
||||
{ decoration: null,
|
||||
line: "blink", color: null, style: null,
|
||||
blink: "blink", line: null, color: null, style: null,
|
||||
expectedValue: "blink", expectedCSSValue: "blink" },
|
||||
{ decoration: null,
|
||||
line: "underline", color: null, style: null,
|
||||
blink: null, line: "underline", color: null, style: null,
|
||||
expectedValue: "underline", expectedCSSValue: "underline" },
|
||||
{ decoration: null,
|
||||
line: "overline", color: null, style: null,
|
||||
blink: null, line: "overline", color: null, style: null,
|
||||
expectedValue: "overline", expectedCSSValue: "overline" },
|
||||
{ decoration: null,
|
||||
line: "line-through", color: null, style: null,
|
||||
blink: null, line: "line-through", color: null, style: null,
|
||||
expectedValue: "line-through", expectedCSSValue: "line-through" },
|
||||
{ decoration: null,
|
||||
line: "blink underline", color: null, style: null,
|
||||
blink: "blink", line: "underline", color: null, style: null,
|
||||
expectedValue: "underline blink", expectedCSSValue: "underline blink" },
|
||||
{ decoration: null,
|
||||
blink: "none", line: "underline", color: null, style: null,
|
||||
expectedValue: "underline", expectedCSSValue: "underline" },
|
||||
{ decoration: null,
|
||||
blink: "blink", line: "none", color: null, style: null,
|
||||
expectedValue: "blink", expectedCSSValue: "blink" },
|
||||
|
||||
// When text-decoration-color isn't its initial value,
|
||||
// text-decoration should be a shorthand property.
|
||||
{ decoration: "blink",
|
||||
line: null, color: "rgb(0, 0, 0)", style: null,
|
||||
blink: null, line: null, color: "rgb(0, 0, 0)", style: null,
|
||||
expectedValue: "", expectedCSSValue: null },
|
||||
{ decoration: "underline",
|
||||
line: null, color: "black", style: null,
|
||||
blink: null, line: null, color: "black", style: null,
|
||||
expectedValue: "", expectedCSSValue: null },
|
||||
{ decoration: "overline",
|
||||
line: null, color: "#ff0000", style: null,
|
||||
blink: null, line: null, color: "#ff0000", style: null,
|
||||
expectedValue: "", expectedCSSValue: null },
|
||||
{ decoration: "line-through",
|
||||
line: null, color: "initial", style: null,
|
||||
blink: null, line: null, color: "initial", style: null,
|
||||
expectedValue: "line-through", expectedCSSValue: "line-through" },
|
||||
{ decoration: "blink underline",
|
||||
line: null, color: "currentColor", style: null,
|
||||
blink: null, line: null, color: "currentColor", style: null,
|
||||
expectedValue: "underline blink", expectedCSSValue: "underline blink" },
|
||||
{ decoration: "underline line-through",
|
||||
line: null, color: "-moz-use-text-color", style: null,
|
||||
blink: null, line: null, color: "-moz-use-text-color", style: null,
|
||||
expectedValue: "underline line-through",
|
||||
expectedCSSValue: "underline line-through" },
|
||||
|
||||
// When text-decoration-style isn't its initial value,
|
||||
// text-decoration should be a shorthand property.
|
||||
{ decoration: "blink",
|
||||
line: null, color: null, style: "-moz-none",
|
||||
blink: null, line: null, color: null, style: "-moz-none",
|
||||
expectedValue: "", expectedCSSValue: null },
|
||||
{ decoration: "underline",
|
||||
line: null, color: null, style: "dotted",
|
||||
blink: null, line: null, color: null, style: "dotted",
|
||||
expectedValue: "", expectedCSSValue: null },
|
||||
{ decoration: "overline",
|
||||
line: null, color: null, style: "dashed",
|
||||
blink: null, line: null, color: null, style: "dashed",
|
||||
expectedValue: "", expectedCSSValue: null },
|
||||
{ decoration: "line-through",
|
||||
line: null, color: null, style: "double",
|
||||
blink: null, line: null, color: null, style: "double",
|
||||
expectedValue: "", expectedCSSValue: null },
|
||||
{ decoration: "blink underline",
|
||||
line: null, color: null, style: "wavy",
|
||||
blink: null, line: null, color: null, style: "wavy",
|
||||
expectedValue: "", expectedCSSValue: null },
|
||||
{ decoration: "underline blink overline line-through",
|
||||
line: null, color: null, style: "solid",
|
||||
blink: null, line: null, color: null, style: "solid",
|
||||
expectedValue: "underline overline line-through blink",
|
||||
expectedCSSValue: "underline overline line-through blink" },
|
||||
{ decoration: "line-through overline underline",
|
||||
line: null, color: null, style: "initial",
|
||||
blink: null, line: null, color: null, style: "initial",
|
||||
expectedValue: "underline overline line-through",
|
||||
expectedCSSValue: "underline overline line-through" }
|
||||
];
|
||||
@ -139,6 +145,9 @@ function makeDeclaration(aTest)
|
||||
if (aTest.decoration) {
|
||||
str += "text-decoration: " + aTest.decoration + "; ";
|
||||
}
|
||||
if (aTest.blink) {
|
||||
str += "-moz-text-blink: " + aTest.blink + "; ";
|
||||
}
|
||||
if (aTest.color) {
|
||||
str += "-moz-text-decoration-color: " + aTest.color + "; ";
|
||||
}
|
||||
@ -161,6 +170,9 @@ for (var i = 0; i < tests.length; ++i) {
|
||||
if (test.decoration) {
|
||||
$('t').style.textDecoration = test.decoration;
|
||||
}
|
||||
if (test.blink) {
|
||||
$('t').style.MozTextBlink = test.blink;
|
||||
}
|
||||
if (test.color) {
|
||||
$('t').style.MozTextDecorationColor = test.color;
|
||||
}
|
||||
|
@ -183,7 +183,6 @@ abstract public class GeckoApp
|
||||
private static GeckoApp sAppContext;
|
||||
protected MenuPanel mMenuPanel;
|
||||
protected Menu mMenu;
|
||||
private static GeckoThread sGeckoThread;
|
||||
protected GeckoProfile mProfile;
|
||||
public static int mOrientation;
|
||||
protected boolean mIsRestoringActivity;
|
||||
@ -1213,7 +1212,7 @@ abstract public class GeckoApp
|
||||
return;
|
||||
}
|
||||
|
||||
if (sGeckoThread != null) {
|
||||
if (GeckoThread.isCreated()) {
|
||||
// This happens when the GeckoApp activity is destroyed by Android
|
||||
// without killing the entire application (see Bug 769269).
|
||||
mIsRestoringActivity = true;
|
||||
@ -1438,18 +1437,20 @@ abstract public class GeckoApp
|
||||
Telemetry.HistogramAdd("FENNEC_STARTUP_GECKOAPP_ACTION", startupAction.ordinal());
|
||||
|
||||
if (!mIsRestoringActivity) {
|
||||
sGeckoThread = new GeckoThread(intent, passedUri);
|
||||
GeckoThread.setArgs(intent.getStringExtra("args"));
|
||||
GeckoThread.setAction(intent.getAction());
|
||||
GeckoThread.setUri(passedUri);
|
||||
}
|
||||
if (!ACTION_DEBUG.equals(action) &&
|
||||
GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.Launched)) {
|
||||
sGeckoThread.start();
|
||||
GeckoThread.createAndStart();
|
||||
} else if (ACTION_DEBUG.equals(action) &&
|
||||
GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.WaitForDebugger)) {
|
||||
ThreadUtils.getUiHandler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GeckoThread.setLaunchState(GeckoThread.LaunchState.Launching);
|
||||
sGeckoThread.start();
|
||||
GeckoThread.createAndStart();
|
||||
}
|
||||
}, 1000 * 5 /* 5 seconds */);
|
||||
}
|
||||
|
@ -38,16 +38,53 @@ public class GeckoThread extends Thread implements GeckoEventListener {
|
||||
|
||||
private static LaunchState sLaunchState = LaunchState.Launching;
|
||||
|
||||
private Intent mIntent;
|
||||
private static GeckoThread sGeckoThread;
|
||||
|
||||
private final String mArgs;
|
||||
private final String mAction;
|
||||
private final String mUri;
|
||||
|
||||
GeckoThread(Intent intent, String uri) {
|
||||
mIntent = intent;
|
||||
public static boolean ensureInit() {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
if (isCreated())
|
||||
return false;
|
||||
sGeckoThread = new GeckoThread(sArgs, sAction, sUri);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static String sArgs;
|
||||
public static String sAction;
|
||||
public static String sUri;
|
||||
|
||||
public static void setArgs(String args) {
|
||||
sArgs = args;
|
||||
}
|
||||
|
||||
public static void setAction(String action) {
|
||||
sAction = action;
|
||||
}
|
||||
|
||||
public static void setUri(String uri) {
|
||||
sUri = uri;
|
||||
}
|
||||
|
||||
GeckoThread(String args, String action, String uri) {
|
||||
mArgs = args;
|
||||
mAction = action;
|
||||
mUri = uri;
|
||||
setName("Gecko");
|
||||
GeckoAppShell.getEventDispatcher().registerEventListener("Gecko:Ready", this);
|
||||
}
|
||||
|
||||
public static boolean isCreated() {
|
||||
return sGeckoThread != null;
|
||||
}
|
||||
|
||||
public static void createAndStart() {
|
||||
if (ensureInit())
|
||||
sGeckoThread.start();
|
||||
}
|
||||
|
||||
private String initGeckoEnvironment() {
|
||||
// At some point while loading the gecko libs our default locale gets set
|
||||
// so just save it to locale here and reset it as default after the join
|
||||
@ -123,9 +160,8 @@ public class GeckoThread extends Thread implements GeckoEventListener {
|
||||
|
||||
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - runGecko");
|
||||
|
||||
String args = addCustomProfileArg(mIntent.getStringExtra("args"));
|
||||
String type = getTypeFromAction(mIntent.getAction());
|
||||
mIntent = null;
|
||||
String args = addCustomProfileArg(mArgs);
|
||||
String type = getTypeFromAction(mAction);
|
||||
|
||||
// and then fire us up
|
||||
Log.i(LOGTAG, "RunGecko - args = " + args);
|
||||
|
@ -28,7 +28,6 @@ import android.os.Handler;
|
||||
|
||||
public class GeckoView extends LayerView
|
||||
implements GeckoEventListener, ContextGetter {
|
||||
static GeckoThread sGeckoThread;
|
||||
|
||||
public GeckoView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@ -37,11 +36,9 @@ public class GeckoView extends LayerView
|
||||
String url = a.getString(R.styleable.GeckoView_url);
|
||||
a.recycle();
|
||||
|
||||
Intent intent;
|
||||
if (url == null) {
|
||||
intent = new Intent(Intent.ACTION_MAIN);
|
||||
} else {
|
||||
intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
if (url != null) {
|
||||
GeckoThread.setUri(url);
|
||||
GeckoThread.setAction(Intent.ACTION_VIEW);
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(url));
|
||||
}
|
||||
GeckoAppShell.setContextGetter(this);
|
||||
@ -53,12 +50,11 @@ public class GeckoView extends LayerView
|
||||
BrowserDB.initialize(profile.getName());
|
||||
GeckoAppShell.registerEventListener("Gecko:Ready", this);
|
||||
|
||||
sGeckoThread = new GeckoThread(intent, url);
|
||||
ThreadUtils.setUiThread(Thread.currentThread(), new Handler());
|
||||
initializeView(GeckoAppShell.getEventDispatcher());
|
||||
if (GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.Launched)) {
|
||||
GeckoAppShell.setLayerView(this);
|
||||
sGeckoThread.start();
|
||||
GeckoThread.createAndStart();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ class JavaAddonManager implements GeckoEventListener {
|
||||
Log.d(LOGTAG, "Attempting to load classes.dex file from " + zipFile + " and instantiate " + implClass);
|
||||
try {
|
||||
File tmpDir = mApplicationContext.getDir("dex", 0);
|
||||
DexClassLoader loader = new DexClassLoader(zipFile, tmpDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader());
|
||||
DexClassLoader loader = new DexClassLoader(zipFile, tmpDir.getAbsolutePath(), null, mApplicationContext.getClassLoader());
|
||||
Class<?> c = loader.loadClass(implClass);
|
||||
try {
|
||||
Constructor<?> constructor = c.getDeclaredConstructor(Map.class);
|
||||
|
@ -4320,10 +4320,14 @@ var BrowserEventHandler = {
|
||||
|
||||
onDoubleTap: function(aData) {
|
||||
let data = JSON.parse(aData);
|
||||
let element = ElementTouchHelper.anyElementFromPoint(data.x, data.y);
|
||||
|
||||
// We only want to do this if reflow-on-zoom is enabled.
|
||||
// We only want to do this if reflow-on-zoom is enabled, we don't already
|
||||
// have a reflow-on-zoom event pending, and the element upon which the user
|
||||
// double-tapped isn't of a type we want to avoid reflow-on-zoom.
|
||||
if (BrowserEventHandler.mReflozPref &&
|
||||
!BrowserApp.selectedTab._mReflozPoint) {
|
||||
!BrowserApp.selectedTab._mReflozPoint &&
|
||||
!this._shouldSuppressReflowOnZoom(element)) {
|
||||
let data = JSON.parse(aData);
|
||||
let zoomPointX = data.x;
|
||||
let zoomPointY = data.y;
|
||||
@ -4333,8 +4337,6 @@ var BrowserEventHandler = {
|
||||
BrowserApp.selectedTab.probablyNeedRefloz = true;
|
||||
}
|
||||
|
||||
let zoom = BrowserApp.selectedTab._zoom;
|
||||
let element = ElementTouchHelper.anyElementFromPoint(data.x, data.y);
|
||||
if (!element) {
|
||||
this._zoomOut();
|
||||
return;
|
||||
@ -4350,6 +4352,28 @@ var BrowserEventHandler = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if reflow-on-zoom functionality should be suppressed, given a
|
||||
* particular element. Double-tapping on the following elements suppresses
|
||||
* reflow-on-zoom:
|
||||
*
|
||||
* <video>, <object>, <embed>, <applet>, <canvas>, <img>, <media>, <pre>
|
||||
*/
|
||||
_shouldSuppressReflowOnZoom: function(aElement) {
|
||||
if (aElement instanceof Ci.nsIDOMHTMLVideoElement ||
|
||||
aElement instanceof Ci.nsIDOMHTMLObjectElement ||
|
||||
aElement instanceof Ci.nsIDOMHTMLEmbedElement ||
|
||||
aElement instanceof Ci.nsIDOMHTMLAppletElement ||
|
||||
aElement instanceof Ci.nsIDOMHTMLCanvasElement ||
|
||||
aElement instanceof Ci.nsIDOMHTMLImageElement ||
|
||||
aElement instanceof Ci.nsIDOMHTMLMediaElement ||
|
||||
aElement instanceof Ci.nsIDOMHTMLPreElement) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/* Zoom to an element, optionally keeping a particular part of it
|
||||
* in view if it is really tall.
|
||||
*/
|
||||
|
@ -98,6 +98,7 @@ static NS_DEFINE_CID(kSocketProviderServiceCID, NS_SOCKETPROVIDERSERVICE_CID);
|
||||
#define BROWSER_PREF_PREFIX "browser.cache."
|
||||
#define DONOTTRACK_HEADER_ENABLED "privacy.donottrackheader.enabled"
|
||||
#define DONOTTRACK_HEADER_VALUE "privacy.donottrackheader.value"
|
||||
#define DONOTTRACK_VALUE_UNSET 2
|
||||
#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
|
||||
#define TELEMETRY_ENABLED "toolkit.telemetry.enabledPreRelease"
|
||||
#else
|
||||
@ -235,6 +236,13 @@ nsHttpHandler::~nsHttpHandler()
|
||||
mPipelineTestTimer = nullptr;
|
||||
}
|
||||
|
||||
if (!mDoNotTrackEnabled) {
|
||||
Telemetry::Accumulate(Telemetry::DNT_USAGE, DONOTTRACK_VALUE_UNSET);
|
||||
}
|
||||
else {
|
||||
Telemetry::Accumulate(Telemetry::DNT_USAGE, mDoNotTrackValue);
|
||||
}
|
||||
|
||||
gHttpHandler = nullptr;
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,7 @@ public:
|
||||
|
||||
bool mBatchInProgress;
|
||||
int32_t mRelatedNotificationsCount;
|
||||
TimeStamp mLastNotificationTimeStamp;
|
||||
mozilla::TimeStamp mLastNotificationTimeStamp;
|
||||
nsCOMPtr<nsITimer> mEndBatchTimer;
|
||||
|
||||
void MaybeBeginBatch();
|
||||
|
@ -1573,6 +1573,11 @@
|
||||
"n_buckets": 50,
|
||||
"description": "Time spent waiting on the cache service lock (ms) on the main thread in NSASYNCDOOMEVENT_RUN"
|
||||
},
|
||||
"DNT_USAGE": {
|
||||
"kind": "enumerated",
|
||||
"n_values": 3,
|
||||
"description": "I want to be tracked, I do NOT want to be tracked, DNT unset"
|
||||
},
|
||||
"DNS_LOOKUP_METHOD2": {
|
||||
"kind": "enumerated",
|
||||
"n_values": 16,
|
||||
|
@ -51,7 +51,6 @@ var BuiltinProvider = {
|
||||
"devtools": "resource:///modules/devtools",
|
||||
"devtools/server": "resource://gre/modules/devtools/server",
|
||||
"devtools/toolkit/webconsole": "resource://gre/modules/devtools/toolkit/webconsole",
|
||||
"devtools/styleinspector/css-logic": "resource://gre/modules/devtools/styleinspector/css-logic",
|
||||
|
||||
// Allow access to xpcshell test items from the loader.
|
||||
"xpcshell-test": "resource://test"
|
||||
@ -86,8 +85,6 @@ var SrcdirProvider = {
|
||||
let toolkitURI = this.fileURI(OS.Path.join(srcdir, "toolkit", "devtools"));
|
||||
let serverURI = this.fileURI(OS.Path.join(srcdir, "toolkit", "devtools", "server"));
|
||||
let webconsoleURI = this.fileURI(OS.Path.join(srcdir, "toolkit", "devtools", "webconsole"));
|
||||
let cssLogicURI = this.fileURI(OS.Path.join(toolkitURI, "styleinspector", "css-logic"));
|
||||
|
||||
let mainURI = this.fileURI(OS.Path.join(srcdir, "browser", "devtools", "main.js"));
|
||||
this.loader = new loader.Loader({
|
||||
modules: {
|
||||
@ -98,7 +95,6 @@ var SrcdirProvider = {
|
||||
"devtools/server": serverURI,
|
||||
"devtools/toolkit/webconsole": webconsoleURI,
|
||||
"devtools": devtoolsURI,
|
||||
"devtools/styleinspector/css-logic": cssLogicURI,
|
||||
"main": mainURI
|
||||
},
|
||||
globals: loaderGlobals
|
||||
|
@ -10,6 +10,5 @@ PARALLEL_DIRS += [
|
||||
'gcli',
|
||||
'sourcemap',
|
||||
'webconsole',
|
||||
'apps',
|
||||
'styleinspector'
|
||||
'apps'
|
||||
]
|
||||
|
@ -1,15 +0,0 @@
|
||||
# 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/.
|
||||
|
||||
DEPTH = ../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
libs::
|
||||
$(INSTALL) $(IFLAGS1) $(srcdir)/*.js $(FINAL_TARGET)/modules/devtools/styleinspector
|
@ -1,5 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
Loading…
Reference in New Issue
Block a user