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.

     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 }

mercurial