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