1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/xbl/nsXBLProtoImplField.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,494 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsIAtom.h" 1.10 +#include "nsIContent.h" 1.11 +#include "nsString.h" 1.12 +#include "nsJSUtils.h" 1.13 +#include "jsapi.h" 1.14 +#include "js/CharacterEncoding.h" 1.15 +#include "nsUnicharUtils.h" 1.16 +#include "nsReadableUtils.h" 1.17 +#include "nsXBLProtoImplField.h" 1.18 +#include "nsIScriptContext.h" 1.19 +#include "nsIURI.h" 1.20 +#include "nsXBLSerialize.h" 1.21 +#include "nsXBLPrototypeBinding.h" 1.22 +#include "mozilla/dom/BindingUtils.h" 1.23 +#include "mozilla/dom/ScriptSettings.h" 1.24 +#include "nsGlobalWindow.h" 1.25 +#include "xpcpublic.h" 1.26 +#include "WrapperFactory.h" 1.27 + 1.28 +using namespace mozilla; 1.29 +using namespace mozilla::dom; 1.30 + 1.31 +nsXBLProtoImplField::nsXBLProtoImplField(const char16_t* aName, const char16_t* aReadOnly) 1.32 + : mNext(nullptr), 1.33 + mFieldText(nullptr), 1.34 + mFieldTextLength(0), 1.35 + mLineNumber(0) 1.36 +{ 1.37 + MOZ_COUNT_CTOR(nsXBLProtoImplField); 1.38 + mName = NS_strdup(aName); // XXXbz make more sense to use a stringbuffer? 1.39 + 1.40 + mJSAttributes = JSPROP_ENUMERATE; 1.41 + if (aReadOnly) { 1.42 + nsAutoString readOnly; readOnly.Assign(aReadOnly); 1.43 + if (readOnly.LowerCaseEqualsLiteral("true")) 1.44 + mJSAttributes |= JSPROP_READONLY; 1.45 + } 1.46 +} 1.47 + 1.48 + 1.49 +nsXBLProtoImplField::nsXBLProtoImplField(const bool aIsReadOnly) 1.50 + : mNext(nullptr), 1.51 + mFieldText(nullptr), 1.52 + mFieldTextLength(0), 1.53 + mLineNumber(0) 1.54 +{ 1.55 + MOZ_COUNT_CTOR(nsXBLProtoImplField); 1.56 + 1.57 + mJSAttributes = JSPROP_ENUMERATE; 1.58 + if (aIsReadOnly) 1.59 + mJSAttributes |= JSPROP_READONLY; 1.60 +} 1.61 + 1.62 +nsXBLProtoImplField::~nsXBLProtoImplField() 1.63 +{ 1.64 + MOZ_COUNT_DTOR(nsXBLProtoImplField); 1.65 + if (mFieldText) 1.66 + nsMemory::Free(mFieldText); 1.67 + NS_Free(mName); 1.68 + NS_CONTENT_DELETE_LIST_MEMBER(nsXBLProtoImplField, this, mNext); 1.69 +} 1.70 + 1.71 +void 1.72 +nsXBLProtoImplField::AppendFieldText(const nsAString& aText) 1.73 +{ 1.74 + if (mFieldText) { 1.75 + nsDependentString fieldTextStr(mFieldText, mFieldTextLength); 1.76 + nsAutoString newFieldText = fieldTextStr + aText; 1.77 + char16_t* temp = mFieldText; 1.78 + mFieldText = ToNewUnicode(newFieldText); 1.79 + mFieldTextLength = newFieldText.Length(); 1.80 + nsMemory::Free(temp); 1.81 + } 1.82 + else { 1.83 + mFieldText = ToNewUnicode(aText); 1.84 + mFieldTextLength = aText.Length(); 1.85 + } 1.86 +} 1.87 + 1.88 +// XBL fields are represented on elements inheriting that field a bit trickily. 1.89 +// When setting up the XBL prototype object, we install accessors for the fields 1.90 +// on the prototype object. Those accessors, when used, will then (via 1.91 +// InstallXBLField below) reify a property for the field onto the actual XBL-backed 1.92 +// element. 1.93 +// 1.94 +// The accessor property is a plain old property backed by a getter function and 1.95 +// a setter function. These properties are backed by the FieldGetter and 1.96 +// FieldSetter natives; they're created by InstallAccessors. The precise field to be 1.97 +// reified is identified using two extra slots on the getter/setter functions. 1.98 +// XBLPROTO_SLOT stores the XBL prototype object that provides the field. 1.99 +// FIELD_SLOT stores the name of the field, i.e. its JavaScript property name. 1.100 +// 1.101 +// This two-step field installation process -- creating an accessor on the 1.102 +// prototype, then have that reify an own property on the actual element -- is 1.103 +// admittedly convoluted. Better would be for XBL-backed elements to be proxies 1.104 +// that could resolve fields onto themselves. But given that XBL bindings are 1.105 +// associated with elements mutably -- you can add/remove/change -moz-binding 1.106 +// whenever you want, alas -- doing so would require all elements to be proxies, 1.107 +// which isn't performant now. So we do this two-step instead. 1.108 +static const uint32_t XBLPROTO_SLOT = 0; 1.109 +static const uint32_t FIELD_SLOT = 1; 1.110 + 1.111 +bool 1.112 +ValueHasISupportsPrivate(JS::Handle<JS::Value> v) 1.113 +{ 1.114 + if (!v.isObject()) { 1.115 + return false; 1.116 + } 1.117 + 1.118 + const DOMClass* domClass = GetDOMClass(&v.toObject()); 1.119 + if (domClass) { 1.120 + return domClass->mDOMObjectIsISupports; 1.121 + } 1.122 + 1.123 + const JSClass* clasp = ::JS_GetClass(&v.toObject()); 1.124 + const uint32_t HAS_PRIVATE_NSISUPPORTS = 1.125 + JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS; 1.126 + return (clasp->flags & HAS_PRIVATE_NSISUPPORTS) == HAS_PRIVATE_NSISUPPORTS; 1.127 +} 1.128 + 1.129 +#ifdef DEBUG 1.130 +static bool 1.131 +ValueHasISupportsPrivate(JSContext* cx, const JS::Value& aVal) 1.132 +{ 1.133 + JS::Rooted<JS::Value> v(cx, aVal); 1.134 + return ValueHasISupportsPrivate(v); 1.135 +} 1.136 +#endif 1.137 + 1.138 +// Define a shadowing property on |this| for the XBL field defined by the 1.139 +// contents of the callee's reserved slots. If the property was defined, 1.140 +// *installed will be true, and idp will be set to the property name that was 1.141 +// defined. 1.142 +static bool 1.143 +InstallXBLField(JSContext* cx, 1.144 + JS::Handle<JSObject*> callee, JS::Handle<JSObject*> thisObj, 1.145 + JS::MutableHandle<jsid> idp, bool* installed) 1.146 +{ 1.147 + *installed = false; 1.148 + 1.149 + // First ensure |this| is a reasonable XBL bound node. 1.150 + // 1.151 + // FieldAccessorGuard already determined whether |thisObj| was acceptable as 1.152 + // |this| in terms of not throwing a TypeError. Assert this for good measure. 1.153 + MOZ_ASSERT(ValueHasISupportsPrivate(cx, JS::ObjectValue(*thisObj))); 1.154 + 1.155 + // But there are some cases where we must accept |thisObj| but not install a 1.156 + // property on it, or otherwise touch it. Hence this split of |this|-vetting 1.157 + // duties. 1.158 + nsISupports* native = 1.159 + nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, thisObj); 1.160 + if (!native) { 1.161 + // Looks like whatever |thisObj| is it's not our nsIContent. It might well 1.162 + // be the proto our binding installed, however, where the private is the 1.163 + // nsXBLDocumentInfo, so just baul out quietly. Do NOT throw an exception 1.164 + // here. 1.165 + // 1.166 + // We could make this stricter by checking the class maybe, but whatever. 1.167 + return true; 1.168 + } 1.169 + 1.170 + nsCOMPtr<nsIContent> xblNode = do_QueryInterface(native); 1.171 + if (!xblNode) { 1.172 + xpc::Throw(cx, NS_ERROR_UNEXPECTED); 1.173 + return false; 1.174 + } 1.175 + 1.176 + // Now that |this| is okay, actually install the field. 1.177 + 1.178 + // Because of the possibility (due to XBL binding inheritance, because each 1.179 + // XBL binding lives in its own global object) that |this| might be in a 1.180 + // different compartment from the callee (not to mention that this method can 1.181 + // be called with an arbitrary |this| regardless of how insane XBL is), and 1.182 + // because in this method we've entered |this|'s compartment (see in 1.183 + // Field[GS]etter where we attempt a cross-compartment call), we must enter 1.184 + // the callee's compartment to access its reserved slots. 1.185 + nsXBLPrototypeBinding* protoBinding; 1.186 + nsDependentJSString fieldName; 1.187 + { 1.188 + JSAutoCompartment ac(cx, callee); 1.189 + 1.190 + JS::Rooted<JSObject*> xblProto(cx); 1.191 + xblProto = &js::GetFunctionNativeReserved(callee, XBLPROTO_SLOT).toObject(); 1.192 + 1.193 + JS::Rooted<JS::Value> name(cx, js::GetFunctionNativeReserved(callee, FIELD_SLOT)); 1.194 + JSFlatString* fieldStr = JS_ASSERT_STRING_IS_FLAT(name.toString()); 1.195 + fieldName.init(fieldStr); 1.196 + 1.197 + MOZ_ALWAYS_TRUE(JS_ValueToId(cx, name, idp)); 1.198 + 1.199 + // If a separate XBL scope is being used, the callee is not same-compartment 1.200 + // with the xbl prototype, and the object is a cross-compartment wrapper. 1.201 + xblProto = js::UncheckedUnwrap(xblProto); 1.202 + JSAutoCompartment ac2(cx, xblProto); 1.203 + JS::Value slotVal = ::JS_GetReservedSlot(xblProto, 0); 1.204 + protoBinding = static_cast<nsXBLPrototypeBinding*>(slotVal.toPrivate()); 1.205 + MOZ_ASSERT(protoBinding); 1.206 + } 1.207 + 1.208 + nsXBLProtoImplField* field = protoBinding->FindField(fieldName); 1.209 + MOZ_ASSERT(field); 1.210 + 1.211 + nsresult rv = field->InstallField(thisObj, protoBinding->DocURI(), installed); 1.212 + if (NS_SUCCEEDED(rv)) { 1.213 + return true; 1.214 + } 1.215 + 1.216 + if (!::JS_IsExceptionPending(cx)) { 1.217 + xpc::Throw(cx, rv); 1.218 + } 1.219 + return false; 1.220 +} 1.221 + 1.222 +bool 1.223 +FieldGetterImpl(JSContext *cx, JS::CallArgs args) 1.224 +{ 1.225 + JS::Handle<JS::Value> thisv = args.thisv(); 1.226 + MOZ_ASSERT(ValueHasISupportsPrivate(thisv)); 1.227 + 1.228 + JS::Rooted<JSObject*> thisObj(cx, &thisv.toObject()); 1.229 + 1.230 + // We should be in the compartment of |this|. If we got here via nativeCall, 1.231 + // |this| is not same-compartment with |callee|, and it's possible via 1.232 + // asymmetric security semantics that |args.calleev()| is actually a security 1.233 + // wrapper. In this case, we know we want to do an unsafe unwrap, and 1.234 + // InstallXBLField knows how to handle cross-compartment pointers. 1.235 + bool installed = false; 1.236 + JS::Rooted<JSObject*> callee(cx, js::UncheckedUnwrap(&args.calleev().toObject())); 1.237 + JS::Rooted<jsid> id(cx); 1.238 + if (!InstallXBLField(cx, callee, thisObj, &id, &installed)) { 1.239 + return false; 1.240 + } 1.241 + 1.242 + if (!installed) { 1.243 + args.rval().setUndefined(); 1.244 + return true; 1.245 + } 1.246 + 1.247 + JS::Rooted<JS::Value> v(cx); 1.248 + if (!JS_GetPropertyById(cx, thisObj, id, &v)) { 1.249 + return false; 1.250 + } 1.251 + args.rval().set(v); 1.252 + return true; 1.253 +} 1.254 + 1.255 +static bool 1.256 +FieldGetter(JSContext *cx, unsigned argc, JS::Value *vp) 1.257 +{ 1.258 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.259 + return JS::CallNonGenericMethod<ValueHasISupportsPrivate, FieldGetterImpl> 1.260 + (cx, args); 1.261 +} 1.262 + 1.263 +bool 1.264 +FieldSetterImpl(JSContext *cx, JS::CallArgs args) 1.265 +{ 1.266 + JS::Handle<JS::Value> thisv = args.thisv(); 1.267 + MOZ_ASSERT(ValueHasISupportsPrivate(thisv)); 1.268 + 1.269 + JS::Rooted<JSObject*> thisObj(cx, &thisv.toObject()); 1.270 + 1.271 + // We should be in the compartment of |this|. If we got here via nativeCall, 1.272 + // |this| is not same-compartment with |callee|, and it's possible via 1.273 + // asymmetric security semantics that |args.calleev()| is actually a security 1.274 + // wrapper. In this case, we know we want to do an unsafe unwrap, and 1.275 + // InstallXBLField knows how to handle cross-compartment pointers. 1.276 + bool installed = false; 1.277 + JS::Rooted<JSObject*> callee(cx, js::UncheckedUnwrap(&args.calleev().toObject())); 1.278 + JS::Rooted<jsid> id(cx); 1.279 + if (!InstallXBLField(cx, callee, thisObj, &id, &installed)) { 1.280 + return false; 1.281 + } 1.282 + 1.283 + if (installed) { 1.284 + if (!::JS_SetPropertyById(cx, thisObj, id, args.get(0))) { 1.285 + return false; 1.286 + } 1.287 + } 1.288 + args.rval().setUndefined(); 1.289 + return true; 1.290 +} 1.291 + 1.292 +static bool 1.293 +FieldSetter(JSContext *cx, unsigned argc, JS::Value *vp) 1.294 +{ 1.295 + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 1.296 + return JS::CallNonGenericMethod<ValueHasISupportsPrivate, FieldSetterImpl> 1.297 + (cx, args); 1.298 +} 1.299 + 1.300 +nsresult 1.301 +nsXBLProtoImplField::InstallAccessors(JSContext* aCx, 1.302 + JS::Handle<JSObject*> aTargetClassObject) 1.303 +{ 1.304 + MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx)); 1.305 + JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject)); 1.306 + JS::Rooted<JSObject*> scopeObject(aCx, xpc::GetXBLScopeOrGlobal(aCx, globalObject)); 1.307 + NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); 1.308 + 1.309 + // Don't install it if the field is empty; see also InstallField which also must 1.310 + // implement the not-empty requirement. 1.311 + if (IsEmpty()) { 1.312 + return NS_OK; 1.313 + } 1.314 + 1.315 + // Install a getter/setter pair which will resolve the field onto the actual 1.316 + // object, when invoked. 1.317 + 1.318 + // Get the field name as an id. 1.319 + JS::Rooted<jsid> id(aCx); 1.320 + JS::TwoByteChars chars(mName, NS_strlen(mName)); 1.321 + if (!JS_CharsToId(aCx, chars, &id)) 1.322 + return NS_ERROR_OUT_OF_MEMORY; 1.323 + 1.324 + // Properties/Methods have historically taken precendence over fields. We 1.325 + // install members first, so just bounce here if the property is already 1.326 + // defined. 1.327 + bool found = false; 1.328 + if (!JS_AlreadyHasOwnPropertyById(aCx, aTargetClassObject, id, &found)) 1.329 + return NS_ERROR_FAILURE; 1.330 + if (found) 1.331 + return NS_OK; 1.332 + 1.333 + // FieldGetter and FieldSetter need to run in the XBL scope so that they can 1.334 + // see through any SOWs on their targets. 1.335 + 1.336 + // First, enter the XBL scope, and compile the functions there. 1.337 + JSAutoCompartment ac(aCx, scopeObject); 1.338 + JS::Rooted<JS::Value> wrappedClassObj(aCx, JS::ObjectValue(*aTargetClassObject)); 1.339 + if (!JS_WrapValue(aCx, &wrappedClassObj) || !JS_WrapId(aCx, &id)) 1.340 + return NS_ERROR_OUT_OF_MEMORY; 1.341 + 1.342 + JS::Rooted<JSObject*> get(aCx, 1.343 + JS_GetFunctionObject(js::NewFunctionByIdWithReserved(aCx, FieldGetter, 1.344 + 0, 0, scopeObject, id))); 1.345 + if (!get) { 1.346 + return NS_ERROR_OUT_OF_MEMORY; 1.347 + } 1.348 + js::SetFunctionNativeReserved(get, XBLPROTO_SLOT, wrappedClassObj); 1.349 + js::SetFunctionNativeReserved(get, FIELD_SLOT, 1.350 + JS::StringValue(JSID_TO_STRING(id))); 1.351 + 1.352 + JS::Rooted<JSObject*> set(aCx, 1.353 + JS_GetFunctionObject(js::NewFunctionByIdWithReserved(aCx, FieldSetter, 1.354 + 1, 0, scopeObject, id))); 1.355 + if (!set) { 1.356 + return NS_ERROR_OUT_OF_MEMORY; 1.357 + } 1.358 + js::SetFunctionNativeReserved(set, XBLPROTO_SLOT, wrappedClassObj); 1.359 + js::SetFunctionNativeReserved(set, FIELD_SLOT, 1.360 + JS::StringValue(JSID_TO_STRING(id))); 1.361 + 1.362 + // Now, re-enter the class object's scope, wrap the getters/setters, and define 1.363 + // them there. 1.364 + JSAutoCompartment ac2(aCx, aTargetClassObject); 1.365 + if (!JS_WrapObject(aCx, &get) || !JS_WrapObject(aCx, &set) || 1.366 + !JS_WrapId(aCx, &id)) 1.367 + { 1.368 + return NS_ERROR_OUT_OF_MEMORY; 1.369 + } 1.370 + 1.371 + if (!::JS_DefinePropertyById(aCx, aTargetClassObject, id, JS::UndefinedValue(), 1.372 + JS_DATA_TO_FUNC_PTR(JSPropertyOp, get.get()), 1.373 + JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, set.get()), 1.374 + AccessorAttributes())) { 1.375 + return NS_ERROR_OUT_OF_MEMORY; 1.376 + } 1.377 + 1.378 + return NS_OK; 1.379 +} 1.380 + 1.381 +nsresult 1.382 +nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode, 1.383 + nsIURI* aBindingDocURI, 1.384 + bool* aDidInstall) const 1.385 +{ 1.386 + NS_PRECONDITION(aBoundNode, 1.387 + "uh-oh, bound node should NOT be null or bad things will " 1.388 + "happen"); 1.389 + 1.390 + *aDidInstall = false; 1.391 + 1.392 + // Empty fields are treated as not actually present. 1.393 + if (IsEmpty()) { 1.394 + return NS_OK; 1.395 + } 1.396 + 1.397 + nsAutoMicroTask mt; 1.398 + 1.399 + // EvaluateString and JS_DefineUCProperty can both trigger GC, so 1.400 + // protect |result| here. 1.401 + nsresult rv; 1.402 + 1.403 + nsAutoCString uriSpec; 1.404 + aBindingDocURI->GetSpec(uriSpec); 1.405 + 1.406 + nsIGlobalObject* globalObject = xpc::WindowGlobalOrNull(aBoundNode); 1.407 + if (!globalObject) { 1.408 + return NS_OK; 1.409 + } 1.410 + 1.411 + // We are going to run script via EvaluateString, so we need a script entry 1.412 + // point, but as this is XBL related it does not appear in the HTML spec. 1.413 + AutoEntryScript entryScript(globalObject, true); 1.414 + JSContext* cx = entryScript.cx(); 1.415 + 1.416 + NS_ASSERTION(!::JS_IsExceptionPending(cx), 1.417 + "Shouldn't get here when an exception is pending!"); 1.418 + 1.419 + // First, enter the xbl scope, wrap the node, and use that as the scope for 1.420 + // the evaluation. 1.421 + JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScopeOrGlobal(cx, aBoundNode)); 1.422 + NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); 1.423 + JSAutoCompartment ac(cx, scopeObject); 1.424 + 1.425 + JS::Rooted<JSObject*> wrappedNode(cx, aBoundNode); 1.426 + if (!JS_WrapObject(cx, &wrappedNode)) 1.427 + return NS_ERROR_OUT_OF_MEMORY; 1.428 + 1.429 + JS::Rooted<JS::Value> result(cx); 1.430 + JS::CompileOptions options(cx); 1.431 + options.setFileAndLine(uriSpec.get(), mLineNumber) 1.432 + .setVersion(JSVERSION_LATEST); 1.433 + nsJSUtils::EvaluateOptions evalOptions; 1.434 + rv = nsJSUtils::EvaluateString(cx, nsDependentString(mFieldText, 1.435 + mFieldTextLength), 1.436 + wrappedNode, options, evalOptions, 1.437 + &result); 1.438 + if (NS_FAILED(rv)) { 1.439 + return rv; 1.440 + } 1.441 + 1.442 + 1.443 + // Now, enter the node's compartment, wrap the eval result, and define it on 1.444 + // the bound node. 1.445 + JSAutoCompartment ac2(cx, aBoundNode); 1.446 + nsDependentString name(mName); 1.447 + if (!JS_WrapValue(cx, &result) || 1.448 + !::JS_DefineUCProperty(cx, aBoundNode, 1.449 + reinterpret_cast<const jschar*>(mName), 1.450 + name.Length(), result, nullptr, nullptr, 1.451 + mJSAttributes)) { 1.452 + return NS_ERROR_OUT_OF_MEMORY; 1.453 + } 1.454 + 1.455 + *aDidInstall = true; 1.456 + return NS_OK; 1.457 +} 1.458 + 1.459 +nsresult 1.460 +nsXBLProtoImplField::Read(nsIObjectInputStream* aStream) 1.461 +{ 1.462 + nsAutoString name; 1.463 + nsresult rv = aStream->ReadString(name); 1.464 + NS_ENSURE_SUCCESS(rv, rv); 1.465 + mName = ToNewUnicode(name); 1.466 + 1.467 + rv = aStream->Read32(&mLineNumber); 1.468 + NS_ENSURE_SUCCESS(rv, rv); 1.469 + 1.470 + nsAutoString fieldText; 1.471 + rv = aStream->ReadString(fieldText); 1.472 + NS_ENSURE_SUCCESS(rv, rv); 1.473 + mFieldTextLength = fieldText.Length(); 1.474 + if (mFieldTextLength) 1.475 + mFieldText = ToNewUnicode(fieldText); 1.476 + 1.477 + return NS_OK; 1.478 +} 1.479 + 1.480 +nsresult 1.481 +nsXBLProtoImplField::Write(nsIObjectOutputStream* aStream) 1.482 +{ 1.483 + XBLBindingSerializeDetails type = XBLBinding_Serialize_Field; 1.484 + 1.485 + if (mJSAttributes & JSPROP_READONLY) { 1.486 + type |= XBLBinding_Serialize_ReadOnly; 1.487 + } 1.488 + 1.489 + nsresult rv = aStream->Write8(type); 1.490 + NS_ENSURE_SUCCESS(rv, rv); 1.491 + rv = aStream->WriteWStringZ(mName); 1.492 + NS_ENSURE_SUCCESS(rv, rv); 1.493 + rv = aStream->Write32(mLineNumber); 1.494 + NS_ENSURE_SUCCESS(rv, rv); 1.495 + 1.496 + return aStream->WriteWStringZ(mFieldText ? mFieldText : MOZ_UTF16("")); 1.497 +}