dom/xbl/nsXBLProtoImplMethod.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsIAtom.h"
michael@0 7 #include "nsString.h"
michael@0 8 #include "jsapi.h"
michael@0 9 #include "nsIContent.h"
michael@0 10 #include "nsIDocument.h"
michael@0 11 #include "nsIScriptGlobalObject.h"
michael@0 12 #include "nsUnicharUtils.h"
michael@0 13 #include "nsReadableUtils.h"
michael@0 14 #include "nsXBLProtoImplMethod.h"
michael@0 15 #include "nsIScriptContext.h"
michael@0 16 #include "nsJSUtils.h"
michael@0 17 #include "nsContentUtils.h"
michael@0 18 #include "nsCxPusher.h"
michael@0 19 #include "nsIScriptSecurityManager.h"
michael@0 20 #include "nsIXPConnect.h"
michael@0 21 #include "xpcpublic.h"
michael@0 22 #include "nsXBLPrototypeBinding.h"
michael@0 23
michael@0 24 using namespace mozilla;
michael@0 25
michael@0 26 nsXBLProtoImplMethod::nsXBLProtoImplMethod(const char16_t* aName) :
michael@0 27 nsXBLProtoImplMember(aName),
michael@0 28 mMethod()
michael@0 29 {
michael@0 30 MOZ_COUNT_CTOR(nsXBLProtoImplMethod);
michael@0 31 }
michael@0 32
michael@0 33 nsXBLProtoImplMethod::~nsXBLProtoImplMethod()
michael@0 34 {
michael@0 35 MOZ_COUNT_DTOR(nsXBLProtoImplMethod);
michael@0 36
michael@0 37 if (!IsCompiled()) {
michael@0 38 delete GetUncompiledMethod();
michael@0 39 }
michael@0 40 }
michael@0 41
michael@0 42 void
michael@0 43 nsXBLProtoImplMethod::AppendBodyText(const nsAString& aText)
michael@0 44 {
michael@0 45 NS_PRECONDITION(!IsCompiled(),
michael@0 46 "Must not be compiled when accessing uncompiled method");
michael@0 47
michael@0 48 nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
michael@0 49 if (!uncompiledMethod) {
michael@0 50 uncompiledMethod = new nsXBLUncompiledMethod();
michael@0 51 if (!uncompiledMethod)
michael@0 52 return;
michael@0 53 SetUncompiledMethod(uncompiledMethod);
michael@0 54 }
michael@0 55
michael@0 56 uncompiledMethod->AppendBodyText(aText);
michael@0 57 }
michael@0 58
michael@0 59 void
michael@0 60 nsXBLProtoImplMethod::AddParameter(const nsAString& aText)
michael@0 61 {
michael@0 62 NS_PRECONDITION(!IsCompiled(),
michael@0 63 "Must not be compiled when accessing uncompiled method");
michael@0 64
michael@0 65 if (aText.IsEmpty()) {
michael@0 66 NS_WARNING("Empty name attribute in xbl:parameter!");
michael@0 67 return;
michael@0 68 }
michael@0 69
michael@0 70 nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
michael@0 71 if (!uncompiledMethod) {
michael@0 72 uncompiledMethod = new nsXBLUncompiledMethod();
michael@0 73 if (!uncompiledMethod)
michael@0 74 return;
michael@0 75 SetUncompiledMethod(uncompiledMethod);
michael@0 76 }
michael@0 77
michael@0 78 uncompiledMethod->AddParameter(aText);
michael@0 79 }
michael@0 80
michael@0 81 void
michael@0 82 nsXBLProtoImplMethod::SetLineNumber(uint32_t aLineNumber)
michael@0 83 {
michael@0 84 NS_PRECONDITION(!IsCompiled(),
michael@0 85 "Must not be compiled when accessing uncompiled method");
michael@0 86
michael@0 87 nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
michael@0 88 if (!uncompiledMethod) {
michael@0 89 uncompiledMethod = new nsXBLUncompiledMethod();
michael@0 90 if (!uncompiledMethod)
michael@0 91 return;
michael@0 92 SetUncompiledMethod(uncompiledMethod);
michael@0 93 }
michael@0 94
michael@0 95 uncompiledMethod->SetLineNumber(aLineNumber);
michael@0 96 }
michael@0 97
michael@0 98 nsresult
michael@0 99 nsXBLProtoImplMethod::InstallMember(JSContext* aCx,
michael@0 100 JS::Handle<JSObject*> aTargetClassObject)
michael@0 101 {
michael@0 102 NS_PRECONDITION(IsCompiled(),
michael@0 103 "Should not be installing an uncompiled method");
michael@0 104 MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
michael@0 105
michael@0 106 JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
michael@0 107 MOZ_ASSERT(xpc::IsInXBLScope(globalObject) ||
michael@0 108 globalObject == xpc::GetXBLScope(aCx, globalObject));
michael@0 109
michael@0 110 JS::Rooted<JSObject*> jsMethodObject(aCx, GetCompiledMethod());
michael@0 111 if (jsMethodObject) {
michael@0 112 nsDependentString name(mName);
michael@0 113
michael@0 114 JS::Rooted<JSObject*> method(aCx, JS_CloneFunctionObject(aCx, jsMethodObject, globalObject));
michael@0 115 NS_ENSURE_TRUE(method, NS_ERROR_OUT_OF_MEMORY);
michael@0 116
michael@0 117 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*method));
michael@0 118 if (!::JS_DefineUCProperty(aCx, aTargetClassObject,
michael@0 119 static_cast<const jschar*>(mName),
michael@0 120 name.Length(), value,
michael@0 121 nullptr, nullptr, JSPROP_ENUMERATE)) {
michael@0 122 return NS_ERROR_OUT_OF_MEMORY;
michael@0 123 }
michael@0 124 }
michael@0 125 return NS_OK;
michael@0 126 }
michael@0 127
michael@0 128 nsresult
michael@0 129 nsXBLProtoImplMethod::CompileMember(const nsCString& aClassStr,
michael@0 130 JS::Handle<JSObject*> aClassObject)
michael@0 131 {
michael@0 132 AssertInCompilationScope();
michael@0 133 NS_PRECONDITION(!IsCompiled(),
michael@0 134 "Trying to compile an already-compiled method");
michael@0 135 NS_PRECONDITION(aClassObject,
michael@0 136 "Must have class object to compile");
michael@0 137
michael@0 138 nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
michael@0 139
michael@0 140 // No parameters or body was supplied, so don't install method.
michael@0 141 if (!uncompiledMethod) {
michael@0 142 // Early return after which we consider ourselves compiled.
michael@0 143 SetCompiledMethod(nullptr);
michael@0 144
michael@0 145 return NS_OK;
michael@0 146 }
michael@0 147
michael@0 148 // Don't install method if no name was supplied.
michael@0 149 if (!mName) {
michael@0 150 delete uncompiledMethod;
michael@0 151
michael@0 152 // Early return after which we consider ourselves compiled.
michael@0 153 SetCompiledMethod(nullptr);
michael@0 154
michael@0 155 return NS_OK;
michael@0 156 }
michael@0 157
michael@0 158 // We have a method.
michael@0 159 // Allocate an array for our arguments.
michael@0 160 int32_t paramCount = uncompiledMethod->GetParameterCount();
michael@0 161 char** args = nullptr;
michael@0 162 if (paramCount > 0) {
michael@0 163 args = new char*[paramCount];
michael@0 164 if (!args)
michael@0 165 return NS_ERROR_OUT_OF_MEMORY;
michael@0 166
michael@0 167 // Add our parameters to our args array.
michael@0 168 int32_t argPos = 0;
michael@0 169 for (nsXBLParameter* curr = uncompiledMethod->mParameters;
michael@0 170 curr;
michael@0 171 curr = curr->mNext) {
michael@0 172 args[argPos] = curr->mName;
michael@0 173 argPos++;
michael@0 174 }
michael@0 175 }
michael@0 176
michael@0 177 // Get the body
michael@0 178 nsDependentString body;
michael@0 179 char16_t *bodyText = uncompiledMethod->mBodyText.GetText();
michael@0 180 if (bodyText)
michael@0 181 body.Rebind(bodyText);
michael@0 182
michael@0 183 // Now that we have a body and args, compile the function
michael@0 184 // and then define it.
michael@0 185 NS_ConvertUTF16toUTF8 cname(mName);
michael@0 186 nsAutoCString functionUri(aClassStr);
michael@0 187 int32_t hash = functionUri.RFindChar('#');
michael@0 188 if (hash != kNotFound) {
michael@0 189 functionUri.Truncate(hash);
michael@0 190 }
michael@0 191
michael@0 192 AutoJSContext cx;
michael@0 193 JSAutoCompartment ac(cx, aClassObject);
michael@0 194 JS::CompileOptions options(cx);
michael@0 195 options.setFileAndLine(functionUri.get(),
michael@0 196 uncompiledMethod->mBodyText.GetLineNumber())
michael@0 197 .setVersion(JSVERSION_LATEST);
michael@0 198 JS::Rooted<JSObject*> methodObject(cx);
michael@0 199 nsresult rv = nsJSUtils::CompileFunction(cx, JS::NullPtr(), options, cname,
michael@0 200 paramCount,
michael@0 201 const_cast<const char**>(args),
michael@0 202 body, methodObject.address());
michael@0 203
michael@0 204 // Destroy our uncompiled method and delete our arg list.
michael@0 205 delete uncompiledMethod;
michael@0 206 delete [] args;
michael@0 207 if (NS_FAILED(rv)) {
michael@0 208 SetUncompiledMethod(nullptr);
michael@0 209 return rv;
michael@0 210 }
michael@0 211
michael@0 212 SetCompiledMethod(methodObject);
michael@0 213
michael@0 214 return NS_OK;
michael@0 215 }
michael@0 216
michael@0 217 void
michael@0 218 nsXBLProtoImplMethod::Trace(const TraceCallbacks& aCallbacks, void *aClosure)
michael@0 219 {
michael@0 220 if (IsCompiled() && GetCompiledMethodPreserveColor()) {
michael@0 221 aCallbacks.Trace(&mMethod.AsHeapObject(), "mMethod", aClosure);
michael@0 222 }
michael@0 223 }
michael@0 224
michael@0 225 nsresult
michael@0 226 nsXBLProtoImplMethod::Read(nsIObjectInputStream* aStream)
michael@0 227 {
michael@0 228 AssertInCompilationScope();
michael@0 229 MOZ_ASSERT(!IsCompiled() && !GetUncompiledMethod());
michael@0 230
michael@0 231 AutoJSContext cx;
michael@0 232 JS::Rooted<JSObject*> methodObject(cx);
michael@0 233 nsresult rv = XBL_DeserializeFunction(aStream, &methodObject);
michael@0 234 if (NS_FAILED(rv)) {
michael@0 235 SetUncompiledMethod(nullptr);
michael@0 236 return rv;
michael@0 237 }
michael@0 238
michael@0 239 SetCompiledMethod(methodObject);
michael@0 240
michael@0 241 return NS_OK;
michael@0 242 }
michael@0 243
michael@0 244 nsresult
michael@0 245 nsXBLProtoImplMethod::Write(nsIObjectOutputStream* aStream)
michael@0 246 {
michael@0 247 AssertInCompilationScope();
michael@0 248 MOZ_ASSERT(IsCompiled());
michael@0 249 if (GetCompiledMethodPreserveColor()) {
michael@0 250 nsresult rv = aStream->Write8(XBLBinding_Serialize_Method);
michael@0 251 NS_ENSURE_SUCCESS(rv, rv);
michael@0 252
michael@0 253 rv = aStream->WriteWStringZ(mName);
michael@0 254 NS_ENSURE_SUCCESS(rv, rv);
michael@0 255
michael@0 256 // Calling fromMarkedLocation() is safe because mMethod is traced by the
michael@0 257 // Trace() method above, and because its value is never changed after it has
michael@0 258 // been set to a compiled method.
michael@0 259 JS::Handle<JSObject*> method =
michael@0 260 JS::Handle<JSObject*>::fromMarkedLocation(mMethod.AsHeapObject().address());
michael@0 261 return XBL_SerializeFunction(aStream, method);
michael@0 262 }
michael@0 263
michael@0 264 return NS_OK;
michael@0 265 }
michael@0 266
michael@0 267 nsresult
michael@0 268 nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
michael@0 269 {
michael@0 270 NS_PRECONDITION(IsCompiled(), "Can't execute uncompiled method");
michael@0 271
michael@0 272 if (!GetCompiledMethod()) {
michael@0 273 // Nothing to do here
michael@0 274 return NS_OK;
michael@0 275 }
michael@0 276
michael@0 277 // Get the script context the same way
michael@0 278 // nsXBLProtoImpl::InstallImplementation does.
michael@0 279 nsIDocument* document = aBoundElement->OwnerDoc();
michael@0 280
michael@0 281 nsCOMPtr<nsIScriptGlobalObject> global =
michael@0 282 do_QueryInterface(document->GetWindow());
michael@0 283 if (!global) {
michael@0 284 return NS_OK;
michael@0 285 }
michael@0 286
michael@0 287 nsCOMPtr<nsIScriptContext> context = global->GetContext();
michael@0 288 if (!context) {
michael@0 289 return NS_OK;
michael@0 290 }
michael@0 291
michael@0 292 nsAutoMicroTask mt;
michael@0 293
michael@0 294 AutoPushJSContext cx(context->GetNativeContext());
michael@0 295
michael@0 296 JS::Rooted<JSObject*> globalObject(cx, global->GetGlobalJSObject());
michael@0 297
michael@0 298 JS::Rooted<JS::Value> v(cx);
michael@0 299 nsresult rv = nsContentUtils::WrapNative(cx, aBoundElement, &v);
michael@0 300 NS_ENSURE_SUCCESS(rv, rv);
michael@0 301
michael@0 302 // Use nsCxPusher to make sure we call ScriptEvaluated when we're done.
michael@0 303 //
michael@0 304 // Make sure to do this before entering the compartment, since pushing Push()
michael@0 305 // may call JS_SaveFrameChain(), which puts us back in an unentered state.
michael@0 306 nsCxPusher pusher;
michael@0 307 if (!pusher.Push(aBoundElement))
michael@0 308 return NS_ERROR_UNEXPECTED;
michael@0 309 MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
michael@0 310
michael@0 311 JS::Rooted<JSObject*> thisObject(cx, &v.toObject());
michael@0 312 JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScopeOrGlobal(cx, globalObject));
michael@0 313 NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
michael@0 314
michael@0 315 JSAutoCompartment ac(cx, scopeObject);
michael@0 316 if (!JS_WrapObject(cx, &thisObject))
michael@0 317 return NS_ERROR_OUT_OF_MEMORY;
michael@0 318
michael@0 319 // Clone the function object, using thisObject as the parent so "this" is in
michael@0 320 // the scope chain of the resulting function (for backwards compat to the
michael@0 321 // days when this was an event handler).
michael@0 322 JS::Rooted<JSObject*> jsMethodObject(cx, GetCompiledMethod());
michael@0 323 JS::Rooted<JSObject*> method(cx, ::JS_CloneFunctionObject(cx, jsMethodObject, thisObject));
michael@0 324 if (!method)
michael@0 325 return NS_ERROR_OUT_OF_MEMORY;
michael@0 326
michael@0 327 // Now call the method
michael@0 328
michael@0 329 // Check whether script is enabled.
michael@0 330 bool scriptAllowed = nsContentUtils::GetSecurityManager()->
michael@0 331 ScriptAllowed(js::GetGlobalForObjectCrossCompartment(method));
michael@0 332
michael@0 333 bool ok = true;
michael@0 334 if (scriptAllowed) {
michael@0 335 JS::Rooted<JS::Value> retval(cx);
michael@0 336 JS::Rooted<JS::Value> methodVal(cx, JS::ObjectValue(*method));
michael@0 337 ok = ::JS::Call(cx, thisObject, methodVal, JS::HandleValueArray::empty(), &retval);
michael@0 338 }
michael@0 339
michael@0 340 if (!ok) {
michael@0 341 // If a constructor or destructor threw an exception, it doesn't stop
michael@0 342 // anything else. We just report it. Note that we need to set aside the
michael@0 343 // frame chain here, since the constructor invocation is not related to
michael@0 344 // whatever is on the stack right now, really.
michael@0 345 nsJSUtils::ReportPendingException(cx);
michael@0 346 return NS_ERROR_FAILURE;
michael@0 347 }
michael@0 348
michael@0 349 return NS_OK;
michael@0 350 }
michael@0 351
michael@0 352 nsresult
michael@0 353 nsXBLProtoImplAnonymousMethod::Write(nsIObjectOutputStream* aStream,
michael@0 354 XBLBindingSerializeDetails aType)
michael@0 355 {
michael@0 356 AssertInCompilationScope();
michael@0 357 MOZ_ASSERT(IsCompiled());
michael@0 358 if (GetCompiledMethodPreserveColor()) {
michael@0 359 nsresult rv = aStream->Write8(aType);
michael@0 360 NS_ENSURE_SUCCESS(rv, rv);
michael@0 361
michael@0 362 rv = aStream->WriteWStringZ(mName);
michael@0 363 NS_ENSURE_SUCCESS(rv, rv);
michael@0 364
michael@0 365 // Calling fromMarkedLocation() is safe because mMethod is traced by the
michael@0 366 // Trace() method above, and because its value is never changed after it has
michael@0 367 // been set to a compiled method.
michael@0 368 JS::Handle<JSObject*> method =
michael@0 369 JS::Handle<JSObject*>::fromMarkedLocation(mMethod.AsHeapObject().address());
michael@0 370 rv = XBL_SerializeFunction(aStream, method);
michael@0 371 NS_ENSURE_SUCCESS(rv, rv);
michael@0 372 }
michael@0 373
michael@0 374 return NS_OK;
michael@0 375 }

mercurial