LCOV - code coverage report
Current view: top level - dom/xbl - nsXBLProtoImplMethod.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 61 170 35.9 %
Date: 2017-07-14 16:53:18 Functions: 5 13 38.5 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13