/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=4 sw=4 et tw=80: * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"WrapperOwner.h"#include"JavaScriptLogging.h"#include"mozilla/Unused.h"#include"mozilla/dom/BindingUtils.h"#include"jsfriendapi.h"#include"js/CharacterEncoding.h"#include"xpcprivate.h"#include"CPOWTimer.h"#include"WrapperFactory.h"#include"nsIDocShellTreeItem.h"#include"nsIDOMDocument.h"usingnamespacejs;usingnamespaceJS;usingnamespacemozilla;usingnamespacemozilla::jsipc;structAuxCPOWData{ObjectIdid;boolisCallable;boolisConstructor;boolisDOMObject;// The object tag is just some auxilliary information that clients can use// however they see fit.nsCStringobjectTag;// The class name for WrapperOwner::className, below.nsCStringclassName;AuxCPOWData(ObjectIdid,boolisCallable,boolisConstructor,boolisDOMObject,constnsACString&objectTag):id(id),isCallable(isCallable),isConstructor(isConstructor),isDOMObject(isDOMObject),objectTag(objectTag){}};WrapperOwner::WrapperOwner():inactive_(false){}staticinlineAuxCPOWData*AuxCPOWDataOf(JSObject*obj){MOZ_ASSERT(IsCPOW(obj));returnstatic_cast<AuxCPOWData*>(GetProxyExtra(obj,1).toPrivate());}staticinlineWrapperOwner*OwnerOf(JSObject*obj){MOZ_ASSERT(IsCPOW(obj));returnreinterpret_cast<WrapperOwner*>(GetProxyExtra(obj,0).toPrivate());}ObjectIdWrapperOwner::idOfUnchecked(JSObject*obj){MOZ_ASSERT(IsCPOW(obj));AuxCPOWData*aux=AuxCPOWDataOf(obj);MOZ_ASSERT(!aux->id.isNull());returnaux->id;}ObjectIdWrapperOwner::idOf(JSObject*obj){ObjectIdobjId=idOfUnchecked(obj);MOZ_ASSERT(hasCPOW(objId,obj));returnobjId;}classCPOWProxyHandler:publicBaseProxyHandler{public:constexprCPOWProxyHandler():BaseProxyHandler(&family){}virtualboolfinalizeInBackground(constValue&priv)constoverride{returnfalse;}virtualboolgetOwnPropertyDescriptor(JSContext*cx,HandleObjectproxy,HandleIdid,MutableHandle<PropertyDescriptor>desc)constoverride;virtualbooldefineProperty(JSContext*cx,HandleObjectproxy,HandleIdid,Handle<PropertyDescriptor>desc,ObjectOpResult&result)constoverride;virtualboolownPropertyKeys(JSContext*cx,HandleObjectproxy,AutoIdVector&props)constoverride;virtualbooldelete_(JSContext*cx,HandleObjectproxy,HandleIdid,ObjectOpResult&result)constoverride;virtualboolenumerate(JSContext*cx,HandleObjectproxy,MutableHandleObjectobjp)constoverride;virtualboolpreventExtensions(JSContext*cx,HandleObjectproxy,ObjectOpResult&result)constoverride;virtualboolisExtensible(JSContext*cx,HandleObjectproxy,bool*extensible)constoverride;virtualboolhas(JSContext*cx,HandleObjectproxy,HandleIdid,bool*bp)constoverride;virtualboolget(JSContext*cx,HandleObjectproxy,HandleValuereceiver,HandleIdid,MutableHandleValuevp)constoverride;virtualboolset(JSContext*cx,JS::HandleObjectproxy,JS::HandleIdid,JS::HandleValuev,JS::HandleValuereceiver,JS::ObjectOpResult&result)constoverride;virtualboolcall(JSContext*cx,HandleObjectproxy,constCallArgs&args)constoverride;virtualboolconstruct(JSContext*cx,HandleObjectproxy,constCallArgs&args)constoverride;virtualboolgetPropertyDescriptor(JSContext*cx,HandleObjectproxy,HandleIdid,MutableHandle<PropertyDescriptor>desc)constoverride;virtualboolhasOwn(JSContext*cx,HandleObjectproxy,HandleIdid,bool*bp)constoverride;virtualboolgetOwnEnumerablePropertyKeys(JSContext*cx,HandleObjectproxy,AutoIdVector&props)constoverride;virtualboolhasInstance(JSContext*cx,HandleObjectproxy,MutableHandleValuev,bool*bp)constoverride;virtualboolgetBuiltinClass(JSContext*cx,HandleObjectobj,js::ESClass*cls)constoverride;virtualboolisArray(JSContext*cx,HandleObjectobj,IsArrayAnswer*answer)constoverride;virtualconstchar*className(JSContext*cx,HandleObjectproxy)constoverride;virtualboolregexp_toShared(JSContext*cx,HandleObjectproxy,RegExpGuard*g)constoverride;virtualvoidfinalize(JSFreeOp*fop,JSObject*proxy)constoverride;virtualvoidobjectMoved(JSObject*proxy,constJSObject*old)constoverride;virtualboolisCallable(JSObject*obj)constoverride;virtualboolisConstructor(JSObject*obj)constoverride;virtualboolgetPrototype(JSContext*cx,HandleObjectproxy,MutableHandleObjectprotop)constoverride;virtualboolgetPrototypeIfOrdinary(JSContext*cx,HandleObjectproxy,bool*isOrdinary,MutableHandleObjectprotop)constoverride;staticconstcharfamily;staticconstCPOWProxyHandlersingleton;};constcharCPOWProxyHandler::family=0;constCPOWProxyHandlerCPOWProxyHandler::singleton;#define FORWARD(call, args) \ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::JS); \ WrapperOwner* owner = OwnerOf(proxy); \ if (!owner->active()) { \ JS_ReportErrorASCII(cx, "cannot use a CPOW whose process is gone"); \ return false; \ } \ if (!owner->allowMessage(cx)) { \ return false; \ } \ { \ CPOWTimer timer(cx); \ return owner->call args; \ }boolCPOWProxyHandler::getPropertyDescriptor(JSContext*cx,HandleObjectproxy,HandleIdid,MutableHandle<PropertyDescriptor>desc)const{FORWARD(getPropertyDescriptor,(cx,proxy,id,desc));}boolWrapperOwner::getPropertyDescriptor(JSContext*cx,HandleObjectproxy,HandleIdid,MutableHandle<PropertyDescriptor>desc){ObjectIdobjId=idOf(proxy);JSIDVariantidVar;if(!toJSIDVariant(cx,id,&idVar))returnfalse;ReturnStatusstatus;PPropertyDescriptorresult;if(!SendGetPropertyDescriptor(objId,idVar,&status,&result))returnipcfail(cx);LOG_STACK();if(!ok(cx,status))returnfalse;returntoDescriptor(cx,result,desc);}boolCPOWProxyHandler::getOwnPropertyDescriptor(JSContext*cx,HandleObjectproxy,HandleIdid,MutableHandle<PropertyDescriptor>desc)const{FORWARD(getOwnPropertyDescriptor,(cx,proxy,id,desc));}boolWrapperOwner::getOwnPropertyDescriptor(JSContext*cx,HandleObjectproxy,HandleIdid,MutableHandle<PropertyDescriptor>desc){ObjectIdobjId=idOf(proxy);JSIDVariantidVar;if(!toJSIDVariant(cx,id,&idVar))returnfalse;ReturnStatusstatus;PPropertyDescriptorresult;if(!SendGetOwnPropertyDescriptor(objId,idVar,&status,&result))returnipcfail(cx);LOG_STACK();if(!ok(cx,status))returnfalse;returntoDescriptor(cx,result,desc);}boolCPOWProxyHandler::defineProperty(JSContext*cx,HandleObjectproxy,HandleIdid,Handle<PropertyDescriptor>desc,ObjectOpResult&result)const{FORWARD(defineProperty,(cx,proxy,id,desc,result));}boolWrapperOwner::defineProperty(JSContext*cx,HandleObjectproxy,HandleIdid,Handle<PropertyDescriptor>desc,ObjectOpResult&result){ObjectIdobjId=idOf(proxy);JSIDVariantidVar;if(!toJSIDVariant(cx,id,&idVar))returnfalse;PPropertyDescriptordescriptor;if(!fromDescriptor(cx,desc,&descriptor))returnfalse;ReturnStatusstatus;if(!SendDefineProperty(objId,idVar,descriptor,&status))returnipcfail(cx);LOG_STACK();returnok(cx,status,result);}boolCPOWProxyHandler::ownPropertyKeys(JSContext*cx,HandleObjectproxy,AutoIdVector&props)const{FORWARD(ownPropertyKeys,(cx,proxy,props));}boolWrapperOwner::ownPropertyKeys(JSContext*cx,HandleObjectproxy,AutoIdVector&props){returngetPropertyKeys(cx,proxy,JSITER_OWNONLY|JSITER_HIDDEN|JSITER_SYMBOLS,props);}boolCPOWProxyHandler::delete_(JSContext*cx,HandleObjectproxy,HandleIdid,ObjectOpResult&result)const{FORWARD(delete_,(cx,proxy,id,result));}boolWrapperOwner::delete_(JSContext*cx,HandleObjectproxy,HandleIdid,ObjectOpResult&result){ObjectIdobjId=idOf(proxy);JSIDVariantidVar;if(!toJSIDVariant(cx,id,&idVar))returnfalse;ReturnStatusstatus;if(!SendDelete(objId,idVar,&status))returnipcfail(cx);LOG_STACK();returnok(cx,status,result);}boolCPOWProxyHandler::enumerate(JSContext*cx,HandleObjectproxy,MutableHandleObjectobjp)const{// Using a CPOW for the Iterator would slow down for .. in performance, instead// call the base hook, that will use our implementation of getOwnEnumerablePropertyKeys// and follow the proto chain.returnBaseProxyHandler::enumerate(cx,proxy,objp);}boolCPOWProxyHandler::has(JSContext*cx,HandleObjectproxy,HandleIdid,bool*bp)const{FORWARD(has,(cx,proxy,id,bp));}boolWrapperOwner::has(JSContext*cx,HandleObjectproxy,HandleIdid,bool*bp){ObjectIdobjId=idOf(proxy);JSIDVariantidVar;if(!toJSIDVariant(cx,id,&idVar))returnfalse;ReturnStatusstatus;if(!SendHas(objId,idVar,&status,bp))returnipcfail(cx);LOG_STACK();returnok(cx,status);}boolCPOWProxyHandler::hasOwn(JSContext*cx,HandleObjectproxy,HandleIdid,bool*bp)const{FORWARD(hasOwn,(cx,proxy,id,bp));}boolWrapperOwner::hasOwn(JSContext*cx,HandleObjectproxy,HandleIdid,bool*bp){ObjectIdobjId=idOf(proxy);JSIDVariantidVar;if(!toJSIDVariant(cx,id,&idVar))returnfalse;ReturnStatusstatus;if(!SendHasOwn(objId,idVar,&status,bp))returnipcfail(cx);LOG_STACK();return!!ok(cx,status);}boolCPOWProxyHandler::get(JSContext*cx,HandleObjectproxy,HandleValuereceiver,HandleIdid,MutableHandleValuevp)const{FORWARD(get,(cx,proxy,receiver,id,vp));}staticboolCPOWDOMQI(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);if(!args.thisv().isObject()||!IsCPOW(&args.thisv().toObject())){JS_ReportErrorASCII(cx,"bad this object passed to special QI");returnfalse;}RootedObjectproxy(cx,&args.thisv().toObject());FORWARD(DOMQI,(cx,proxy,args));}staticboolCPOWToString(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedObjectcallee(cx,&args.callee());RootedValuecpowValue(cx);if(!JS_GetProperty(cx,callee,"__cpow__",&cpowValue))returnfalse;if(!cpowValue.isObject()||!IsCPOW(&cpowValue.toObject())){JS_ReportErrorASCII(cx,"CPOWToString called on an incompatible object");returnfalse;}RootedObjectproxy(cx,&cpowValue.toObject());FORWARD(toString,(cx,proxy,args));}boolWrapperOwner::toString(JSContext*cx,HandleObjectcpow,JS::CallArgs&args){// Ask the other side to call its toString method. Update the callee so that// it points to the CPOW and not to the synthesized CPOWToString function.args.setCallee(ObjectValue(*cpow));if(!callOrConstruct(cx,cpow,args,false))returnfalse;if(!args.rval().isString())returntrue;RootedStringcpowResult(cx,args.rval().toString());nsAutoJSStringtoStringResult;if(!toStringResult.init(cx,cpowResult))returnfalse;// We don't want to wrap toString() results for things like the location// object, where toString() is supposed to return a URL and nothing else.nsAutoStringresult;if(toStringResult[0]=='['){result.AppendLiteral("[object CPOW ");result+=toStringResult;result.AppendLiteral("]");}else{result+=toStringResult;}JSString*str=JS_NewUCStringCopyN(cx,result.get(),result.Length());if(!str)returnfalse;args.rval().setString(str);returntrue;}boolWrapperOwner::DOMQI(JSContext*cx,JS::HandleObjectproxy,JS::CallArgs&args){// Someone's calling us, handle nsISupports specially to avoid unnecessary// CPOW traffic.HandleValueid=args[0];if(id.isObject()){RootedObjectidobj(cx,&id.toObject());nsCOMPtr<nsIJSID>jsid;nsresultrv=UnwrapArg<nsIJSID>(idobj,getter_AddRefs(jsid));if(NS_SUCCEEDED(rv)){MOZ_ASSERT(jsid,"bad wrapJS");constnsID*idptr=jsid->GetID();if(idptr->Equals(NS_GET_IID(nsISupports))){args.rval().set(args.thisv());returntrue;}// Webidl-implemented DOM objects never have nsIClassInfo.if(idptr->Equals(NS_GET_IID(nsIClassInfo)))returnThrow(cx,NS_ERROR_NO_INTERFACE);}}// It wasn't nsISupports, call into the other process to do the QI for us// (since we don't know what other interfaces our object supports). Note// that we have to use JS_GetPropertyDescriptor here to avoid infinite// recursion back into CPOWDOMQI via WrapperOwner::get().// We could stash the actual QI function on our own function object to avoid// if we're called multiple times, but since we're transient, there's no// point right now.JS::Rooted<PropertyDescriptor>propDesc(cx);if(!JS_GetPropertyDescriptor(cx,proxy,"QueryInterface",&propDesc))returnfalse;if(!propDesc.value().isObject()){MOZ_ASSERT_UNREACHABLE("We didn't get QueryInterface off a node");returnThrow(cx,NS_ERROR_UNEXPECTED);}returnJS_CallFunctionValue(cx,proxy,propDesc.value(),args,args.rval());}boolWrapperOwner::get(JSContext*cx,HandleObjectproxy,HandleValuereceiver,HandleIdid,MutableHandleValuevp){ObjectIdobjId=idOf(proxy);JSVariantreceiverVar;if(!toVariant(cx,receiver,&receiverVar))returnfalse;JSIDVariantidVar;if(!toJSIDVariant(cx,id,&idVar))returnfalse;AuxCPOWData*data=AuxCPOWDataOf(proxy);if(data->isDOMObject&&idVar.type()==JSIDVariant::TnsString&&idVar.get_nsString().EqualsLiteral("QueryInterface")){// Handle QueryInterface on DOM Objects specially since we can assume// certain things about their implementation.RootedFunctionqi(cx,JS_NewFunction(cx,CPOWDOMQI,1,0,"QueryInterface"));if(!qi)returnfalse;vp.set(ObjectValue(*JS_GetFunctionObject(qi)));returntrue;}JSVariantval;ReturnStatusstatus;if(!SendGet(objId,receiverVar,idVar,&status,&val))returnipcfail(cx);LOG_STACK();if(!ok(cx,status))returnfalse;if(!fromVariant(cx,val,vp))returnfalse;if(idVar.type()==JSIDVariant::TnsString&&idVar.get_nsString().EqualsLiteral("toString")){RootedFunctiontoString(cx,JS_NewFunction(cx,CPOWToString,0,0,"toString"));if(!toString)returnfalse;RootedObjecttoStringObj(cx,JS_GetFunctionObject(toString));if(!JS_DefineProperty(cx,toStringObj,"__cpow__",vp,JSPROP_PERMANENT|JSPROP_READONLY))returnfalse;vp.set(ObjectValue(*toStringObj));}returntrue;}boolCPOWProxyHandler::set(JSContext*cx,JS::HandleObjectproxy,JS::HandleIdid,JS::HandleValuev,JS::HandleValuereceiver,JS::ObjectOpResult&result)const{FORWARD(set,(cx,proxy,id,v,receiver,result));}boolWrapperOwner::set(JSContext*cx,JS::HandleObjectproxy,JS::HandleIdid,JS::HandleValuev,JS::HandleValuereceiver,JS::ObjectOpResult&result){ObjectIdobjId=idOf(proxy);JSIDVariantidVar;if(!toJSIDVariant(cx,id,&idVar))returnfalse;JSVariantval;if(!toVariant(cx,v,&val))returnfalse;JSVariantreceiverVar;if(!toVariant(cx,receiver,&receiverVar))returnfalse;ReturnStatusstatus;if(!SendSet(objId,idVar,val,receiverVar,&status))returnipcfail(cx);LOG_STACK();returnok(cx,status,result);}boolCPOWProxyHandler::getOwnEnumerablePropertyKeys(JSContext*cx,HandleObjectproxy,AutoIdVector&props)const{FORWARD(getOwnEnumerablePropertyKeys,(cx,proxy,props));}boolWrapperOwner::getOwnEnumerablePropertyKeys(JSContext*cx,HandleObjectproxy,AutoIdVector&props){returngetPropertyKeys(cx,proxy,JSITER_OWNONLY,props);}boolCPOWProxyHandler::preventExtensions(JSContext*cx,HandleObjectproxy,ObjectOpResult&result)const{FORWARD(preventExtensions,(cx,proxy,result));}boolWrapperOwner::preventExtensions(JSContext*cx,HandleObjectproxy,ObjectOpResult&result){ObjectIdobjId=idOf(proxy);ReturnStatusstatus;if(!SendPreventExtensions(objId,&status))returnipcfail(cx);LOG_STACK();returnok(cx,status,result);}boolCPOWProxyHandler::isExtensible(JSContext*cx,HandleObjectproxy,bool*extensible)const{FORWARD(isExtensible,(cx,proxy,extensible));}boolWrapperOwner::isExtensible(JSContext*cx,HandleObjectproxy,bool*extensible){ObjectIdobjId=idOf(proxy);ReturnStatusstatus;if(!SendIsExtensible(objId,&status,extensible))returnipcfail(cx);LOG_STACK();returnok(cx,status);}boolCPOWProxyHandler::call(JSContext*cx,HandleObjectproxy,constCallArgs&args)const{FORWARD(callOrConstruct,(cx,proxy,args,false));}boolCPOWProxyHandler::construct(JSContext*cx,HandleObjectproxy,constCallArgs&args)const{FORWARD(callOrConstruct,(cx,proxy,args,true));}boolWrapperOwner::callOrConstruct(JSContext*cx,HandleObjectproxy,constCallArgs&args,boolconstruct){ObjectIdobjId=idOf(proxy);InfallibleTArray<JSParam>vals;AutoValueVectoroutobjects(cx);RootedValuev(cx);for(size_ti=0;i<args.length()+2;i++){// The |this| value for constructors is a magic value that we won't be// able to convert, so skip it.if(i==1&&construct)v=UndefinedValue();elsev=args.base()[i];if(v.isObject()){RootedObjectobj(cx,&v.toObject());if(xpc::IsOutObject(cx,obj)){// Make sure it is not an in-out object.boolfound;if(!JS_HasProperty(cx,obj,"value",&found))returnfalse;if(found){JS_ReportErrorASCII(cx,"in-out objects cannot be sent via CPOWs yet");returnfalse;}vals.AppendElement(JSParam(void_t()));if(!outobjects.append(ObjectValue(*obj)))returnfalse;continue;}}JSVariantval;if(!toVariant(cx,v,&val))returnfalse;vals.AppendElement(JSParam(val));}JSVariantresult;ReturnStatusstatus;InfallibleTArray<JSParam>outparams;if(!SendCallOrConstruct(objId,vals,construct,&status,&result,&outparams))returnipcfail(cx);LOG_STACK();if(!ok(cx,status))returnfalse;if(outparams.Length()!=outobjects.length())returnipcfail(cx);RootedObjectobj(cx);for(size_ti=0;i<outparams.Length();i++){// Don't bother doing anything for outparams that weren't set.if(outparams[i].type()==JSParam::Tvoid_t)continue;// Take the value the child process returned, and set it on the XPC// object.if(!fromVariant(cx,outparams[i],&v))returnfalse;obj=&outobjects[i].toObject();if(!JS_SetProperty(cx,obj,"value",v))returnfalse;}if(!fromVariant(cx,result,args.rval()))returnfalse;returntrue;}boolCPOWProxyHandler::hasInstance(JSContext*cx,HandleObjectproxy,MutableHandleValuev,bool*bp)const{FORWARD(hasInstance,(cx,proxy,v,bp));}boolWrapperOwner::hasInstance(JSContext*cx,HandleObjectproxy,MutableHandleValuev,bool*bp){ObjectIdobjId=idOf(proxy);JSVariantvVar;if(!toVariant(cx,v,&vVar))returnfalse;ReturnStatusstatus;JSVariantresult;if(!SendHasInstance(objId,vVar,&status,bp))returnipcfail(cx);LOG_STACK();returnok(cx,status);}boolCPOWProxyHandler::getBuiltinClass(JSContext*cx,HandleObjectproxy,ESClass*cls)const{FORWARD(getBuiltinClass,(cx,proxy,cls));}boolWrapperOwner::getBuiltinClass(JSContext*cx,HandleObjectproxy,ESClass*cls){ObjectIdobjId=idOf(proxy);uint32_tclassValue=uint32_t(ESClass::Other);ReturnStatusstatus;if(!SendGetBuiltinClass(objId,&status,&classValue))returnipcfail(cx);*cls=ESClass(classValue);LOG_STACK();returnok(cx,status);}boolCPOWProxyHandler::isArray(JSContext*cx,HandleObjectproxy,IsArrayAnswer*answer)const{FORWARD(isArray,(cx,proxy,answer));}boolWrapperOwner::isArray(JSContext*cx,HandleObjectproxy,IsArrayAnswer*answer){ObjectIdobjId=idOf(proxy);uint32_tans;ReturnStatusstatus;if(!SendIsArray(objId,&status,&ans))returnipcfail(cx);LOG_STACK();*answer=IsArrayAnswer(ans);MOZ_ASSERT(*answer==IsArrayAnswer::Array||*answer==IsArrayAnswer::NotArray||*answer==IsArrayAnswer::RevokedProxy);returnok(cx,status);}constchar*CPOWProxyHandler::className(JSContext*cx,HandleObjectproxy)const{WrapperOwner*parent=OwnerOf(proxy);if(!parent->active())return"<dead CPOW>";returnparent->className(cx,proxy);}constchar*WrapperOwner::className(JSContext*cx,HandleObjectproxy){AuxCPOWData*data=AuxCPOWDataOf(proxy);if(data->className.IsEmpty()){ObjectIdobjId=idOf(proxy);if(!SendClassName(objId,&data->className))return"<error>";LOG_STACK();}returndata->className.get();}boolCPOWProxyHandler::getPrototype(JSContext*cx,HandleObjectproxy,MutableHandleObjectobjp)const{FORWARD(getPrototype,(cx,proxy,objp));}boolWrapperOwner::getPrototype(JSContext*cx,HandleObjectproxy,MutableHandleObjectobjp){ObjectIdobjId=idOf(proxy);ObjectOrNullVariantval;ReturnStatusstatus;if(!SendGetPrototype(objId,&status,&val))returnipcfail(cx);LOG_STACK();if(!ok(cx,status))returnfalse;objp.set(fromObjectOrNullVariant(cx,val));returntrue;}boolCPOWProxyHandler::getPrototypeIfOrdinary(JSContext*cx,HandleObjectproxy,bool*isOrdinary,MutableHandleObjectobjp)const{FORWARD(getPrototypeIfOrdinary,(cx,proxy,isOrdinary,objp));}boolWrapperOwner::getPrototypeIfOrdinary(JSContext*cx,HandleObjectproxy,bool*isOrdinary,MutableHandleObjectobjp){ObjectIdobjId=idOf(proxy);ObjectOrNullVariantval;ReturnStatusstatus;if(!SendGetPrototypeIfOrdinary(objId,&status,isOrdinary,&val))returnipcfail(cx);LOG_STACK();if(!ok(cx,status))returnfalse;objp.set(fromObjectOrNullVariant(cx,val));returntrue;}boolCPOWProxyHandler::regexp_toShared(JSContext*cx,HandleObjectproxy,RegExpGuard*g)const{FORWARD(regexp_toShared,(cx,proxy,g));}boolWrapperOwner::regexp_toShared(JSContext*cx,HandleObjectproxy,RegExpGuard*g){ObjectIdobjId=idOf(proxy);ReturnStatusstatus;nsStringsource;unsignedflags=0;if(!SendRegExpToShared(objId,&status,&source,&flags))returnipcfail(cx);LOG_STACK();if(!ok(cx,status))returnfalse;RootedObjectregexp(cx);regexp=JS_NewUCRegExpObject(cx,source.get(),source.Length(),flags);if(!regexp)returnfalse;returnjs::RegExpToSharedNonInline(cx,regexp,g);}voidCPOWProxyHandler::finalize(JSFreeOp*fop,JSObject*proxy)const{AuxCPOWData*aux=AuxCPOWDataOf(proxy);OwnerOf(proxy)->drop(proxy);if(aux)deleteaux;}voidCPOWProxyHandler::objectMoved(JSObject*proxy,constJSObject*old)const{OwnerOf(proxy)->updatePointer(proxy,old);}boolCPOWProxyHandler::isCallable(JSObject*proxy)const{AuxCPOWData*aux=AuxCPOWDataOf(proxy);returnaux->isCallable;}boolCPOWProxyHandler::isConstructor(JSObject*proxy)const{AuxCPOWData*aux=AuxCPOWDataOf(proxy);returnaux->isConstructor;}voidWrapperOwner::drop(JSObject*obj){ObjectIdobjId=idOf(obj);cpows_.remove(objId);if(active())Unused<<SendDropObject(objId);decref();}voidWrapperOwner::updatePointer(JSObject*obj,constJSObject*old){ObjectIdobjId=idOfUnchecked(obj);MOZ_ASSERT(hasCPOW(objId,old));cpows_.add(objId,obj);}boolWrapperOwner::init(){if(!JavaScriptShared::init())returnfalse;returntrue;}boolWrapperOwner::getPropertyKeys(JSContext*cx,HandleObjectproxy,uint32_tflags,AutoIdVector&props){ObjectIdobjId=idOf(proxy);ReturnStatusstatus;InfallibleTArray<JSIDVariant>ids;if(!SendGetPropertyKeys(objId,flags,&status,&ids))returnipcfail(cx);LOG_STACK();if(!ok(cx,status))returnfalse;for(size_ti=0;i<ids.Length();i++){RootedIdid(cx);if(!fromJSIDVariant(cx,ids[i],&id))returnfalse;if(!props.append(id))returnfalse;}returntrue;}namespacemozilla{namespacejsipc{boolIsCPOW(JSObject*obj){returnIsProxy(obj)&&GetProxyHandler(obj)==&CPOWProxyHandler::singleton;}boolIsWrappedCPOW(JSObject*obj){JSObject*unwrapped=js::UncheckedUnwrap(obj,true);if(!unwrapped)returnfalse;returnIsCPOW(unwrapped);}voidGetWrappedCPOWTag(JSObject*obj,nsACString&out){JSObject*unwrapped=js::UncheckedUnwrap(obj,true);MOZ_ASSERT(IsCPOW(unwrapped));AuxCPOWData*aux=AuxCPOWDataOf(unwrapped);if(aux)out=aux->objectTag;}nsresultInstanceOf(JSObject*proxy,constnsID*id,bool*bp){WrapperOwner*parent=OwnerOf(proxy);if(!parent->active())returnNS_ERROR_UNEXPECTED;returnparent->instanceOf(proxy,id,bp);}boolDOMInstanceOf(JSContext*cx,JSObject*proxyArg,intprototypeID,intdepth,bool*bp){RootedObjectproxy(cx,proxyArg);FORWARD(domInstanceOf,(cx,proxy,prototypeID,depth,bp));}}/* namespace jsipc */}/* namespace mozilla */nsresultWrapperOwner::instanceOf(JSObject*obj,constnsID*id,bool*bp){ObjectIdobjId=idOf(obj);JSIIDiid;ConvertID(*id,&iid);ReturnStatusstatus;if(!SendInstanceOf(objId,iid,&status,bp))returnNS_ERROR_UNEXPECTED;if(status.type()!=ReturnStatus::TReturnSuccess)returnNS_ERROR_UNEXPECTED;returnNS_OK;}boolWrapperOwner::domInstanceOf(JSContext*cx,JSObject*obj,intprototypeID,intdepth,bool*bp){ObjectIdobjId=idOf(obj);ReturnStatusstatus;if(!SendDOMInstanceOf(objId,prototypeID,depth,&status,bp))returnipcfail(cx);LOG_STACK();returnok(cx,status);}voidWrapperOwner::ActorDestroy(ActorDestroyReasonwhy){inactive_=true;objects_.clear();unwaivedObjectIds_.clear();waivedObjectIds_.clear();}boolWrapperOwner::ipcfail(JSContext*cx){JS_ReportErrorASCII(cx,"cross-process JS call failed");returnfalse;}boolWrapperOwner::ok(JSContext*cx,constReturnStatus&status){if(status.type()==ReturnStatus::TReturnSuccess)returntrue;if(status.type()==ReturnStatus::TReturnStopIteration)returnJS_ThrowStopIteration(cx);if(status.type()==ReturnStatus::TReturnDeadCPOW){JS_ReportErrorASCII(cx,"operation not possible on dead CPOW");returnfalse;}RootedValueexn(cx);if(!fromVariant(cx,status.get_ReturnException().exn(),&exn))returnfalse;JS_SetPendingException(cx,exn);returnfalse;}boolWrapperOwner::ok(JSContext*cx,constReturnStatus&status,ObjectOpResult&result){if(status.type()==ReturnStatus::TReturnObjectOpResult)returnresult.fail(status.get_ReturnObjectOpResult().code());if(!ok(cx,status))returnfalse;returnresult.succeed();}// CPOWs can have a tag string attached to them, originating in the local// process from this function. It's sent with the CPOW to the remote process,// where it can be fetched with Components.utils.getCrossProcessWrapperTag.staticnsCStringGetRemoteObjectTag(JS::Handle<JSObject*>obj){if(nsCOMPtr<nsISupports>supports=xpc::UnwrapReflectorToISupports(obj)){nsCOMPtr<nsIDocShellTreeItem>treeItem(do_QueryInterface(supports));if(treeItem)returnNS_LITERAL_CSTRING("ContentDocShellTreeItem");nsCOMPtr<nsIDOMDocument>doc(do_QueryInterface(supports));if(doc)returnNS_LITERAL_CSTRING("ContentDocument");}returnNS_LITERAL_CSTRING("generic");}staticRemoteObjectMakeRemoteObject(JSContext*cx,ObjectIdid,HandleObjectobj){returnRemoteObject(id.serialize(),JS::IsCallable(obj),JS::IsConstructor(obj),dom::IsDOMObject(obj),GetRemoteObjectTag(obj));}boolWrapperOwner::toObjectVariant(JSContext*cx,JSObject*objArg,ObjectVariant*objVarp){RootedObjectobj(cx,objArg);MOZ_ASSERT(obj);// We always save objects unwrapped in the CPOW table. If we stored// wrappers, then the wrapper might be GCed while the target remained alive.// Whenever operating on an object that comes from the table, we wrap it// in findObjectById.unsignedwrapperFlags=0;obj=js::UncheckedUnwrap(obj,true,&wrapperFlags);if(obj&&IsCPOW(obj)&&OwnerOf(obj)==this){*objVarp=LocalObject(idOf(obj).serialize());returntrue;}boolwaiveXray=wrapperFlags&xpc::WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG;ObjectIdid=objectIdMap(waiveXray).find(obj);if(!id.isNull()){MOZ_ASSERT(id.hasXrayWaiver()==waiveXray);*objVarp=MakeRemoteObject(cx,id,obj);returntrue;}// Need to call PreserveWrapper on |obj| in case it's a reflector.// FIXME: What if it's an XPCWrappedNative?if(mozilla::dom::IsDOMObject(obj))mozilla::dom::TryPreserveWrapper(obj);id=ObjectId(nextSerialNumber_++,waiveXray);if(!objects_.add(id,obj))returnfalse;if(!objectIdMap(waiveXray).add(cx,obj,id))returnfalse;*objVarp=MakeRemoteObject(cx,id,obj);returntrue;}JSObject*WrapperOwner::fromObjectVariant(JSContext*cx,ObjectVariantobjVar){if(objVar.type()==ObjectVariant::TRemoteObject){returnfromRemoteObjectVariant(cx,objVar.get_RemoteObject());}else{returnfromLocalObjectVariant(cx,objVar.get_LocalObject());}}JSObject*WrapperOwner::fromRemoteObjectVariant(JSContext*cx,RemoteObjectobjVar){ObjectIdobjId=ObjectId::deserialize(objVar.serializedId());RootedObjectobj(cx,findCPOWById(objId));if(!obj){// All CPOWs live in the privileged junk scope.RootedObjectjunkScope(cx,xpc::PrivilegedJunkScope());JSAutoCompartmentac(cx,junkScope);RootedValuev(cx,UndefinedValue());// We need to setLazyProto for the getPrototype/getPrototypeIfOrdinary// hooks.ProxyOptionsoptions;options.setLazyProto(true);obj=NewProxyObject(cx,&CPOWProxyHandler::singleton,v,nullptr,options);if(!obj)returnnullptr;if(!cpows_.add(objId,obj))returnnullptr;// Incref once we know the decref will be called.incref();AuxCPOWData*aux=newAuxCPOWData(objId,objVar.isCallable(),objVar.isConstructor(),objVar.isDOMObject(),objVar.objectTag());SetProxyExtra(obj,0,PrivateValue(this));SetProxyExtra(obj,1,PrivateValue(aux));}if(!JS_WrapObject(cx,&obj))returnnullptr;returnobj;}JSObject*WrapperOwner::fromLocalObjectVariant(JSContext*cx,LocalObjectobjVar){ObjectIdid=ObjectId::deserialize(objVar.serializedId());Rooted<JSObject*>obj(cx,findObjectById(cx,id));if(!obj)returnnullptr;if(!JS_WrapObject(cx,&obj))returnnullptr;returnobj;}