fune/dom/xbl/nsXBLProtoImplProperty.cpp
Kris Maglione 83371fe0b5 Bug 1445551: Part 4 - Remove compartment-per-addon. r=mccr8
The compartment-per-addon code was added so that we could segregate at least
some of the code from system-privileged add-ons into tagged compartments, even
when it ran in browser windows. That allowed us to apply certain special
behavior to them, such as enabling e10s shims, and to track some performance
characteristics.

The only remaining chrome-privileged add-ons now are system add-ons controlled
by us, and the shim system and per-compartment performance metrics are gone,
it no longer serves a purpose.

MozReview-Commit-ID: Ap186bWAaqP

--HG--
extra : rebase_source : c5bf81b44dd42b7cebce2784b7dd98480b41b593
2018-03-13 19:43:30 -07:00

368 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsAtom.h"
#include "nsString.h"
#include "jsapi.h"
#include "nsIContent.h"
#include "nsXBLProtoImplProperty.h"
#include "nsUnicharUtils.h"
#include "nsReadableUtils.h"
#include "nsJSUtils.h"
#include "nsXBLPrototypeBinding.h"
#include "nsXBLSerialize.h"
#include "xpcpublic.h"
using namespace mozilla;
using namespace mozilla::dom;
nsXBLProtoImplProperty::nsXBLProtoImplProperty(const char16_t* aName,
const char16_t* aGetter,
const char16_t* aSetter,
const char16_t* aReadOnly,
uint32_t aLineNumber) :
nsXBLProtoImplMember(aName),
mJSAttributes(JSPROP_ENUMERATE)
#ifdef DEBUG
, mIsCompiled(false)
#endif
{
MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
if (aReadOnly) {
nsAutoString readOnly; readOnly.Assign(*aReadOnly);
if (readOnly.LowerCaseEqualsLiteral("true"))
mJSAttributes |= JSPROP_READONLY;
}
if (aGetter) {
AppendGetterText(nsDependentString(aGetter));
SetGetterLineNumber(aLineNumber);
}
if (aSetter) {
AppendSetterText(nsDependentString(aSetter));
SetSetterLineNumber(aLineNumber);
}
}
nsXBLProtoImplProperty::nsXBLProtoImplProperty(const char16_t* aName,
const bool aIsReadOnly)
: nsXBLProtoImplMember(aName),
mJSAttributes(JSPROP_ENUMERATE)
#ifdef DEBUG
, mIsCompiled(false)
#endif
{
MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
if (aIsReadOnly)
mJSAttributes |= JSPROP_READONLY;
}
nsXBLProtoImplProperty::~nsXBLProtoImplProperty()
{
MOZ_COUNT_DTOR(nsXBLProtoImplProperty);
if (!mGetter.IsCompiled()) {
delete mGetter.GetUncompiled();
}
if (!mSetter.IsCompiled()) {
delete mSetter.GetUncompiled();
}
}
void nsXBLProtoImplProperty::EnsureUncompiledText(PropertyOp& aPropertyOp)
{
if (!aPropertyOp.GetUncompiled()) {
nsXBLTextWithLineNumber* text = new nsXBLTextWithLineNumber();
aPropertyOp.SetUncompiled(text);
}
}
void
nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText)
{
NS_PRECONDITION(!mIsCompiled,
"Must not be compiled when accessing getter text");
EnsureUncompiledText(mGetter);
mGetter.GetUncompiled()->AppendText(aText);
}
void
nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText)
{
NS_PRECONDITION(!mIsCompiled,
"Must not be compiled when accessing setter text");
EnsureUncompiledText(mSetter);
mSetter.GetUncompiled()->AppendText(aText);
}
void
nsXBLProtoImplProperty::SetGetterLineNumber(uint32_t aLineNumber)
{
NS_PRECONDITION(!mIsCompiled,
"Must not be compiled when accessing getter text");
EnsureUncompiledText(mGetter);
mGetter.GetUncompiled()->SetLineNumber(aLineNumber);
}
void
nsXBLProtoImplProperty::SetSetterLineNumber(uint32_t aLineNumber)
{
NS_PRECONDITION(!mIsCompiled,
"Must not be compiled when accessing setter text");
EnsureUncompiledText(mSetter);
mSetter.GetUncompiled()->SetLineNumber(aLineNumber);
}
const char* gPropertyArgs[] = { "val" };
nsresult
nsXBLProtoImplProperty::InstallMember(JSContext *aCx,
JS::Handle<JSObject*> aTargetClassObject)
{
NS_PRECONDITION(mIsCompiled,
"Should not be installing an uncompiled property");
MOZ_ASSERT(mGetter.IsCompiled() && mSetter.IsCompiled());
MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
#ifdef DEBUG
{
JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) ||
globalObject == xpc::GetXBLScope(aCx, globalObject));
MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == globalObject);
}
#endif
JS::Rooted<JSObject*> getter(aCx, mGetter.GetJSFunction());
JS::Rooted<JSObject*> setter(aCx, mSetter.GetJSFunction());
if (getter || setter) {
if (getter) {
if (!(getter = JS::CloneFunctionObject(aCx, getter)))
return NS_ERROR_OUT_OF_MEMORY;
}
if (setter) {
if (!(setter = JS::CloneFunctionObject(aCx, setter)))
return NS_ERROR_OUT_OF_MEMORY;
}
nsDependentString name(mName);
if (!::JS_DefineUCProperty(aCx, aTargetClassObject,
static_cast<const char16_t*>(mName),
name.Length(),
JS_DATA_TO_FUNC_PTR(JSNative, getter.get()),
JS_DATA_TO_FUNC_PTR(JSNative, setter.get()),
mJSAttributes))
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsresult
nsXBLProtoImplProperty::CompileMember(AutoJSAPI& jsapi, const nsString& aClassStr,
JS::Handle<JSObject*> aClassObject)
{
AssertInCompilationScope();
NS_PRECONDITION(!mIsCompiled,
"Trying to compile an already-compiled property");
NS_PRECONDITION(aClassObject,
"Must have class object to compile");
MOZ_ASSERT(!mGetter.IsCompiled() && !mSetter.IsCompiled());
JSContext *cx = jsapi.cx();
if (!mName)
return NS_ERROR_FAILURE; // Without a valid name, we can't install the member.
// We have a property.
nsresult rv = NS_OK;
nsAutoCString functionUri;
if (mGetter.GetUncompiled() || mSetter.GetUncompiled()) {
functionUri = NS_ConvertUTF16toUTF8(aClassStr);
int32_t hash = functionUri.RFindChar('#');
if (hash != kNotFound) {
functionUri.Truncate(hash);
}
}
bool deletedGetter = false;
nsXBLTextWithLineNumber *getterText = mGetter.GetUncompiled();
if (getterText && getterText->GetText()) {
nsDependentString getter(getterText->GetText());
if (!getter.IsEmpty()) {
JSAutoCompartment ac(cx, aClassObject);
JS::CompileOptions options(cx);
options.setFileAndLine(functionUri.get(), getterText->GetLineNumber());
nsCString name = NS_LITERAL_CSTRING("get_") + NS_ConvertUTF16toUTF8(mName);
JS::Rooted<JSObject*> getterObject(cx);
JS::AutoObjectVector emptyVector(cx);
rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, name, 0,
nullptr, getter, getterObject.address());
delete getterText;
deletedGetter = true;
mGetter.SetJSFunction(getterObject);
if (mGetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
mJSAttributes |= JSPROP_GETTER;
}
if (NS_FAILED(rv)) {
mGetter.SetJSFunction(nullptr);
mJSAttributes &= ~JSPROP_GETTER;
/*chaining to return failure*/
}
}
} // if getter is not empty
if (!deletedGetter) { // Empty getter
delete getterText;
mGetter.SetJSFunction(nullptr);
}
if (NS_FAILED(rv)) {
// We failed to compile our getter. So either we've set it to null, or
// it's still set to the text object. In either case, it's safe to return
// the error here, since then we'll be cleaned up as uncompiled and that
// will be ok. Going on and compiling the setter and _then_ returning an
// error, on the other hand, will try to clean up a compiled setter as
// uncompiled and crash.
return rv;
}
bool deletedSetter = false;
nsXBLTextWithLineNumber *setterText = mSetter.GetUncompiled();
if (setterText && setterText->GetText()) {
nsDependentString setter(setterText->GetText());
if (!setter.IsEmpty()) {
JSAutoCompartment ac(cx, aClassObject);
JS::CompileOptions options(cx);
options.setFileAndLine(functionUri.get(), setterText->GetLineNumber());
nsCString name = NS_LITERAL_CSTRING("set_") + NS_ConvertUTF16toUTF8(mName);
JS::Rooted<JSObject*> setterObject(cx);
JS::AutoObjectVector emptyVector(cx);
rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, name, 1,
gPropertyArgs, setter,
setterObject.address());
delete setterText;
deletedSetter = true;
mSetter.SetJSFunction(setterObject);
if (mSetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
mJSAttributes |= JSPROP_SETTER;
}
if (NS_FAILED(rv)) {
mSetter.SetJSFunction(nullptr);
mJSAttributes &= ~JSPROP_SETTER;
/*chaining to return failure*/
}
}
} // if setter wasn't empty....
if (!deletedSetter) { // Empty setter
delete setterText;
mSetter.SetJSFunction(nullptr);
}
#ifdef DEBUG
mIsCompiled = NS_SUCCEEDED(rv);
#endif
return rv;
}
void
nsXBLProtoImplProperty::Trace(const TraceCallbacks& aCallbacks, void *aClosure)
{
if (mJSAttributes & JSPROP_GETTER) {
aCallbacks.Trace(&mGetter.AsHeapObject(), "mGetter", aClosure);
}
if (mJSAttributes & JSPROP_SETTER) {
aCallbacks.Trace(&mSetter.AsHeapObject(), "mSetter", aClosure);
}
}
nsresult
nsXBLProtoImplProperty::Read(nsIObjectInputStream* aStream,
XBLBindingSerializeDetails aType)
{
AssertInCompilationScope();
MOZ_ASSERT(!mIsCompiled);
MOZ_ASSERT(!mGetter.GetUncompiled() && !mSetter.GetUncompiled());
AutoJSContext cx;
JS::Rooted<JSObject*> getterObject(cx);
if (aType == XBLBinding_Serialize_GetterProperty ||
aType == XBLBinding_Serialize_GetterSetterProperty) {
nsresult rv = XBL_DeserializeFunction(aStream, &getterObject);
NS_ENSURE_SUCCESS(rv, rv);
mJSAttributes |= JSPROP_GETTER;
}
mGetter.SetJSFunction(getterObject);
JS::Rooted<JSObject*> setterObject(cx);
if (aType == XBLBinding_Serialize_SetterProperty ||
aType == XBLBinding_Serialize_GetterSetterProperty) {
nsresult rv = XBL_DeserializeFunction(aStream, &setterObject);
NS_ENSURE_SUCCESS(rv, rv);
mJSAttributes |= JSPROP_SETTER;
}
mSetter.SetJSFunction(setterObject);
#ifdef DEBUG
mIsCompiled = true;
#endif
return NS_OK;
}
nsresult
nsXBLProtoImplProperty::Write(nsIObjectOutputStream* aStream)
{
AssertInCompilationScope();
XBLBindingSerializeDetails type;
if (mJSAttributes & JSPROP_GETTER) {
type = mJSAttributes & JSPROP_SETTER ?
XBLBinding_Serialize_GetterSetterProperty :
XBLBinding_Serialize_GetterProperty;
}
else {
type = XBLBinding_Serialize_SetterProperty;
}
if (mJSAttributes & JSPROP_READONLY) {
type |= XBLBinding_Serialize_ReadOnly;
}
nsresult rv = aStream->Write8(type);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->WriteWStringZ(mName);
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT_IF(mJSAttributes & (JSPROP_GETTER | JSPROP_SETTER), mIsCompiled);
if (mJSAttributes & JSPROP_GETTER) {
JS::Rooted<JSObject*> function(RootingCx(), mGetter.GetJSFunction());
rv = XBL_SerializeFunction(aStream, function);
NS_ENSURE_SUCCESS(rv, rv);
}
if (mJSAttributes & JSPROP_SETTER) {
JS::Rooted<JSObject*> function(RootingCx(), mSetter.GetJSFunction());
rv = XBL_SerializeFunction(aStream, function);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}