1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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 "BindingUtils.h"
- #include <algorithm>
- #include <stdarg.h>
- #include "mozilla/Assertions.h"
- #include "mozilla/DebugOnly.h"
- #include "mozilla/FloatingPoint.h"
- #include "mozilla/Preferences.h"
- #include "mozilla/SizePrintfMacros.h"
- #include "mozilla/Unused.h"
- #include "mozilla/UseCounter.h"
- #include "mozilla/dom/DocGroup.h"
- #include "AccessCheck.h"
- #include "jsfriendapi.h"
- #include "nsContentCreatorFunctions.h"
- #include "nsContentUtils.h"
- #include "nsGlobalWindow.h"
- #include "nsHTMLTags.h"
- #include "nsIDocShell.h"
- #include "nsIDOMGlobalPropertyInitializer.h"
- #include "nsIPermissionManager.h"
- #include "nsIPrincipal.h"
- #include "nsIXPConnect.h"
- #include "nsUTF8Utils.h"
- #include "WorkerPrivate.h"
- #include "WorkerRunnable.h"
- #include "WrapperFactory.h"
- #include "xpcprivate.h"
- #include "XrayWrapper.h"
- #include "nsPrintfCString.h"
- #include "mozilla/Sprintf.h"
- #include "nsGlobalWindow.h"
- #include "mozilla/dom/ScriptSettings.h"
- #include "mozilla/dom/CustomElementRegistry.h"
- #include "mozilla/dom/DOMError.h"
- #include "mozilla/dom/DOMErrorBinding.h"
- #include "mozilla/dom/DOMException.h"
- #include "mozilla/dom/ElementBinding.h"
- #include "mozilla/dom/HTMLObjectElement.h"
- #include "mozilla/dom/HTMLObjectElementBinding.h"
- #include "mozilla/dom/HTMLSharedObjectElement.h"
- #include "mozilla/dom/HTMLElementBinding.h"
- #include "mozilla/dom/HTMLEmbedElementBinding.h"
- #include "mozilla/dom/HTMLAppletElementBinding.h"
- #include "mozilla/dom/Promise.h"
- #include "mozilla/dom/ResolveSystemBinding.h"
- #include "mozilla/dom/WebIDLGlobalNameHash.h"
- #include "mozilla/dom/WorkerPrivate.h"
- #include "mozilla/dom/WorkerScope.h"
- #include "mozilla/dom/XrayExpandoClass.h"
- #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
- #include "nsDOMClassInfo.h"
- #include "ipc/ErrorIPCUtils.h"
- #include "mozilla/UseCounter.h"
- namespace mozilla {
- namespace dom {
- using namespace workers;
- // Forward declare GetConstructorObject methods.
- #define HTML_TAG(_tag, _classname, _interfacename) \
- namespace HTML##_interfacename##ElementBinding { \
- JSObject* GetConstructorObject(JSContext*); \
- }
- #define HTML_OTHER(_tag)
- #include "nsHTMLTagList.h"
- #undef HTML_TAG
- #undef HTML_OTHER
- typedef JSObject* (*constructorGetterCallback)(JSContext*);
- // Mapping of html tag and GetConstructorObject methods.
- #define HTML_TAG(_tag, _classname, _interfacename) HTML##_interfacename##ElementBinding::GetConstructorObject,
- #define HTML_OTHER(_tag) nullptr,
- // We use eHTMLTag_foo (where foo is the tag) which is defined in nsHTMLTags.h
- // to index into this array.
- static const constructorGetterCallback sConstructorGetterCallback[] = {
- HTMLUnknownElementBinding::GetConstructorObject,
- #include "nsHTMLTagList.h"
- #undef HTML_TAG
- #undef HTML_OTHER
- };
- const JSErrorFormatString ErrorFormatString[] = {
- #define MSG_DEF(_name, _argc, _exn, _str) \
- { #_name, _str, _argc, _exn },
- #include "mozilla/dom/Errors.msg"
- #undef MSG_DEF
- };
- #define MSG_DEF(_name, _argc, _exn, _str) \
- static_assert(_argc < JS::MaxNumErrorArguments, \
- #_name " must only have as many error arguments as the JS engine can support");
- #include "mozilla/dom/Errors.msg"
- #undef MSG_DEF
- const JSErrorFormatString*
- GetErrorMessage(void* aUserRef, const unsigned aErrorNumber)
- {
- MOZ_ASSERT(aErrorNumber < ArrayLength(ErrorFormatString));
- return &ErrorFormatString[aErrorNumber];
- }
- uint16_t
- GetErrorArgCount(const ErrNum aErrorNumber)
- {
- return GetErrorMessage(nullptr, aErrorNumber)->argCount;
- }
- void
- binding_detail::ThrowErrorMessage(JSContext* aCx, const unsigned aErrorNumber, ...)
- {
- va_list ap;
- va_start(ap, aErrorNumber);
- JS_ReportErrorNumberUTF8VA(aCx, GetErrorMessage, nullptr, aErrorNumber, ap);
- va_end(ap);
- }
- bool
- ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
- bool aSecurityError, const char* aInterfaceName)
- {
- NS_ConvertASCIItoUTF16 ifaceName(aInterfaceName);
- // This should only be called for DOM methods/getters/setters, which
- // are JSNative-backed functions, so we can assume that
- // JS_ValueToFunction and JS_GetFunctionDisplayId will both return
- // non-null and that JS_GetStringCharsZ returns non-null.
- JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, aArgs.calleev()));
- MOZ_ASSERT(func);
- JS::Rooted<JSString*> funcName(aCx, JS_GetFunctionDisplayId(func));
- MOZ_ASSERT(funcName);
- nsAutoJSString funcNameStr;
- if (!funcNameStr.init(aCx, funcName)) {
- return false;
- }
- const ErrNum errorNumber = aSecurityError ?
- MSG_METHOD_THIS_UNWRAPPING_DENIED :
- MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
- MOZ_RELEASE_ASSERT(GetErrorArgCount(errorNumber) <= 2);
- JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr,
- static_cast<unsigned>(errorNumber),
- funcNameStr.get(), ifaceName.get());
- return false;
- }
- bool
- ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
- bool aSecurityError,
- prototypes::ID aProtoId)
- {
- return ThrowInvalidThis(aCx, aArgs, aSecurityError,
- NamesOfInterfacesWithProtos(aProtoId));
- }
- bool
- ThrowNoSetterArg(JSContext* aCx, prototypes::ID aProtoId)
- {
- nsPrintfCString errorMessage("%s attribute setter",
- NamesOfInterfacesWithProtos(aProtoId));
- return ThrowErrorMessage(aCx, MSG_MISSING_ARGUMENTS, errorMessage.get());
- }
- } // namespace dom
- namespace binding_danger {
- template<typename CleanupPolicy>
- struct TErrorResult<CleanupPolicy>::Message {
- Message() { MOZ_COUNT_CTOR(TErrorResult::Message); }
- ~Message() { MOZ_COUNT_DTOR(TErrorResult::Message); }
- nsTArray<nsString> mArgs;
- dom::ErrNum mErrorNumber;
- bool HasCorrectNumberOfArguments()
- {
- return GetErrorArgCount(mErrorNumber) == mArgs.Length();
- }
- };
- template<typename CleanupPolicy>
- nsTArray<nsString>&
- TErrorResult<CleanupPolicy>::CreateErrorMessageHelper(const dom::ErrNum errorNumber,
- nsresult errorType)
- {
- AssertInOwningThread();
- mResult = errorType;
- mMessage = new Message();
- mMessage->mErrorNumber = errorNumber;
- return mMessage->mArgs;
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::SerializeMessage(IPC::Message* aMsg) const
- {
- using namespace IPC;
- AssertInOwningThread();
- MOZ_ASSERT(mUnionState == HasMessage);
- MOZ_ASSERT(mMessage);
- WriteParam(aMsg, mMessage->mArgs);
- WriteParam(aMsg, mMessage->mErrorNumber);
- }
- template<typename CleanupPolicy>
- bool
- TErrorResult<CleanupPolicy>::DeserializeMessage(const IPC::Message* aMsg,
- PickleIterator* aIter)
- {
- using namespace IPC;
- AssertInOwningThread();
- nsAutoPtr<Message> readMessage(new Message());
- if (!ReadParam(aMsg, aIter, &readMessage->mArgs) ||
- !ReadParam(aMsg, aIter, &readMessage->mErrorNumber)) {
- return false;
- }
- if (!readMessage->HasCorrectNumberOfArguments()) {
- return false;
- }
- MOZ_ASSERT(mUnionState == HasNothing);
- mMessage = readMessage.forget();
- #ifdef DEBUG
- mUnionState = HasMessage;
- #endif // DEBUG
- return true;
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::SetPendingExceptionWithMessage(JSContext* aCx)
- {
- AssertInOwningThread();
- MOZ_ASSERT(mMessage, "SetPendingExceptionWithMessage() can be called only once");
- MOZ_ASSERT(mUnionState == HasMessage);
- Message* message = mMessage;
- MOZ_RELEASE_ASSERT(message->HasCorrectNumberOfArguments());
- const uint32_t argCount = message->mArgs.Length();
- const char16_t* args[JS::MaxNumErrorArguments + 1];
- for (uint32_t i = 0; i < argCount; ++i) {
- args[i] = message->mArgs.ElementAt(i).get();
- }
- args[argCount] = nullptr;
- JS_ReportErrorNumberUCArray(aCx, dom::GetErrorMessage, nullptr,
- static_cast<unsigned>(message->mErrorNumber),
- argCount > 0 ? args : nullptr);
- ClearMessage();
- mResult = NS_OK;
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::ClearMessage()
- {
- AssertInOwningThread();
- MOZ_ASSERT(IsErrorWithMessage());
- delete mMessage;
- mMessage = nullptr;
- #ifdef DEBUG
- mUnionState = HasNothing;
- #endif // DEBUG
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn)
- {
- AssertInOwningThread();
- MOZ_ASSERT(mMightHaveUnreportedJSException,
- "Why didn't you tell us you planned to throw a JS exception?");
- ClearUnionData();
- // Make sure mJSException is initialized _before_ we try to root it. But
- // don't set it to exn yet, because we don't want to do that until after we
- // root.
- mJSException.asValueRef().setUndefined();
- if (!js::AddRawValueRoot(cx, &mJSException.asValueRef(), "TErrorResult::mJSException")) {
- // Don't use NS_ERROR_DOM_JS_EXCEPTION, because that indicates we have
- // in fact rooted mJSException.
- mResult = NS_ERROR_OUT_OF_MEMORY;
- } else {
- mJSException = exn;
- mResult = NS_ERROR_DOM_JS_EXCEPTION;
- #ifdef DEBUG
- mUnionState = HasJSException;
- #endif // DEBUG
- }
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::SetPendingJSException(JSContext* cx)
- {
- AssertInOwningThread();
- MOZ_ASSERT(!mMightHaveUnreportedJSException,
- "Why didn't you tell us you planned to handle JS exceptions?");
- MOZ_ASSERT(mUnionState == HasJSException);
- JS::Rooted<JS::Value> exception(cx, mJSException);
- if (JS_WrapValue(cx, &exception)) {
- JS_SetPendingException(cx, exception);
- }
- mJSException = exception;
- // If JS_WrapValue failed, not much we can do about it... No matter
- // what, go ahead and unroot mJSException.
- js::RemoveRawValueRoot(cx, &mJSException.asValueRef());
- mResult = NS_OK;
- #ifdef DEBUG
- mUnionState = HasNothing;
- #endif // DEBUG
- }
- template<typename CleanupPolicy>
- struct TErrorResult<CleanupPolicy>::DOMExceptionInfo {
- DOMExceptionInfo(nsresult rv, const nsACString& message)
- : mMessage(message)
- , mRv(rv)
- {}
- nsCString mMessage;
- nsresult mRv;
- };
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::SerializeDOMExceptionInfo(IPC::Message* aMsg) const
- {
- using namespace IPC;
- AssertInOwningThread();
- MOZ_ASSERT(mDOMExceptionInfo);
- MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
- WriteParam(aMsg, mDOMExceptionInfo->mMessage);
- WriteParam(aMsg, mDOMExceptionInfo->mRv);
- }
- template<typename CleanupPolicy>
- bool
- TErrorResult<CleanupPolicy>::DeserializeDOMExceptionInfo(const IPC::Message* aMsg,
- PickleIterator* aIter)
- {
- using namespace IPC;
- AssertInOwningThread();
- nsCString message;
- nsresult rv;
- if (!ReadParam(aMsg, aIter, &message) ||
- !ReadParam(aMsg, aIter, &rv)) {
- return false;
- }
- MOZ_ASSERT(mUnionState == HasNothing);
- MOZ_ASSERT(IsDOMException());
- mDOMExceptionInfo = new DOMExceptionInfo(rv, message);
- #ifdef DEBUG
- mUnionState = HasDOMExceptionInfo;
- #endif // DEBUG
- return true;
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::ThrowDOMException(nsresult rv,
- const nsACString& message)
- {
- AssertInOwningThread();
- ClearUnionData();
- mResult = NS_ERROR_DOM_DOMEXCEPTION;
- mDOMExceptionInfo = new DOMExceptionInfo(rv, message);
- #ifdef DEBUG
- mUnionState = HasDOMExceptionInfo;
- #endif
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::SetPendingDOMException(JSContext* cx)
- {
- AssertInOwningThread();
- MOZ_ASSERT(mDOMExceptionInfo,
- "SetPendingDOMException() can be called only once");
- MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
- dom::Throw(cx, mDOMExceptionInfo->mRv, mDOMExceptionInfo->mMessage);
- ClearDOMExceptionInfo();
- mResult = NS_OK;
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::ClearDOMExceptionInfo()
- {
- AssertInOwningThread();
- MOZ_ASSERT(IsDOMException());
- MOZ_ASSERT(mUnionState == HasDOMExceptionInfo || !mDOMExceptionInfo);
- delete mDOMExceptionInfo;
- mDOMExceptionInfo = nullptr;
- #ifdef DEBUG
- mUnionState = HasNothing;
- #endif // DEBUG
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::ClearUnionData()
- {
- AssertInOwningThread();
- if (IsJSException()) {
- JSContext* cx = dom::danger::GetJSContext();
- MOZ_ASSERT(cx);
- mJSException.asValueRef().setUndefined();
- js::RemoveRawValueRoot(cx, &mJSException.asValueRef());
- #ifdef DEBUG
- mUnionState = HasNothing;
- #endif // DEBUG
- } else if (IsErrorWithMessage()) {
- ClearMessage();
- } else if (IsDOMException()) {
- ClearDOMExceptionInfo();
- }
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::SetPendingGenericErrorException(JSContext* cx)
- {
- AssertInOwningThread();
- MOZ_ASSERT(!IsErrorWithMessage());
- MOZ_ASSERT(!IsJSException());
- MOZ_ASSERT(!IsDOMException());
- dom::Throw(cx, ErrorCode());
- mResult = NS_OK;
- }
- template<typename CleanupPolicy>
- TErrorResult<CleanupPolicy>&
- TErrorResult<CleanupPolicy>::operator=(TErrorResult<CleanupPolicy>&& aRHS)
- {
- AssertInOwningThread();
- aRHS.AssertInOwningThread();
- // Clear out any union members we may have right now, before we
- // start writing to it.
- ClearUnionData();
- #ifdef DEBUG
- mMightHaveUnreportedJSException = aRHS.mMightHaveUnreportedJSException;
- aRHS.mMightHaveUnreportedJSException = false;
- #endif
- if (aRHS.IsErrorWithMessage()) {
- mMessage = aRHS.mMessage;
- aRHS.mMessage = nullptr;
- } else if (aRHS.IsJSException()) {
- JSContext* cx = dom::danger::GetJSContext();
- MOZ_ASSERT(cx);
- mJSException.asValueRef().setUndefined();
- if (!js::AddRawValueRoot(cx, &mJSException.asValueRef(), "TErrorResult::mJSException")) {
- MOZ_CRASH("Could not root mJSException, we're about to OOM");
- }
- mJSException = aRHS.mJSException;
- aRHS.mJSException.asValueRef().setUndefined();
- js::RemoveRawValueRoot(cx, &aRHS.mJSException.asValueRef());
- } else if (aRHS.IsDOMException()) {
- mDOMExceptionInfo = aRHS.mDOMExceptionInfo;
- aRHS.mDOMExceptionInfo = nullptr;
- } else {
- // Null out the union on both sides for hygiene purposes.
- mMessage = aRHS.mMessage = nullptr;
- }
- #ifdef DEBUG
- mUnionState = aRHS.mUnionState;
- aRHS.mUnionState = HasNothing;
- #endif // DEBUG
- // Note: It's important to do this last, since this affects the condition
- // checks above!
- mResult = aRHS.mResult;
- aRHS.mResult = NS_OK;
- return *this;
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::CloneTo(TErrorResult& aRv) const
- {
- AssertInOwningThread();
- aRv.AssertInOwningThread();
- aRv.ClearUnionData();
- aRv.mResult = mResult;
- #ifdef DEBUG
- aRv.mMightHaveUnreportedJSException = mMightHaveUnreportedJSException;
- #endif
- if (IsErrorWithMessage()) {
- #ifdef DEBUG
- aRv.mUnionState = HasMessage;
- #endif
- aRv.mMessage = new Message();
- aRv.mMessage->mArgs = mMessage->mArgs;
- aRv.mMessage->mErrorNumber = mMessage->mErrorNumber;
- } else if (IsDOMException()) {
- #ifdef DEBUG
- aRv.mUnionState = HasDOMExceptionInfo;
- #endif
- aRv.mDOMExceptionInfo = new DOMExceptionInfo(mDOMExceptionInfo->mRv,
- mDOMExceptionInfo->mMessage);
- } else if (IsJSException()) {
- #ifdef DEBUG
- aRv.mUnionState = HasJSException;
- #endif
- JSContext* cx = dom::danger::GetJSContext();
- JS::Rooted<JS::Value> exception(cx, mJSException.asValueRef());
- aRv.ThrowJSException(cx, exception);
- }
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::SuppressException()
- {
- AssertInOwningThread();
- WouldReportJSException();
- ClearUnionData();
- // We don't use AssignErrorCode, because we want to override existing error
- // states, which AssignErrorCode is not allowed to do.
- mResult = NS_OK;
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::SetPendingException(JSContext* cx)
- {
- AssertInOwningThread();
- if (IsUncatchableException()) {
- // Nuke any existing exception on cx, to make sure we're uncatchable.
- JS_ClearPendingException(cx);
- // Don't do any reporting. Just return, to create an
- // uncatchable exception.
- mResult = NS_OK;
- return;
- }
- if (IsJSContextException()) {
- // Whatever we need to throw is on the JSContext already.
- MOZ_ASSERT(JS_IsExceptionPending(cx));
- mResult = NS_OK;
- return;
- }
- if (IsErrorWithMessage()) {
- SetPendingExceptionWithMessage(cx);
- return;
- }
- if (IsJSException()) {
- SetPendingJSException(cx);
- return;
- }
- if (IsDOMException()) {
- SetPendingDOMException(cx);
- return;
- }
- SetPendingGenericErrorException(cx);
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::StealExceptionFromJSContext(JSContext* cx)
- {
- AssertInOwningThread();
- MOZ_ASSERT(mMightHaveUnreportedJSException,
- "Why didn't you tell us you planned to throw a JS exception?");
- JS::Rooted<JS::Value> exn(cx);
- if (!JS_GetPendingException(cx, &exn)) {
- ThrowUncatchableException();
- return;
- }
- ThrowJSException(cx, exn);
- JS_ClearPendingException(cx);
- }
- template<typename CleanupPolicy>
- void
- TErrorResult<CleanupPolicy>::NoteJSContextException(JSContext* aCx)
- {
- AssertInOwningThread();
- if (JS_IsExceptionPending(aCx)) {
- mResult = NS_ERROR_DOM_EXCEPTION_ON_JSCONTEXT;
- } else {
- mResult = NS_ERROR_UNCATCHABLE_EXCEPTION;
- }
- }
- template class TErrorResult<JustAssertCleanupPolicy>;
- template class TErrorResult<AssertAndSuppressCleanupPolicy>;
- template class TErrorResult<JustSuppressCleanupPolicy>;
- } // namespace binding_danger
- namespace dom {
- bool
- DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
- const ConstantSpec* cs)
- {
- JS::Rooted<JS::Value> value(cx);
- for (; cs->name; ++cs) {
- value = cs->value;
- bool ok =
- JS_DefineProperty(cx, obj, cs->name, value,
- JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
- if (!ok) {
- return false;
- }
- }
- return true;
- }
- static inline bool
- Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSFunctionSpec* spec) {
- return JS_DefineFunctions(cx, obj, spec);
- }
- static inline bool
- Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSPropertySpec* spec) {
- return JS_DefineProperties(cx, obj, spec);
- }
- static inline bool
- Define(JSContext* cx, JS::Handle<JSObject*> obj, const ConstantSpec* spec) {
- return DefineConstants(cx, obj, spec);
- }
- template<typename T>
- bool
- DefinePrefable(JSContext* cx, JS::Handle<JSObject*> obj,
- const Prefable<T>* props)
- {
- MOZ_ASSERT(props);
- MOZ_ASSERT(props->specs);
- do {
- // Define if enabled
- if (props->isEnabled(cx, obj)) {
- if (!Define(cx, obj, props->specs)) {
- return false;
- }
- }
- } while ((++props)->specs);
- return true;
- }
- bool
- DefineUnforgeableMethods(JSContext* cx, JS::Handle<JSObject*> obj,
- const Prefable<const JSFunctionSpec>* props)
- {
- return DefinePrefable(cx, obj, props);
- }
- bool
- DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj,
- const Prefable<const JSPropertySpec>* props)
- {
- return DefinePrefable(cx, obj, props);
- }
- // We should use JSFunction objects for interface objects, but we need a custom
- // hasInstance hook because we have new interface objects on prototype chains of
- // old (XPConnect-based) bindings. We also need Xrays and arbitrary numbers of
- // reserved slots (e.g. for named constructors). So we define a custom
- // funToString ObjectOps member for interface objects.
- JSString*
- InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
- unsigned /* indent */)
- {
- const js::Class* clasp = js::GetObjectClass(aObject);
- MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
- const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
- DOMIfaceAndProtoJSClass::FromJSClass(clasp);
- return JS_NewStringCopyZ(aCx, ifaceAndProtoJSClass->mToString);
- }
- bool
- Constructor(JSContext* cx, unsigned argc, JS::Value* vp)
- {
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- const JS::Value& v =
- js::GetFunctionNativeReserved(&args.callee(),
- CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
- const JSNativeHolder* nativeHolder =
- static_cast<const JSNativeHolder*>(v.toPrivate());
- return (nativeHolder->mNative)(cx, argc, vp);
- }
- static JSObject*
- CreateConstructor(JSContext* cx, JS::Handle<JSObject*> global, const char* name,
- const JSNativeHolder* nativeHolder, unsigned ctorNargs)
- {
- JSFunction* fun = js::NewFunctionWithReserved(cx, Constructor, ctorNargs,
- JSFUN_CONSTRUCTOR, name);
- if (!fun) {
- return nullptr;
- }
- JSObject* constructor = JS_GetFunctionObject(fun);
- js::SetFunctionNativeReserved(constructor,
- CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT,
- js::PrivateValue(const_cast<JSNativeHolder*>(nativeHolder)));
- return constructor;
- }
- static bool
- DefineConstructor(JSContext* cx, JS::Handle<JSObject*> global, const char* name,
- JS::Handle<JSObject*> constructor)
- {
- bool alreadyDefined;
- if (!JS_AlreadyHasOwnProperty(cx, global, name, &alreadyDefined)) {
- return false;
- }
- // This is Enumerable: False per spec.
- return alreadyDefined ||
- JS_DefineProperty(cx, global, name, constructor, JSPROP_RESOLVING);
- }
- static JSObject*
- CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
- JS::Handle<JSObject*> constructorProto,
- const js::Class* constructorClass,
- unsigned ctorNargs, const NamedConstructor* namedConstructors,
- JS::Handle<JSObject*> proto,
- const NativeProperties* properties,
- const NativeProperties* chromeOnlyProperties,
- const char* name, bool defineOnGlobal)
- {
- JS::Rooted<JSObject*> constructor(cx);
- MOZ_ASSERT(constructorProto);
- MOZ_ASSERT(constructorClass);
- constructor = JS_NewObjectWithGivenProto(cx, Jsvalify(constructorClass),
- constructorProto);
- if (!constructor) {
- return nullptr;
- }
- if (!JS_DefineProperty(cx, constructor, "length", ctorNargs,
- JSPROP_READONLY)) {
- return nullptr;
- }
- // Might as well intern, since we're going to need an atomized
- // version of name anyway when we stick our constructor on the
- // global.
- JS::Rooted<JSString*> nameStr(cx, JS_AtomizeAndPinString(cx, name));
- if (!nameStr) {
- return nullptr;
- }
- if (!JS_DefineProperty(cx, constructor, "name", nameStr, JSPROP_READONLY)) {
- return nullptr;
- }
- if (DOMIfaceAndProtoJSClass::FromJSClass(constructorClass)->wantsInterfaceHasInstance) {
- JS::Rooted<jsid> hasInstanceId(cx,
- SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::hasInstance)));
- if (!JS_DefineFunctionById(cx, constructor, hasInstanceId,
- InterfaceHasInstance, 1,
- // Flags match those of Function[Symbol.hasInstance]
- JSPROP_READONLY | JSPROP_PERMANENT)) {
- return nullptr;
- }
- }
- if (properties) {
- if (properties->HasStaticMethods() &&
- !DefinePrefable(cx, constructor, properties->StaticMethods())) {
- return nullptr;
- }
- if (properties->HasStaticAttributes() &&
- !DefinePrefable(cx, constructor, properties->StaticAttributes())) {
- return nullptr;
- }
- if (properties->HasConstants() &&
- !DefinePrefable(cx, constructor, properties->Constants())) {
- return nullptr;
- }
- }
- if (chromeOnlyProperties) {
- if (chromeOnlyProperties->HasStaticMethods() &&
- !DefinePrefable(cx, constructor,
- chromeOnlyProperties->StaticMethods())) {
- return nullptr;
- }
- if (chromeOnlyProperties->HasStaticAttributes() &&
- !DefinePrefable(cx, constructor,
- chromeOnlyProperties->StaticAttributes())) {
- return nullptr;
- }
- if (chromeOnlyProperties->HasConstants() &&
- !DefinePrefable(cx, constructor, chromeOnlyProperties->Constants())) {
- return nullptr;
- }
- }
- if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) {
- return nullptr;
- }
- if (defineOnGlobal && !DefineConstructor(cx, global, name, constructor)) {
- return nullptr;
- }
- if (namedConstructors) {
- int namedConstructorSlot = DOM_INTERFACE_SLOTS_BASE;
- while (namedConstructors->mName) {
- JS::Rooted<JSObject*> namedConstructor(cx,
- CreateConstructor(cx, global, namedConstructors->mName,
- &namedConstructors->mHolder,
- namedConstructors->mNargs));
- if (!namedConstructor ||
- !JS_DefineProperty(cx, namedConstructor, "prototype",
- proto,
- JSPROP_PERMANENT | JSPROP_READONLY,
- JS_STUBGETTER, JS_STUBSETTER) ||
- (defineOnGlobal &&
- !DefineConstructor(cx, global, namedConstructors->mName,
- namedConstructor))) {
- return nullptr;
- }
- js::SetReservedSlot(constructor, namedConstructorSlot++,
- JS::ObjectValue(*namedConstructor));
- ++namedConstructors;
- }
- }
- return constructor;
- }
- static JSObject*
- CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
- JS::Handle<JSObject*> parentProto,
- const js::Class* protoClass,
- const NativeProperties* properties,
- const NativeProperties* chromeOnlyProperties,
- const char* const* unscopableNames,
- bool isGlobal)
- {
- JS::Rooted<JSObject*> ourProto(cx,
- JS_NewObjectWithUniqueType(cx, Jsvalify(protoClass), parentProto));
- if (!ourProto ||
- // We don't try to define properties on the global's prototype; those
- // properties go on the global itself.
- (!isGlobal &&
- !DefineProperties(cx, ourProto, properties, chromeOnlyProperties))) {
- return nullptr;
- }
- if (unscopableNames) {
- JS::Rooted<JSObject*> unscopableObj(cx, JS_NewPlainObject(cx));
- if (!unscopableObj) {
- return nullptr;
- }
- for (; *unscopableNames; ++unscopableNames) {
- if (!JS_DefineProperty(cx, unscopableObj, *unscopableNames,
- JS::TrueHandleValue, JSPROP_ENUMERATE)) {
- return nullptr;
- }
- }
- JS::Rooted<jsid> unscopableId(cx,
- SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::unscopables)));
- // Readonly and non-enumerable to match Array.prototype.
- if (!JS_DefinePropertyById(cx, ourProto, unscopableId, unscopableObj,
- JSPROP_READONLY)) {
- return nullptr;
- }
- }
- return ourProto;
- }
- bool
- DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj,
- const NativeProperties* properties,
- const NativeProperties* chromeOnlyProperties)
- {
- if (properties) {
- if (properties->HasMethods() &&
- !DefinePrefable(cx, obj, properties->Methods())) {
- return false;
- }
- if (properties->HasAttributes() &&
- !DefinePrefable(cx, obj, properties->Attributes())) {
- return false;
- }
- if (properties->HasConstants() &&
- !DefinePrefable(cx, obj, properties->Constants())) {
- return false;
- }
- }
- if (chromeOnlyProperties) {
- if (chromeOnlyProperties->HasMethods() &&
- !DefinePrefable(cx, obj, chromeOnlyProperties->Methods())) {
- return false;
- }
- if (chromeOnlyProperties->HasAttributes() &&
- !DefinePrefable(cx, obj, chromeOnlyProperties->Attributes())) {
- return false;
- }
- if (chromeOnlyProperties->HasConstants() &&
- !DefinePrefable(cx, obj, chromeOnlyProperties->Constants())) {
- return false;
- }
- }
- return true;
- }
- void
- CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
- JS::Handle<JSObject*> protoProto,
- const js::Class* protoClass, JS::Heap<JSObject*>* protoCache,
- JS::Handle<JSObject*> constructorProto,
- const js::Class* constructorClass,
- unsigned ctorNargs, const NamedConstructor* namedConstructors,
- JS::Heap<JSObject*>* constructorCache,
- const NativeProperties* properties,
- const NativeProperties* chromeOnlyProperties,
- const char* name, bool defineOnGlobal,
- const char* const* unscopableNames,
- bool isGlobal)
- {
- MOZ_ASSERT(protoClass || constructorClass,
- "Need at least one class!");
- MOZ_ASSERT(!((properties &&
- (properties->HasMethods() || properties->HasAttributes())) ||
- (chromeOnlyProperties &&
- (chromeOnlyProperties->HasMethods() ||
- chromeOnlyProperties->HasAttributes()))) || protoClass,
- "Methods or properties but no protoClass!");
- MOZ_ASSERT(!((properties &&
- (properties->HasStaticMethods() ||
- properties->HasStaticAttributes())) ||
- (chromeOnlyProperties &&
- (chromeOnlyProperties->HasStaticMethods() ||
- chromeOnlyProperties->HasStaticAttributes()))) ||
- constructorClass,
- "Static methods but no constructorClass!");
- MOZ_ASSERT(bool(name) == bool(constructorClass),
- "Must have name precisely when we have an interface object");
- MOZ_ASSERT(!protoClass == !protoCache,
- "If, and only if, there is an interface prototype object we need "
- "to cache it");
- MOZ_ASSERT(bool(constructorClass) == bool(constructorCache),
- "If, and only if, there is an interface object we need to cache "
- "it");
- MOZ_ASSERT(constructorProto || !constructorClass,
- "Must have a constructor proto if we plan to create a constructor "
- "object");
- JS::Rooted<JSObject*> proto(cx);
- if (protoClass) {
- proto =
- CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
- properties, chromeOnlyProperties,
- unscopableNames, isGlobal);
- if (!proto) {
- return;
- }
- *protoCache = proto;
- }
- else {
- MOZ_ASSERT(!proto);
- }
- JSObject* interface;
- if (constructorClass) {
- interface = CreateInterfaceObject(cx, global, constructorProto,
- constructorClass, ctorNargs,
- namedConstructors, proto, properties,
- chromeOnlyProperties, name,
- defineOnGlobal);
- if (!interface) {
- if (protoCache) {
- // If we fail we need to make sure to clear the value of protoCache we
- // set above.
- *protoCache = nullptr;
- }
- return;
- }
- *constructorCache = interface;
- }
- }
- bool
- NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
- JS::Handle<JSObject*> aScope,
- JS::MutableHandle<JS::Value> aRetval,
- xpcObjectHelper& aHelper,
- const nsIID* aIID,
- bool aAllowNativeWrapper)
- {
- js::AssertSameCompartment(aCx, aScope);
- nsresult rv;
- // Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need
- // on all threads.
- nsWrapperCache *cache = aHelper.GetWrapperCache();
- if (cache && cache->IsDOMBinding()) {
- JS::Rooted<JSObject*> obj(aCx, cache->GetWrapper());
- if (!obj) {
- obj = cache->WrapObject(aCx, nullptr);
- }
- if (obj && aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) {
- return false;
- }
- if (obj) {
- aRetval.setObject(*obj);
- return true;
- }
- }
- MOZ_ASSERT(NS_IsMainThread());
- if (!XPCConvert::NativeInterface2JSObject(aRetval, nullptr, aHelper, aIID,
- aAllowNativeWrapper, &rv)) {
- // I can't tell if NativeInterface2JSObject throws JS exceptions
- // or not. This is a sloppy stab at the right semantics; the
- // method really ought to be fixed to behave consistently.
- if (!JS_IsExceptionPending(aCx)) {
- Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
- }
- return false;
- }
- return true;
- }
- bool
- TryPreserveWrapper(JSObject* obj)
- {
- MOZ_ASSERT(IsDOMObject(obj));
- if (nsISupports* native = UnwrapDOMObjectToISupports(obj)) {
- nsWrapperCache* cache = nullptr;
- CallQueryInterface(native, &cache);
- if (cache) {
- cache->PreserveWrapper(native);
- }
- return true;
- }
- // If this DOMClass is not cycle collected, then it isn't wrappercached,
- // so it does not need to be preserved. If it is cycle collected, then
- // we can't tell if it is wrappercached or not, so we just return false.
- const DOMJSClass* domClass = GetDOMClass(obj);
- return domClass && !domClass->mParticipant;
- }
- // Can only be called with a DOM JSClass.
- bool
- InstanceClassHasProtoAtDepth(const js::Class* clasp,
- uint32_t protoID, uint32_t depth)
- {
- const DOMJSClass* domClass = DOMJSClass::FromJSClass(clasp);
- return static_cast<uint32_t>(domClass->mInterfaceChain[depth]) == protoID;
- }
- // Only set allowNativeWrapper to false if you really know you need it, if in
- // doubt use true. Setting it to false disables security wrappers.
- bool
- XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
- xpcObjectHelper& helper, const nsIID* iid,
- bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval)
- {
- if (!NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, helper, iid,
- allowNativeWrapper)) {
- return false;
- }
- #ifdef DEBUG
- JSObject* jsobj = rval.toObjectOrNull();
- if (jsobj &&
- js::GetGlobalForObjectCrossCompartment(jsobj) == jsobj) {
- NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
- "Why did we recreate this wrapper?");
- }
- #endif
- return true;
- }
- bool
- VariantToJsval(JSContext* aCx, nsIVariant* aVariant,
- JS::MutableHandle<JS::Value> aRetval)
- {
- nsresult rv;
- if (!XPCVariant::VariantDataToJS(aVariant, &rv, aRetval)) {
- // Does it throw? Who knows
- if (!JS_IsExceptionPending(aCx)) {
- Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
- }
- return false;
- }
- return true;
- }
- bool
- QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp)
- {
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- JS::Rooted<JS::Value> thisv(cx, JS_THIS(cx, vp));
- if (thisv.isNull())
- return false;
- // Get the object. It might be a security wrapper, in which case we do a checked
- // unwrap.
- JS::Rooted<JSObject*> origObj(cx, &thisv.toObject());
- JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(origObj,
- /* stopAtWindowProxy = */ false));
- if (!obj) {
- JS_ReportErrorASCII(cx, "Permission denied to access object");
- return false;
- }
- // Switch this to UnwrapDOMObjectToISupports once our global objects are
- // using new bindings.
- nsCOMPtr<nsISupports> native;
- UnwrapArg<nsISupports>(obj, getter_AddRefs(native));
- if (!native) {
- return Throw(cx, NS_ERROR_FAILURE);
- }
- if (argc < 1) {
- return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
- }
- if (!args[0].isObject()) {
- return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
- }
- nsCOMPtr<nsIJSID> iid;
- obj = &args[0].toObject();
- if (NS_FAILED(UnwrapArg<nsIJSID>(obj, getter_AddRefs(iid)))) {
- return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
- }
- MOZ_ASSERT(iid);
- if (iid->GetID()->Equals(NS_GET_IID(nsIClassInfo))) {
- nsresult rv;
- nsCOMPtr<nsIClassInfo> ci = do_QueryInterface(native, &rv);
- if (NS_FAILED(rv)) {
- return Throw(cx, rv);
- }
- return WrapObject(cx, ci, &NS_GET_IID(nsIClassInfo), args.rval());
- }
- nsCOMPtr<nsISupports> unused;
- nsresult rv = native->QueryInterface(*iid->GetID(), getter_AddRefs(unused));
- if (NS_FAILED(rv)) {
- return Throw(cx, rv);
- }
- *vp = thisv;
- return true;
- }
- void
- GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
- nsWrapperCache* aCache, nsIJSID* aIID,
- JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
- {
- const nsID* iid = aIID->GetID();
- RefPtr<nsISupports> result;
- aError = aRequestor->GetInterface(*iid, getter_AddRefs(result));
- if (aError.Failed()) {
- return;
- }
- if (!WrapObject(aCx, result, iid, aRetval)) {
- aError.Throw(NS_ERROR_FAILURE);
- }
- }
- bool
- ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
- {
- return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
- }
- bool
- ThrowConstructorWithoutNew(JSContext* cx, const char* name)
- {
- return ThrowErrorMessage(cx, MSG_CONSTRUCTOR_WITHOUT_NEW, name);
- }
- inline const NativePropertyHooks*
- GetNativePropertyHooksFromConstructorFunction(JS::Handle<JSObject*> obj)
- {
- MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
- const JS::Value& v =
- js::GetFunctionNativeReserved(obj,
- CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
- const JSNativeHolder* nativeHolder =
- static_cast<const JSNativeHolder*>(v.toPrivate());
- return nativeHolder->mPropertyHooks;
- }
- inline const NativePropertyHooks*
- GetNativePropertyHooks(JSContext *cx, JS::Handle<JSObject*> obj,
- DOMObjectType& type)
- {
- const js::Class* clasp = js::GetObjectClass(obj);
- const DOMJSClass* domClass = GetDOMClass(clasp);
- if (domClass) {
- bool isGlobal = (clasp->flags & JSCLASS_DOM_GLOBAL) != 0;
- type = isGlobal ? eGlobalInstance : eInstance;
- return domClass->mNativeHooks;
- }
- if (JS_ObjectIsFunction(cx, obj)) {
- type = eInterface;
- return GetNativePropertyHooksFromConstructorFunction(obj);
- }
- MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(obj)));
- const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
- DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj));
- type = ifaceAndProtoJSClass->mType;
- return ifaceAndProtoJSClass->mNativeHooks;
- }
- static JSObject*
- XrayCreateFunction(JSContext* cx, JS::Handle<JSObject*> wrapper,
- JSNativeWrapper native, unsigned nargs, JS::Handle<jsid> id)
- {
- JSFunction* fun;
- if (JSID_IS_STRING(id)) {
- fun = js::NewFunctionByIdWithReserved(cx, native.op, nargs, 0, id);
- } else {
- // Can't pass this id (probably a symbol) to NewFunctionByIdWithReserved;
- // just use an empty name for lack of anything better.
- fun = js::NewFunctionWithReserved(cx, native.op, nargs, 0, nullptr);
- }
- if (!fun) {
- return nullptr;
- }
- SET_JITINFO(fun, native.info);
- JSObject* obj = JS_GetFunctionObject(fun);
- js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT,
- JS::ObjectValue(*wrapper));
- #ifdef DEBUG
- js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF,
- JS::ObjectValue(*obj));
- #endif
- return obj;
- }
- static bool
- XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper,
- JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
- const Prefable<const JSPropertySpec>* attributes,
- const jsid* attributeIds,
- const JSPropertySpec* attributeSpecs,
- JS::MutableHandle<JS::PropertyDescriptor> desc,
- bool& cacheOnHolder)
- {
- for (; attributes->specs; ++attributes) {
- if (attributes->isEnabled(cx, obj)) {
- // Set i to be the index into our full list of ids/specs that we're
- // looking at now.
- size_t i = attributes->specs - attributeSpecs;
- for ( ; attributeIds[i] != JSID_VOID; ++i) {
- if (id == attributeIds[i]) {
- cacheOnHolder = true;
- const JSPropertySpec& attrSpec = attributeSpecs[i];
- // Because of centralization, we need to make sure we fault in the
- // JitInfos as well. At present, until the JSAPI changes, the easiest
- // way to do this is wrap them up as functions ourselves.
- desc.setAttributes(attrSpec.flags);
- // They all have getters, so we can just make it.
- JS::Rooted<JSObject*> funobj(cx,
- XrayCreateFunction(cx, wrapper, attrSpec.accessors.getter.native, 0, id));
- if (!funobj)
- return false;
- desc.setGetterObject(funobj);
- desc.attributesRef() |= JSPROP_GETTER;
- if (attrSpec.accessors.setter.native.op) {
- // We have a setter! Make it.
- funobj =
- XrayCreateFunction(cx, wrapper, attrSpec.accessors.setter.native, 1, id);
- if (!funobj)
- return false;
- desc.setSetterObject(funobj);
- desc.attributesRef() |= JSPROP_SETTER;
- } else {
- desc.setSetter(nullptr);
- }
- desc.object().set(wrapper);
- return true;
- }
- }
- }
- }
- return true;
- }
- static bool
- XrayResolveMethod(JSContext* cx, JS::Handle<JSObject*> wrapper,
- JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
- const Prefable<const JSFunctionSpec>* methods,
- const jsid* methodIds,
- const JSFunctionSpec* methodSpecs,
- JS::MutableHandle<JS::PropertyDescriptor> desc,
- bool& cacheOnHolder)
- {
- const Prefable<const JSFunctionSpec>* method;
- for (method = methods; method->specs; ++method) {
- if (method->isEnabled(cx, obj)) {
- // Set i to be the index into our full list of ids/specs that we're
- // looking at now.
- size_t i = method->specs - methodSpecs;
- for ( ; methodIds[i] != JSID_VOID; ++i) {
- if (id == methodIds[i]) {
- cacheOnHolder = true;
- const JSFunctionSpec& methodSpec = methodSpecs[i];
- JSObject *funobj;
- if (methodSpec.selfHostedName) {
- JSFunction* fun =
- JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, id,
- methodSpec.nargs);
- if (!fun) {
- return false;
- }
- MOZ_ASSERT(!methodSpec.call.op, "Bad FunctionSpec declaration: non-null native");
- MOZ_ASSERT(!methodSpec.call.info, "Bad FunctionSpec declaration: non-null jitinfo");
- funobj = JS_GetFunctionObject(fun);
- } else {
- funobj = XrayCreateFunction(cx, wrapper, methodSpec.call,
- methodSpec.nargs, id);
- if (!funobj) {
- return false;
- }
- }
- desc.value().setObject(*funobj);
- desc.setAttributes(methodSpec.flags);
- desc.object().set(wrapper);
- desc.setSetter(nullptr);
- desc.setGetter(nullptr);
- return true;
- }
- }
- }
- }
- return true;
- }
- // Try to resolve a property as an unforgeable property from the given
- // NativeProperties, if it's there. nativeProperties is allowed to be null (in
- // which case we of course won't resolve anything).
- static bool
- XrayResolveUnforgeableProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
- JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
- JS::MutableHandle<JS::PropertyDescriptor> desc,
- bool& cacheOnHolder,
- const NativeProperties* nativeProperties)
- {
- if (!nativeProperties) {
- return true;
- }
- if (nativeProperties->HasUnforgeableAttributes()) {
- if (!XrayResolveAttribute(cx, wrapper, obj, id,
- nativeProperties->UnforgeableAttributes(),
- nativeProperties->UnforgeableAttributeIds(),
- nativeProperties->UnforgeableAttributeSpecs(),
- desc, cacheOnHolder)) {
- return false;
- }
- if (desc.object()) {
- return true;
- }
- }
- if (nativeProperties->HasUnforgeableMethods()) {
- if (!XrayResolveMethod(cx, wrapper, obj, id,
- nativeProperties->UnforgeableMethods(),
- nativeProperties->UnforgeableMethodIds(),
- nativeProperties->UnforgeableMethodSpecs(),
- desc, cacheOnHolder)) {
- return false;
- }
- if (desc.object()) {
- return true;
- }
- }
- return true;
- }
- static bool
- XrayResolveProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
- JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
- JS::MutableHandle<JS::PropertyDescriptor> desc,
- bool& cacheOnHolder, DOMObjectType type,
- const NativeProperties* nativeProperties)
- {
- bool hasMethods = false;
- if (type == eInterface) {
- hasMethods = nativeProperties->HasStaticMethods();
- } else {
- hasMethods = nativeProperties->HasMethods();
- }
- if (hasMethods) {
- const Prefable<const JSFunctionSpec>* methods;
- const jsid* methodIds;
- const JSFunctionSpec* methodSpecs;
- if (type == eInterface) {
- methods = nativeProperties->StaticMethods();
- methodIds = nativeProperties->StaticMethodIds();
- methodSpecs = nativeProperties->StaticMethodSpecs();
- } else {
- methods = nativeProperties->Methods();
- methodIds = nativeProperties->MethodIds();
- methodSpecs = nativeProperties->MethodSpecs();
- }
- JS::Rooted<jsid> methodId(cx);
- if (nativeProperties->iteratorAliasMethodIndex != -1 &&
- id == SYMBOL_TO_JSID(
- JS::GetWellKnownSymbol(cx, JS::SymbolCode::iterator))) {
- methodId =
- nativeProperties->MethodIds()[nativeProperties->iteratorAliasMethodIndex];
- } else {
- methodId = id;
- }
- if (!XrayResolveMethod(cx, wrapper, obj, methodId, methods, methodIds,
- methodSpecs, desc, cacheOnHolder)) {
- return false;
- }
- if (desc.object()) {
- return true;
- }
- }
- if (type == eInterface) {
- if (nativeProperties->HasStaticAttributes()) {
- if (!XrayResolveAttribute(cx, wrapper, obj, id,
- nativeProperties->StaticAttributes(),
- nativeProperties->StaticAttributeIds(),
- nativeProperties->StaticAttributeSpecs(),
- desc, cacheOnHolder)) {
- return false;
- }
- if (desc.object()) {
- return true;
- }
- }
- } else {
- if (nativeProperties->HasAttributes()) {
- if (!XrayResolveAttribute(cx, wrapper, obj, id,
- nativeProperties->Attributes(),
- nativeProperties->AttributeIds(),
- nativeProperties->AttributeSpecs(),
- desc, cacheOnHolder)) {
- return false;
- }
- if (desc.object()) {
- return true;
- }
- }
- }
- if (nativeProperties->HasConstants()) {
- const Prefable<const ConstantSpec>* constant;
- for (constant = nativeProperties->Constants(); constant->specs; ++constant) {
- if (constant->isEnabled(cx, obj)) {
- // Set i to be the index into our full list of ids/specs that we're
- // looking at now.
- size_t i = constant->specs - nativeProperties->ConstantSpecs();
- for ( ; nativeProperties->ConstantIds()[i] != JSID_VOID; ++i) {
- if (id == nativeProperties->ConstantIds()[i]) {
- cacheOnHolder = true;
- desc.setAttributes(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
- desc.object().set(wrapper);
- desc.value().set(nativeProperties->ConstantSpecs()[i].value);
- return true;
- }
- }
- }
- }
- }
- return true;
- }
- static bool
- ResolvePrototypeOrConstructor(JSContext* cx, JS::Handle<JSObject*> wrapper,
- JS::Handle<JSObject*> obj,
- size_t protoAndIfaceCacheIndex, unsigned attrs,
- JS::MutableHandle<JS::PropertyDescriptor> desc,
- bool& cacheOnHolder)
- {
- JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj));
- {
- JSAutoCompartment ac(cx, global);
- ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
- JSObject* protoOrIface =
- protoAndIfaceCache.EntrySlotIfExists(protoAndIfaceCacheIndex);
- if (!protoOrIface) {
- return false;
- }
- cacheOnHolder = true;
- desc.object().set(wrapper);
- desc.setAttributes(attrs);
- desc.setGetter(nullptr);
- desc.setSetter(nullptr);
- desc.value().set(JS::ObjectValue(*protoOrIface));
- }
- return JS_WrapPropertyDescriptor(cx, desc);
- }
- #ifdef DEBUG
- static void
- DEBUG_CheckXBLCallable(JSContext *cx, JSObject *obj)
- {
- // In general, we shouldn't have cross-compartment wrappers here, because
- // we should be running in an XBL scope, and the content prototype should
- // contain wrappers to functions defined in the XBL scope. But if the node
- // has been adopted into another compartment, those prototypes will now point
- // to a different XBL scope (which is ok).
- MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(obj),
- xpc::IsContentXBLScope(js::GetObjectCompartment(js::UncheckedUnwrap(obj))));
- MOZ_ASSERT(JS::IsCallable(obj));
- }
- static void
- DEBUG_CheckXBLLookup(JSContext *cx, JS::PropertyDescriptor *desc)
- {
- if (!desc->obj)
- return;
- if (!desc->value.isUndefined()) {
- MOZ_ASSERT(desc->value.isObject());
- DEBUG_CheckXBLCallable(cx, &desc->value.toObject());
- }
- if (desc->getter) {
- MOZ_ASSERT(desc->attrs & JSPROP_GETTER);
- DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject *, desc->getter));
- }
- if (desc->setter) {
- MOZ_ASSERT(desc->attrs & JSPROP_SETTER);
- DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject *, desc->setter));
- }
- }
- #else
- #define DEBUG_CheckXBLLookup(a, b) {}
- #endif
- /* static */ bool
- XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
- JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
- JS::MutableHandle<JS::PropertyDescriptor> desc,
- bool& cacheOnHolder)
- {
- cacheOnHolder = false;
- DOMObjectType type;
- const NativePropertyHooks *nativePropertyHooks =
- GetNativePropertyHooks(cx, obj, type);
- const NativePropertiesHolder& nativeProperties =
- nativePropertyHooks->mNativeProperties;
- ResolveOwnProperty resolveOwnProperty =
- nativePropertyHooks->mResolveOwnProperty;
- if (type == eNamedPropertiesObject) {
- // None of these should be cached on the holder, since they're dynamic.
- return resolveOwnProperty(cx, wrapper, obj, id, desc);
- }
- // Check for unforgeable properties first.
- if (IsInstance(type)) {
- const NativePropertiesHolder& nativeProperties =
- nativePropertyHooks->mNativeProperties;
- if (!XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc, cacheOnHolder,
- nativeProperties.regular)) {
- return false;
- }
- if (!desc.object() && xpc::AccessCheck::isChrome(wrapper) &&
- !XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc, cacheOnHolder,
- nativeProperties.chromeOnly)) {
- return false;
- }
- if (desc.object()) {
- return true;
- }
- }
- if (IsInstance(type)) {
- if (resolveOwnProperty) {
- if (!resolveOwnProperty(cx, wrapper, obj, id, desc)) {
- return false;
- }
- if (desc.object()) {
- // None of these should be cached on the holder, since they're dynamic.
- return true;
- }
- }
- // If we're a special scope for in-content XBL, our script expects to see
- // the bound XBL methods and attributes when accessing content. However,
- // these members are implemented in content via custom-spliced prototypes,
- // and thus aren't visible through Xray wrappers unless we handle them
- // explicitly. So we check if we're running in such a scope, and if so,
- // whether the wrappee is a bound element. If it is, we do a lookup via
- // specialized XBL machinery.
- //
- // While we have to do some sketchy walking through content land, we should
- // be protected by read-only/non-configurable properties, and any functions
- // we end up with should _always_ be living in our own scope (the XBL scope).
- // Make sure to assert that.
- JS::Rooted<JSObject*> maybeElement(cx, obj);
- Element* element;
- if (xpc::ObjectScope(wrapper)->IsContentXBLScope() &&
- NS_SUCCEEDED(UNWRAP_OBJECT(Element, &maybeElement, element))) {
- if (!nsContentUtils::LookupBindingMember(cx, element, id, desc)) {
- return false;
- }
- DEBUG_CheckXBLLookup(cx, desc.address());
- if (desc.object()) {
- // XBL properties shouldn't be cached on the holder, as they might be
- // shadowed by own properties returned from mResolveOwnProperty.
- desc.object().set(wrapper);
- return true;
- }
- }
- // For non-global instance Xrays there are no other properties, so return
- // here for them.
- if (type != eGlobalInstance) {
- return true;
- }
- } else if (type == eInterface) {
- if (IdEquals(id, "prototype")) {
- return nativePropertyHooks->mPrototypeID == prototypes::id::_ID_Count ||
- ResolvePrototypeOrConstructor(cx, wrapper, obj,
- nativePropertyHooks->mPrototypeID,
- JSPROP_PERMANENT | JSPROP_READONLY,
- desc, cacheOnHolder);
- }
- if (id == SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::hasInstance)) &&
- DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj))->
- wantsInterfaceHasInstance) {
- cacheOnHolder = true;
- JSNativeWrapper interfaceHasInstanceWrapper = { InterfaceHasInstance,
- nullptr };
- JSObject* funObj = XrayCreateFunction(cx, wrapper,
- interfaceHasInstanceWrapper, 1, id);
- if (!funObj) {
- return false;
- }
- desc.value().setObject(*funObj);
- desc.setAttributes(JSPROP_READONLY | JSPROP_PERMANENT);
- desc.object().set(wrapper);
- desc.setSetter(nullptr);
- desc.setGetter(nullptr);
- return true;
- }
- } else {
- MOZ_ASSERT(IsInterfacePrototype(type));
- if (IdEquals(id, "constructor")) {
- return nativePropertyHooks->mConstructorID == constructors::id::_ID_Count ||
- ResolvePrototypeOrConstructor(cx, wrapper, obj,
- nativePropertyHooks->mConstructorID,
- 0, desc, cacheOnHolder);
- }
- // The properties for globals live on the instance, so return here as there
- // are no properties on their interface prototype object.
- if (type == eGlobalInterfacePrototype) {
- return true;
- }
- }
- if (nativeProperties.regular &&
- !XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder, type,
- nativeProperties.regular)) {
- return false;
- }
- if (!desc.object() &&
- nativeProperties.chromeOnly &&
- xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
- !XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder, type,
- nativeProperties.chromeOnly)) {
- return false;
- }
- return true;
- }
- bool
- XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
- JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
- JS::Handle<JS::PropertyDescriptor> desc,
- JS::ObjectOpResult &result, bool *defined)
- {
- if (!js::IsProxy(obj))
- return true;
- const DOMProxyHandler* handler = GetDOMProxyHandler(obj);
- return handler->defineProperty(cx, wrapper, id, desc, result, defined);
- }
- template<typename SpecType>
- bool
- XrayAttributeOrMethodKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
- JS::Handle<JSObject*> obj,
- const Prefable<const SpecType>* list,
- const jsid* ids, const SpecType* specList,
- unsigned flags, JS::AutoIdVector& props)
- {
- for (; list->specs; ++list) {
- if (list->isEnabled(cx, obj)) {
- // Set i to be the index into our full list of ids/specs that we're
- // looking at now.
- size_t i = list->specs - specList;
- for ( ; ids[i] != JSID_VOID; ++i) {
- // Skip non-enumerable properties and symbol-keyed properties unless
- // they are specially requested via flags.
- if (((flags & JSITER_HIDDEN) ||
- (specList[i].flags & JSPROP_ENUMERATE)) &&
- ((flags & JSITER_SYMBOLS) || !JSID_IS_SYMBOL(ids[i])) &&
- !props.append(ids[i])) {
- return false;
- }
- }
- }
- }
- return true;
- }
- #define ADD_KEYS_IF_DEFINED(FieldName) { \
- if (nativeProperties->Has##FieldName##s() && \
- !XrayAttributeOrMethodKeys(cx, wrapper, obj, \
- nativeProperties->FieldName##s(), \
- nativeProperties->FieldName##Ids(), \
- nativeProperties->FieldName##Specs(), \
- flags, props)) { \
- return false; \
- } \
- }
- bool
- XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
- JS::Handle<JSObject*> obj,
- unsigned flags, JS::AutoIdVector& props,
- DOMObjectType type,
- const NativeProperties* nativeProperties)
- {
- MOZ_ASSERT(type != eNamedPropertiesObject);
- if (IsInstance(type)) {
- ADD_KEYS_IF_DEFINED(UnforgeableMethod);
- ADD_KEYS_IF_DEFINED(UnforgeableAttribute);
- if (type == eGlobalInstance) {
- ADD_KEYS_IF_DEFINED(Method);
- ADD_KEYS_IF_DEFINED(Attribute);
- }
- } else if (type == eInterface) {
- ADD_KEYS_IF_DEFINED(StaticMethod);
- ADD_KEYS_IF_DEFINED(StaticAttribute);
- } else if (type != eGlobalInterfacePrototype) {
- MOZ_ASSERT(IsInterfacePrototype(type));
- ADD_KEYS_IF_DEFINED(Method);
- ADD_KEYS_IF_DEFINED(Attribute);
- }
- if (nativeProperties->HasConstants()) {
- const Prefable<const ConstantSpec>* constant;
- for (constant = nativeProperties->Constants(); constant->specs; ++constant) {
- if (constant->isEnabled(cx, obj)) {
- // Set i to be the index into our full list of ids/specs that we're
- // looking at now.
- size_t i = constant->specs - nativeProperties->ConstantSpecs();
- for ( ; nativeProperties->ConstantIds()[i] != JSID_VOID; ++i) {
- if (!props.append(nativeProperties->ConstantIds()[i])) {
- return false;
- }
- }
- }
- }
- }
- return true;
- }
- #undef ADD_KEYS_IF_DEFINED
- bool
- XrayOwnNativePropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
- const NativePropertyHooks* nativePropertyHooks,
- DOMObjectType type, JS::Handle<JSObject*> obj,
- unsigned flags, JS::AutoIdVector& props)
- {
- MOZ_ASSERT(type != eNamedPropertiesObject);
- if (type == eInterface &&
- nativePropertyHooks->mPrototypeID != prototypes::id::_ID_Count &&
- !AddStringToIDVector(cx, props, "prototype")) {
- return false;
- }
- if (IsInterfacePrototype(type) &&
- nativePropertyHooks->mConstructorID != constructors::id::_ID_Count &&
- (flags & JSITER_HIDDEN) &&
- !AddStringToIDVector(cx, props, "constructor")) {
- return false;
- }
- const NativePropertiesHolder& nativeProperties =
- nativePropertyHooks->mNativeProperties;
- if (nativeProperties.regular &&
- !XrayOwnPropertyKeys(cx, wrapper, obj, flags, props, type,
- nativeProperties.regular)) {
- return false;
- }
- if (nativeProperties.chromeOnly &&
- xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
- !XrayOwnPropertyKeys(cx, wrapper, obj, flags, props, type,
- nativeProperties.chromeOnly)) {
- return false;
- }
- return true;
- }
- bool
- XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
- JS::Handle<JSObject*> obj,
- unsigned flags, JS::AutoIdVector& props)
- {
- DOMObjectType type;
- const NativePropertyHooks* nativePropertyHooks =
- GetNativePropertyHooks(cx, obj, type);
- EnumerateOwnProperties enumerateOwnProperties =
- nativePropertyHooks->mEnumerateOwnProperties;
- if (type == eNamedPropertiesObject) {
- return enumerateOwnProperties(cx, wrapper, obj, props);
- }
- if (IsInstance(type)) {
- // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1071189
- // Should do something about XBL properties too.
- if (enumerateOwnProperties &&
- !enumerateOwnProperties(cx, wrapper, obj, props)) {
- return false;
- }
- }
- return type == eGlobalInterfacePrototype ||
- XrayOwnNativePropertyKeys(cx, wrapper, nativePropertyHooks, type,
- obj, flags, props);
- }
- const JSClass*
- XrayGetExpandoClass(JSContext* cx, JS::Handle<JSObject*> obj)
- {
- DOMObjectType type;
- const NativePropertyHooks* nativePropertyHooks =
- GetNativePropertyHooks(cx, obj, type);
- if (!IsInstance(type)) {
- // Non-instances don't need any special expando classes.
- return &DefaultXrayExpandoObjectClass;
- }
- return nativePropertyHooks->mXrayExpandoClass;
- }
- bool
- XrayDeleteNamedProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
- JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
- JS::ObjectOpResult& opresult)
- {
- DOMObjectType type;
- const NativePropertyHooks* nativePropertyHooks =
- GetNativePropertyHooks(cx, obj, type);
- if (!IsInstance(type) || !nativePropertyHooks->mDeleteNamedProperty) {
- return opresult.succeed();
- }
- return nativePropertyHooks->mDeleteNamedProperty(cx, wrapper, obj, id,
- opresult);
- }
- JSObject*
- GetCachedSlotStorageObjectSlow(JSContext* cx, JS::Handle<JSObject*> obj,
- bool* isXray)
- {
- if (!xpc::WrapperFactory::IsXrayWrapper(obj)) {
- JSObject* retval = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
- MOZ_ASSERT(IsDOMObject(retval));
- *isXray = false;
- return retval;
- }
- *isXray = true;
- return xpc::EnsureXrayExpandoObject(cx, obj);;
- }
- DEFINE_XRAY_EXPANDO_CLASS(, DefaultXrayExpandoObjectClass, 0);
- NativePropertyHooks sEmptyNativePropertyHooks = {
- nullptr,
- nullptr,
- nullptr,
- {
- nullptr,
- nullptr
- },
- prototypes::id::_ID_Count,
- constructors::id::_ID_Count,
- nullptr
- };
- const js::ClassOps sBoringInterfaceObjectClassClassOps = {
- nullptr, /* addProperty */
- nullptr, /* delProperty */
- nullptr, /* getProperty */
- nullptr, /* setProperty */
- nullptr, /* enumerate */
- nullptr, /* resolve */
- nullptr, /* mayResolve */
- nullptr, /* finalize */
- ThrowingConstructor, /* call */
- nullptr, /* hasInstance */
- ThrowingConstructor, /* construct */
- nullptr, /* trace */
- };
- const js::ObjectOps sInterfaceObjectClassObjectOps = {
- nullptr, /* lookupProperty */
- nullptr, /* defineProperty */
- nullptr, /* hasProperty */
- nullptr, /* getProperty */
- nullptr, /* setProperty */
- nullptr, /* getOwnPropertyDescriptor */
- nullptr, /* deleteProperty */
- nullptr, /* getElements */
- nullptr, /* enumerate */
- InterfaceObjectToString, /* funToString */
- };
- bool
- GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
- JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
- bool* found, JS::MutableHandle<JS::Value> vp)
- {
- JS::Rooted<JSObject*> proto(cx);
- if (!js::GetObjectProto(cx, proxy, &proto)) {
- return false;
- }
- if (!proto) {
- *found = false;
- return true;
- }
- if (!JS_HasPropertyById(cx, proto, id, found)) {
- return false;
- }
- if (!*found) {
- return true;
- }
- return JS_ForwardGetPropertyTo(cx, proto, id, receiver, vp);
- }
- bool
- HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
- JS::Handle<jsid> id, bool* has)
- {
- JS::Rooted<JSObject*> proto(cx);
- if (!js::GetObjectProto(cx, proxy, &proto)) {
- return false;
- }
- if (!proto) {
- *has = false;
- return true;
- }
- return JS_HasPropertyById(cx, proto, id, has);
- }
- bool
- AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
- nsTArray<nsString>& names,
- bool shadowPrototypeProperties,
- JS::AutoIdVector& props)
- {
- for (uint32_t i = 0; i < names.Length(); ++i) {
- JS::Rooted<JS::Value> v(cx);
- if (!xpc::NonVoidStringToJsval(cx, names[i], &v)) {
- return false;
- }
- JS::Rooted<jsid> id(cx);
- if (!JS_ValueToId(cx, v, &id)) {
- return false;
- }
- bool shouldAppend = shadowPrototypeProperties;
- if (!shouldAppend) {
- bool has;
- if (!HasPropertyOnPrototype(cx, proxy, id, &has)) {
- return false;
- }
- shouldAppend = !has;
- }
- if (shouldAppend) {
- if (!props.append(id)) {
- return false;
- }
- }
- }
- return true;
- }
- bool
- DictionaryBase::ParseJSON(JSContext* aCx,
- const nsAString& aJSON,
- JS::MutableHandle<JS::Value> aVal)
- {
- if (aJSON.IsEmpty()) {
- return true;
- }
- return JS_ParseJSON(aCx, PromiseFlatString(aJSON).get(), aJSON.Length(), aVal);
- }
- bool
- DictionaryBase::StringifyToJSON(JSContext* aCx,
- JS::Handle<JSObject*> aObj,
- nsAString& aJSON) const
- {
- return JS::ToJSONMaybeSafely(aCx, aObj, AppendJSONToString, &aJSON);
- }
- /* static */
- bool
- DictionaryBase::AppendJSONToString(const char16_t* aJSONData,
- uint32_t aDataLength,
- void* aString)
- {
- nsAString* string = static_cast<nsAString*>(aString);
- string->Append(aJSONData, aDataLength);
- return true;
- }
- nsresult
- ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
- {
- js::AssertSameCompartment(aCx, aObjArg);
- // Check if we're anywhere near the stack limit before we reach the
- // transplanting code, since it has no good way to handle errors. This uses
- // the untrusted script limit, which is not strictly necessary since no
- // actual script should run.
- JS_CHECK_RECURSION_CONSERVATIVE(aCx, return NS_ERROR_FAILURE);
- JS::Rooted<JSObject*> aObj(aCx, aObjArg);
- const DOMJSClass* domClass = GetDOMClass(aObj);
- // DOM things are always parented to globals.
- JS::Rooted<JSObject*> oldParent(aCx,
- js::GetGlobalForObjectCrossCompartment(aObj));
- MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(oldParent) == oldParent);
- JS::Rooted<JSObject*> newParent(aCx,
- domClass->mGetAssociatedGlobal(aCx, aObj));
- MOZ_ASSERT(JS_IsGlobalObject(newParent));
- JSAutoCompartment oldAc(aCx, oldParent);
- JSCompartment* oldCompartment = js::GetObjectCompartment(oldParent);
- JSCompartment* newCompartment = js::GetObjectCompartment(newParent);
- if (oldCompartment == newCompartment) {
- MOZ_ASSERT(oldParent == newParent);
- return NS_OK;
- }
- nsISupports* native = UnwrapDOMObjectToISupports(aObj);
- if (!native) {
- return NS_OK;
- }
- bool isProxy = js::IsProxy(aObj);
- JS::Rooted<JSObject*> expandoObject(aCx);
- if (isProxy) {
- expandoObject = DOMProxyHandler::GetAndClearExpandoObject(aObj);
- }
- JSAutoCompartment newAc(aCx, newParent);
- // First we clone the reflector. We get a copy of its properties and clone its
- // expando chain.
- JS::Handle<JSObject*> proto = (domClass->mGetProto)(aCx);
- if (!proto) {
- return NS_ERROR_FAILURE;
- }
- JS::Rooted<JSObject*> newobj(aCx, JS_CloneObject(aCx, aObj, proto));
- if (!newobj) {
- return NS_ERROR_FAILURE;
- }
- JS::Rooted<JSObject*> propertyHolder(aCx);
- JS::Rooted<JSObject*> copyFrom(aCx, isProxy ? expandoObject : aObj);
- if (copyFrom) {
- propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, nullptr);
- if (!propertyHolder) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- if (!JS_CopyPropertiesFrom(aCx, propertyHolder, copyFrom)) {
- return NS_ERROR_FAILURE;
- }
- } else {
- propertyHolder = nullptr;
- }
- // Expandos from other compartments are attached to the target JS object.
- // Copy them over, and let the old ones die a natural death.
- // Note that at this point the DOM_OBJECT_SLOT for |newobj| has not been set.
- // CloneExpandoChain() will use this property of |newobj| when it calls
- // preserveWrapper() via attachExpandoObject() if |aObj| has expandos set, and
- // preserveWrapper() will not do anything in this case. This is safe because
- // if expandos are present then the wrapper will already have been preserved
- // for this native.
- if (!xpc::XrayUtils::CloneExpandoChain(aCx, newobj, aObj)) {
- return NS_ERROR_FAILURE;
- }
- // We've set up |newobj|, so we make it own the native by setting its reserved
- // slot and nulling out the reserved slot of |obj|.
- //
- // NB: It's important to do this _after_ copying the properties to
- // propertyHolder. Otherwise, an object with |foo.x === foo| will
- // crash when JS_CopyPropertiesFrom tries to call wrap() on foo.x.
- js::SetReservedOrProxyPrivateSlot(newobj, DOM_OBJECT_SLOT,
- js::GetReservedOrProxyPrivateSlot(aObj, DOM_OBJECT_SLOT));
- js::SetReservedOrProxyPrivateSlot(aObj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
- aObj = xpc::TransplantObject(aCx, aObj, newobj);
- if (!aObj) {
- MOZ_CRASH();
- }
- nsWrapperCache* cache = nullptr;
- CallQueryInterface(native, &cache);
- bool preserving = cache->PreservingWrapper();
- cache->SetPreservingWrapper(false);
- cache->SetWrapper(aObj);
- cache->SetPreservingWrapper(preserving);
- if (propertyHolder) {
- JS::Rooted<JSObject*> copyTo(aCx);
- if (isProxy) {
- copyTo = DOMProxyHandler::EnsureExpandoObject(aCx, aObj);
- } else {
- copyTo = aObj;
- }
- if (!copyTo || !JS_CopyPropertiesFrom(aCx, copyTo, propertyHolder)) {
- MOZ_CRASH();
- }
- }
- JS::Rooted<JSObject*> maybeObjLC(aCx, aObj);
- nsObjectLoadingContent* htmlobject;
- nsresult rv = UNWRAP_OBJECT(HTMLObjectElement, &maybeObjLC, htmlobject);
- if (NS_FAILED(rv)) {
- rv = UnwrapObject<prototypes::id::HTMLEmbedElement,
- HTMLSharedObjectElement>(&maybeObjLC, htmlobject);
- if (NS_FAILED(rv)) {
- rv = UnwrapObject<prototypes::id::HTMLAppletElement,
- HTMLSharedObjectElement>(&maybeObjLC, htmlobject);
- if (NS_FAILED(rv)) {
- htmlobject = nullptr;
- }
- }
- }
- if (htmlobject) {
- htmlobject->SetupProtoChain(aCx, aObj);
- }
- // Now we can just return the wrapper
- return NS_OK;
- }
- GlobalObject::GlobalObject(JSContext* aCx, JSObject* aObject)
- : mGlobalJSObject(aCx),
- mCx(aCx),
- mGlobalObject(nullptr)
- {
- MOZ_ASSERT(mCx);
- JS::Rooted<JSObject*> obj(aCx, aObject);
- if (js::IsWrapper(obj)) {
- obj = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
- if (!obj) {
- // We should never end up here on a worker thread, since there shouldn't
- // be any security wrappers to worry about.
- if (!MOZ_LIKELY(NS_IsMainThread())) {
- MOZ_CRASH();
- }
- Throw(aCx, NS_ERROR_XPC_SECURITY_MANAGER_VETO);
- return;
- }
- }
- mGlobalJSObject = js::GetGlobalForObjectCrossCompartment(obj);
- }
- nsISupports*
- GlobalObject::GetAsSupports() const
- {
- if (mGlobalObject) {
- return mGlobalObject;
- }
- MOZ_ASSERT(!js::IsWrapper(mGlobalJSObject));
- // Most of our globals are DOM objects. Try that first. Note that this
- // assumes that either the first nsISupports in the object is the canonical
- // one or that we don't care about the canonical nsISupports here.
- mGlobalObject = UnwrapDOMObjectToISupports(mGlobalJSObject);
- if (mGlobalObject) {
- return mGlobalObject;
- }
- MOZ_ASSERT(NS_IsMainThread(), "All our worker globals are DOM objects");
- // Remove everything below here once all our global objects are using new
- // bindings. If that ever happens; it would need to include Sandbox and
- // BackstagePass.
- // See whether mGlobalJSObject is an XPCWrappedNative. This will redo the
- // IsWrapper bit above and the UnwrapDOMObjectToISupports in the case when
- // we're not actually an XPCWrappedNative, but this should be a rare-ish case
- // anyway.
- nsCOMPtr<nsISupports> supp = xpc::UnwrapReflectorToISupports(mGlobalJSObject);
- if (supp) {
- // See documentation for mGlobalJSObject for why this assignment is OK.
- mGlobalObject = supp;
- return mGlobalObject;
- }
- // And now a final hack. Sandbox is not a reflector, but it does have an
- // nsIGlobalObject hanging out in its private slot. Handle that case here,
- // (though again, this will do the useless UnwrapDOMObjectToISupports if we
- // got here for something that is somehow not a DOM object, not an
- // XPCWrappedNative _and_ not a Sandbox).
- if (XPCConvert::GetISupportsFromJSObject(mGlobalJSObject, &mGlobalObject)) {
- return mGlobalObject;
- }
- MOZ_ASSERT(!mGlobalObject);
- Throw(mCx, NS_ERROR_XPC_BAD_CONVERT_JS);
- return nullptr;
- }
- nsIPrincipal*
- GlobalObject::GetSubjectPrincipal() const
- {
- if (!NS_IsMainThread()) {
- return nullptr;
- }
- JSCompartment* compartment = js::GetContextCompartment(mCx);
- MOZ_ASSERT(compartment);
- JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
- return nsJSPrincipals::get(principals);
- }
- static bool
- CallOrdinaryHasInstance(JSContext* cx, JS::CallArgs& args)
- {
- JS::Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
- bool isInstance;
- if (!JS::OrdinaryHasInstance(cx, thisObj, args.get(0), &isInstance)) {
- return false;
- }
- args.rval().setBoolean(isInstance);
- return true;
- }
- bool
- InterfaceHasInstance(JSContext* cx, unsigned argc, JS::Value* vp)
- {
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- // If the thing we were passed is not an object, return false like
- // OrdinaryHasInstance does.
- if (!args.get(0).isObject()) {
- args.rval().setBoolean(false);
- return true;
- }
- // If "this" is not an object, likewise return false (again, like
- // OrdinaryHasInstance).
- if (!args.thisv().isObject()) {
- args.rval().setBoolean(false);
- return true;
- }
- // If "this" doesn't have a DOMIfaceAndProtoJSClass, it's not a DOM
- // constructor, so just fall back to OrdinaryHasInstance. But note that we
- // should CheckedUnwrap here, because otherwise we won't get the right
- // answers.
- JS::Rooted<JSObject*> thisObj(cx, js::CheckedUnwrap(&args.thisv().toObject()));
- if (!thisObj) {
- // Just fall back on the normal thing, in case it still happens to work.
- return CallOrdinaryHasInstance(cx, args);
- }
- const js::Class* thisClass = js::GetObjectClass(thisObj);
- if (!IsDOMIfaceAndProtoClass(thisClass)) {
- return CallOrdinaryHasInstance(cx, args);
- }
- const DOMIfaceAndProtoJSClass* clasp =
- DOMIfaceAndProtoJSClass::FromJSClass(thisClass);
- // If "this" isn't a DOM constructor or is a constructor for an interface
- // without a prototype, just fall back to OrdinaryHasInstance.
- if (clasp->mType != eInterface ||
- clasp->mPrototypeID == prototypes::id::_ID_Count) {
- return CallOrdinaryHasInstance(cx, args);
- }
- JS::Rooted<JSObject*> instance(cx, &args[0].toObject());
- const DOMJSClass* domClass =
- GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
- if (domClass &&
- domClass->mInterfaceChain[clasp->mDepth] == clasp->mPrototypeID) {
- args.rval().setBoolean(true);
- return true;
- }
- if (jsipc::IsWrappedCPOW(instance)) {
- bool boolp = false;
- if (!jsipc::DOMInstanceOf(cx, js::UncheckedUnwrap(instance), clasp->mPrototypeID,
- clasp->mDepth, &boolp)) {
- return false;
- }
- args.rval().setBoolean(boolp);
- return true;
- }
- return CallOrdinaryHasInstance(cx, args);
- }
- bool
- InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
- JS::Handle<JSObject*> instance,
- bool* bp)
- {
- const DOMJSClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance));
- MOZ_ASSERT(!domClass || prototypeID != prototypes::id::_ID_Count,
- "Why do we have a hasInstance hook if we don't have a prototype "
- "ID?");
- *bp = (domClass && domClass->mInterfaceChain[depth] == prototypeID);
- return true;
- }
- bool
- ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj)
- {
- JS::Rooted<JSObject*> rootedObj(cx, obj);
- GlobalObject global(cx, rootedObj);
- if (global.Failed()) {
- return false;
- }
- nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global.GetAsSupports());
- if (window && window->GetDoc()) {
- window->GetDoc()->WarnOnceAbout(nsIDocument::eLenientThis);
- }
- return true;
- }
- bool
- GetContentGlobalForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj,
- nsIGlobalObject** globalObj)
- {
- // Be very careful to not get tricked here.
- MOZ_ASSERT(NS_IsMainThread());
- if (!xpc::AccessCheck::isChrome(js::GetObjectCompartment(obj))) {
- NS_RUNTIMEABORT("Should have a chrome object here");
- }
- // Look up the content-side object.
- JS::Rooted<JS::Value> domImplVal(cx);
- if (!JS_GetProperty(cx, obj, "__DOM_IMPL__", &domImplVal)) {
- return false;
- }
- if (!domImplVal.isObject()) {
- ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Value");
- return false;
- }
- // Go ahead and get the global from it. GlobalObject will handle
- // doing unwrapping as needed.
- GlobalObject global(cx, &domImplVal.toObject());
- if (global.Failed()) {
- return false;
- }
- DebugOnly<nsresult> rv = CallQueryInterface(global.GetAsSupports(), globalObj);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- MOZ_ASSERT(*globalObj);
- return true;
- }
- already_AddRefed<nsIGlobalObject>
- ConstructJSImplementation(const char* aContractId,
- const GlobalObject& aGlobal,
- JS::MutableHandle<JSObject*> aObject,
- ErrorResult& aRv)
- {
- // Get the global object to use as a parent and for initialization.
- nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
- if (!global) {
- aRv.Throw(NS_ERROR_FAILURE);
- return nullptr;
- }
- ConstructJSImplementation(aContractId, global, aObject, aRv);
- if (aRv.Failed()) {
- return nullptr;
- }
- return global.forget();
- }
- void
- ConstructJSImplementation(const char* aContractId,
- nsIGlobalObject* aGlobal,
- JS::MutableHandle<JSObject*> aObject,
- ErrorResult& aRv)
- {
- MOZ_ASSERT(NS_IsMainThread());
- // Make sure to divorce ourselves from the calling JS while creating and
- // initializing the object, so exceptions from that will get reported
- // properly, since those are never exceptions that a spec wants to be thrown.
- {
- AutoNoJSAPI nojsapi;
- nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
- if (!window->IsCurrentInnerWindow()) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
- // Get the XPCOM component containing the JS implementation.
- nsresult rv;
- nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId, &rv);
- if (!implISupports) {
- nsPrintfCString msg("Failed to get JS implementation for contract \"%s\"",
- aContractId);
- NS_WARNING(msg.get());
- aRv.Throw(rv);
- return;
- }
- // Initialize the object, if it implements nsIDOMGlobalPropertyInitializer
- // and our global is a window.
- nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi =
- do_QueryInterface(implISupports);
- if (gpi) {
- JS::Rooted<JS::Value> initReturn(RootingCx());
- rv = gpi->Init(window, &initReturn);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- return;
- }
- // With JS-implemented WebIDL, the return value of init() is not used to determine
- // if init() failed, so init() should only return undefined. Any kind of permission
- // or pref checking must happen by adding an attribute to the WebIDL interface.
- if (!initReturn.isUndefined()) {
- MOZ_ASSERT(false, "The init() method for JS-implemented WebIDL should not return anything");
- MOZ_CRASH();
- }
- }
- // Extract the JS implementation from the XPCOM object.
- nsCOMPtr<nsIXPConnectWrappedJS> implWrapped =
- do_QueryInterface(implISupports, &rv);
- MOZ_ASSERT(implWrapped, "Failed to get wrapped JS from XPCOM component.");
- if (!implWrapped) {
- aRv.Throw(rv);
- return;
- }
- aObject.set(implWrapped->GetJSObject());
- if (!aObject) {
- aRv.Throw(NS_ERROR_FAILURE);
- }
- }
- }
- bool
- NonVoidByteStringToJsval(JSContext *cx, const nsACString &str,
- JS::MutableHandle<JS::Value> rval)
- {
- // ByteStrings are not UTF-8 encoded.
- JSString* jsStr = JS_NewStringCopyN(cx, str.Data(), str.Length());
- if (!jsStr)
- return false;
- rval.setString(jsStr);
- return true;
- }
- template<typename T> static void
- NormalizeUSVStringInternal(T& aString)
- {
- char16_t* start = aString.BeginWriting();
- // Must use const here because we can't pass char** to UTF16CharEnumerator as
- // it expects const char**. Unclear why this is illegal...
- const char16_t* nextChar = start;
- const char16_t* end = aString.Data() + aString.Length();
- while (nextChar < end) {
- uint32_t enumerated = UTF16CharEnumerator::NextChar(&nextChar, end);
- if (enumerated == UCS2_REPLACEMENT_CHAR) {
- int32_t lastCharIndex = (nextChar - start) - 1;
- start[lastCharIndex] = static_cast<char16_t>(enumerated);
- }
- }
- }
- void
- NormalizeUSVString(nsAString& aString)
- {
- NormalizeUSVStringInternal(aString);
- }
- void
- NormalizeUSVString(binding_detail::FakeString& aString)
- {
- NormalizeUSVStringInternal(aString);
- }
- bool
- ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
- bool nullable, nsACString& result)
- {
- JS::Rooted<JSString*> s(cx);
- if (v.isString()) {
- s = v.toString();
- } else {
- if (nullable && v.isNullOrUndefined()) {
- result.SetIsVoid(true);
- return true;
- }
- s = JS::ToString(cx, v);
- if (!s) {
- return false;
- }
- }
- // Conversion from Javascript string to ByteString is only valid if all
- // characters < 256. This is always the case for Latin1 strings.
- size_t length;
- if (!js::StringHasLatin1Chars(s)) {
- // ThrowErrorMessage can GC, so we first scan the string for bad chars
- // and report the error outside the AutoCheckCannotGC scope.
- bool foundBadChar = false;
- size_t badCharIndex;
- char16_t badChar;
- {
- JS::AutoCheckCannotGC nogc;
- const char16_t* chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, s, &length);
- if (!chars) {
- return false;
- }
- for (size_t i = 0; i < length; i++) {
- if (chars[i] > 255) {
- badCharIndex = i;
- badChar = chars[i];
- foundBadChar = true;
- break;
- }
- }
- }
- if (foundBadChar) {
- MOZ_ASSERT(badCharIndex < length);
- MOZ_ASSERT(badChar > 255);
- // The largest unsigned 64 bit number (18,446,744,073,709,551,615) has
- // 20 digits, plus one more for the null terminator.
- char index[21];
- static_assert(sizeof(size_t) <= 8, "index array too small");
- SprintfLiteral(index, "%" PRIuSIZE, badCharIndex);
- // A char16_t is 16 bits long. The biggest unsigned 16 bit
- // number (65,535) has 5 digits, plus one more for the null
- // terminator.
- char badCharArray[6];
- static_assert(sizeof(char16_t) <= 2, "badCharArray too small");
- SprintfLiteral(badCharArray, "%d", badChar);
- ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badCharArray);
- return false;
- }
- } else {
- length = js::GetStringLength(s);
- }
- static_assert(js::MaxStringLength < UINT32_MAX,
- "length+1 shouldn't overflow");
- if (!result.SetLength(length, fallible)) {
- return false;
- }
- JS_EncodeStringToBuffer(cx, s, result.BeginWriting(), length);
- return true;
- }
- void
- FinalizeGlobal(JSFreeOp* aFreeOp, JSObject* aObj)
- {
- MOZ_ASSERT(js::GetObjectClass(aObj)->flags & JSCLASS_DOM_GLOBAL);
- mozilla::dom::DestroyProtoAndIfaceCache(aObj);
- }
- bool
- ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
- JS::Handle<jsid> aId, bool* aResolvedp)
- {
- MOZ_ASSERT(JS_IsGlobalObject(aObj),
- "Should have a global here, since we plan to resolve standard "
- "classes!");
- return JS_ResolveStandardClass(aCx, aObj, aId, aResolvedp);
- }
- bool
- MayResolveGlobal(const JSAtomState& aNames, jsid aId, JSObject* aMaybeObj)
- {
- return JS_MayResolveStandardClass(aNames, aId, aMaybeObj);
- }
- bool
- EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj)
- {
- MOZ_ASSERT(JS_IsGlobalObject(aObj),
- "Should have a global here, since we plan to enumerate standard "
- "classes!");
- return JS_EnumerateStandardClasses(aCx, aObj);
- }
- bool
- IsNonExposedGlobal(JSContext* aCx, JSObject* aGlobal,
- uint32_t aNonExposedGlobals)
- {
- MOZ_ASSERT(aNonExposedGlobals, "Why did we get called?");
- MOZ_ASSERT((aNonExposedGlobals &
- ~(GlobalNames::Window |
- GlobalNames::BackstagePass |
- GlobalNames::DedicatedWorkerGlobalScope |
- GlobalNames::SharedWorkerGlobalScope |
- GlobalNames::ServiceWorkerGlobalScope |
- GlobalNames::WorkerDebuggerGlobalScope |
- GlobalNames::WorkletGlobalScope)) == 0,
- "Unknown non-exposed global type");
- const char* name = js::GetObjectClass(aGlobal)->name;
- if ((aNonExposedGlobals & GlobalNames::Window) &&
- !strcmp(name, "Window")) {
- return true;
- }
- if ((aNonExposedGlobals & GlobalNames::BackstagePass) &&
- !strcmp(name, "BackstagePass")) {
- return true;
- }
- if ((aNonExposedGlobals & GlobalNames::DedicatedWorkerGlobalScope) &&
- !strcmp(name, "DedicatedWorkerGlobalScope")) {
- return true;
- }
- if ((aNonExposedGlobals & GlobalNames::SharedWorkerGlobalScope) &&
- !strcmp(name, "SharedWorkerGlobalScope")) {
- return true;
- }
- if ((aNonExposedGlobals & GlobalNames::ServiceWorkerGlobalScope) &&
- !strcmp(name, "ServiceWorkerGlobalScope")) {
- return true;
- }
- if ((aNonExposedGlobals & GlobalNames::WorkerDebuggerGlobalScope) &&
- !strcmp(name, "WorkerDebuggerGlobalScopex")) {
- return true;
- }
- if ((aNonExposedGlobals & GlobalNames::WorkletGlobalScope) &&
- !strcmp(name, "WorkletGlobalScope")) {
- return true;
- }
- return false;
- }
- void
- HandlePrerenderingViolation(nsPIDOMWindowInner* aWindow)
- {
- // Freeze the window and its workers, and its children too.
- aWindow->Freeze();
- // Suspend event handling on the document
- nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
- if (doc) {
- doc->SuppressEventHandling(nsIDocument::eEvents);
- }
- }
- bool
- EnforceNotInPrerendering(JSContext* aCx, JSObject* aObj)
- {
- JS::Rooted<JSObject*> thisObj(aCx, js::CheckedUnwrap(aObj));
- if (!thisObj) {
- // Without a this object, we cannot check the safety.
- return true;
- }
- nsGlobalWindow* window = xpc::WindowGlobalOrNull(thisObj);
- if (!window) {
- // Without a window, we cannot check the safety.
- return true;
- }
- if (window->GetIsPrerendered()) {
- HandlePrerenderingViolation(window->AsInner());
- // When the bindings layer sees a false return value, it returns false form
- // the JSNative in order to trigger an uncatchable exception.
- return false;
- }
- return true;
- }
- bool
- GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp)
- {
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
- prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
- if (!args.thisv().isObject()) {
- return ThrowInvalidThis(cx, args, false, protoID);
- }
- JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
- // NOTE: we want to leave obj in its initial compartment, so don't want to
- // pass it to UnwrapObject.
- JS::Rooted<JSObject*> rootSelf(cx, obj);
- void* self;
- {
- binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
- nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(wrapper,
- self,
- protoID,
- info->depth);
- if (NS_FAILED(rv)) {
- return ThrowInvalidThis(cx, args,
- rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
- protoID);
- }
- }
- MOZ_ASSERT(info->type() == JSJitInfo::Getter);
- JSJitGetterOp getter = info->getter;
- bool ok = getter(cx, obj, self, JSJitGetterCallArgs(args));
- #ifdef DEBUG
- if (ok) {
- AssertReturnTypeMatchesJitinfo(info, args.rval());
- }
- #endif
- return ok;
- }
- bool
- GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp)
- {
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
- prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
- if (!args.thisv().isObject()) {
- return ThrowInvalidThis(cx, args, false, protoID);
- }
- JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
- // NOTE: we want to leave obj in its initial compartment, so don't want to
- // pass it to UnwrapObject.
- JS::Rooted<JSObject*> rootSelf(cx, obj);
- void* self;
- {
- binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
- nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(wrapper,
- self,
- protoID,
- info->depth);
- if (NS_FAILED(rv)) {
- return ThrowInvalidThis(cx, args,
- rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
- protoID);
- }
- }
- if (args.length() == 0) {
- return ThrowNoSetterArg(cx, protoID);
- }
- MOZ_ASSERT(info->type() == JSJitInfo::Setter);
- JSJitSetterOp setter = info->setter;
- if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
- return false;
- }
- args.rval().setUndefined();
- #ifdef DEBUG
- AssertReturnTypeMatchesJitinfo(info, args.rval());
- #endif
- return true;
- }
- bool
- GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
- {
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
- prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
- if (!args.thisv().isObject()) {
- return ThrowInvalidThis(cx, args, false, protoID);
- }
- JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
- // NOTE: we want to leave obj in its initial compartment, so don't want to
- // pass it to UnwrapObject.
- JS::Rooted<JSObject*> rootSelf(cx, obj);
- void* self;
- {
- binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
- nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(wrapper,
- self,
- protoID,
- info->depth);
- if (NS_FAILED(rv)) {
- return ThrowInvalidThis(cx, args,
- rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
- protoID);
- }
- }
- MOZ_ASSERT(info->type() == JSJitInfo::Method);
- JSJitMethodOp method = info->method;
- bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
- #ifdef DEBUG
- if (ok) {
- AssertReturnTypeMatchesJitinfo(info, args.rval());
- }
- #endif
- return ok;
- }
- bool
- GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
- {
- // Make sure to save the callee before someone maybe messes with rval().
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- JS::Rooted<JSObject*> callee(cx, &args.callee());
- // We could invoke GenericBindingMethod here, but that involves an
- // extra call. Manually inline it instead.
- const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
- prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
- if (!args.thisv().isObject()) {
- ThrowInvalidThis(cx, args, false, protoID);
- return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
- args.rval());
- }
- JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
- // NOTE: we want to leave obj in its initial compartment, so don't want to
- // pass it to UnwrapObject.
- JS::Rooted<JSObject*> rootSelf(cx, obj);
- void* self;
- {
- binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
- nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(wrapper,
- self,
- protoID,
- info->depth);
- if (NS_FAILED(rv)) {
- ThrowInvalidThis(cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
- protoID);
- return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
- args.rval());
- }
- }
- MOZ_ASSERT(info->type() == JSJitInfo::Method);
- JSJitMethodOp method = info->method;
- bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
- if (ok) {
- #ifdef DEBUG
- AssertReturnTypeMatchesJitinfo(info, args.rval());
- #endif
- return true;
- }
- // Promise-returning methods always return objects
- MOZ_ASSERT(info->returnType() == JSVAL_TYPE_OBJECT);
- return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
- args.rval());
- }
- bool
- StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp)
- {
- // Make sure to save the callee before someone maybe messes with rval().
- JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- JS::Rooted<JSObject*> callee(cx, &args.callee());
- const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
- MOZ_ASSERT(info);
- MOZ_ASSERT(info->type() == JSJitInfo::StaticMethod);
- bool ok = info->staticMethod(cx, argc, vp);
- if (ok) {
- return true;
- }
- return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
- args.rval());
- }
- bool
- ConvertExceptionToPromise(JSContext* cx,
- JSObject* promiseScope,
- JS::MutableHandle<JS::Value> rval)
- {
- {
- JSAutoCompartment ac(cx, promiseScope);
- JS::Rooted<JS::Value> exn(cx);
- if (!JS_GetPendingException(cx, &exn)) {
- // This is very important: if there is no pending exception here but we're
- // ending up in this code, that means the callee threw an uncatchable
- // exception. Just propagate that out as-is.
- return false;
- }
- JS_ClearPendingException(cx);
- JSObject* promise = JS::CallOriginalPromiseReject(cx, exn);
- if (!promise) {
- // We just give up. Put the exception back.
- JS_SetPendingException(cx, exn);
- return false;
- }
- rval.setObject(*promise);
- }
- // Now make sure we rewrap promise back into the compartment we want
- return JS_WrapValue(cx, rval);
- }
- /* static */
- void
- CreateGlobalOptions<nsGlobalWindow>::TraceGlobal(JSTracer* aTrc, JSObject* aObj)
- {
- xpc::TraceXPCGlobal(aTrc, aObj);
- }
- static bool sRegisteredDOMNames = false;
- nsresult
- RegisterDOMNames()
- {
- if (sRegisteredDOMNames) {
- return NS_OK;
- }
- // Register new DOM bindings
- WebIDLGlobalNameHash::Init();
- nsresult rv = nsDOMClassInfo::Init();
- if (NS_FAILED(rv)) {
- NS_ERROR("Could not initialize nsDOMClassInfo");
- return rv;
- }
- sRegisteredDOMNames = true;
- return NS_OK;
- }
- /* static */
- bool
- CreateGlobalOptions<nsGlobalWindow>::PostCreateGlobal(JSContext* aCx,
- JS::Handle<JSObject*> aGlobal)
- {
- nsresult rv = RegisterDOMNames();
- if (NS_FAILED(rv)) {
- return Throw(aCx, rv);
- }
- // Invoking the XPCWrappedNativeScope constructor automatically hooks it
- // up to the compartment of aGlobal.
- (void) new XPCWrappedNativeScope(aCx, aGlobal);
- return true;
- }
- #ifdef DEBUG
- void
- AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitInfo,
- JS::Handle<JS::Value> aValue)
- {
- switch (aJitInfo->returnType()) {
- case JSVAL_TYPE_UNKNOWN:
- // Any value is good.
- break;
- case JSVAL_TYPE_DOUBLE:
- // The value could actually be an int32 value as well.
- MOZ_ASSERT(aValue.isNumber());
- break;
- case JSVAL_TYPE_INT32:
- MOZ_ASSERT(aValue.isInt32());
- break;
- case JSVAL_TYPE_UNDEFINED:
- MOZ_ASSERT(aValue.isUndefined());
- break;
- case JSVAL_TYPE_BOOLEAN:
- MOZ_ASSERT(aValue.isBoolean());
- break;
- case JSVAL_TYPE_STRING:
- MOZ_ASSERT(aValue.isString());
- break;
- case JSVAL_TYPE_NULL:
- MOZ_ASSERT(aValue.isNull());
- break;
- case JSVAL_TYPE_OBJECT:
- MOZ_ASSERT(aValue.isObject());
- break;
- default:
- // Someone messed up their jitinfo type.
- MOZ_ASSERT(false, "Unexpected JSValueType stored in jitinfo");
- break;
- }
- }
- #endif
- bool
- CallerSubsumes(JSObject *aObject)
- {
- nsIPrincipal* objPrin = nsContentUtils::ObjectPrincipal(js::UncheckedUnwrap(aObject));
- return nsContentUtils::SubjectPrincipal()->Subsumes(objPrin);
- }
- nsresult
- UnwrapArgImpl(JS::Handle<JSObject*> src,
- const nsIID &iid,
- void **ppArg)
- {
- if (!NS_IsMainThread()) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- nsCOMPtr<nsISupports> iface = xpc::UnwrapReflectorToISupports(src);
- if (iface) {
- if (NS_FAILED(iface->QueryInterface(iid, ppArg))) {
- return NS_ERROR_XPC_BAD_CONVERT_JS;
- }
- return NS_OK;
- }
- // Only allow XPCWrappedJS stuff in system code. Ideally we would remove this
- // even there, but that involves converting some things to WebIDL callback
- // interfaces and making some other things builtinclass...
- if (!nsContentUtils::IsCallerChrome()) {
- return NS_ERROR_XPC_BAD_CONVERT_JS;
- }
- RefPtr<nsXPCWrappedJS> wrappedJS;
- nsresult rv = nsXPCWrappedJS::GetNewOrUsed(src, iid, getter_AddRefs(wrappedJS));
- if (NS_FAILED(rv) || !wrappedJS) {
- return rv;
- }
- // We need to go through the QueryInterface logic to make this return
- // the right thing for the various 'special' interfaces; e.g.
- // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
- // there is an outer to avoid nasty recursion.
- return wrappedJS->QueryInterface(iid, ppArg);
- }
- nsresult
- UnwrapXPConnectImpl(JSContext* cx,
- JS::MutableHandle<JS::Value> src,
- const nsIID &iid,
- void **ppArg)
- {
- if (!NS_IsMainThread()) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- MOZ_ASSERT(src.isObject());
- // Unwrap ourselves, because we're going to want access to the unwrapped
- // object.
- JS::Rooted<JSObject*> obj(cx,
- js::CheckedUnwrap(&src.toObject(),
- /* stopAtWindowProxy = */ false));
- if (!obj) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- nsCOMPtr<nsISupports> iface = xpc::UnwrapReflectorToISupports(obj);
- if (!iface) {
- return NS_ERROR_XPC_BAD_CONVERT_JS;
- }
- if (NS_FAILED(iface->QueryInterface(iid, ppArg))) {
- return NS_ERROR_XPC_BAD_CONVERT_JS;
- }
- // Now update our source to keep rooting our object.
- src.setObject(*obj);
- return NS_OK;
- }
- nsresult
- UnwrapWindowProxyImpl(JS::Handle<JSObject*> src,
- nsPIDOMWindowOuter** ppArg)
- {
- nsCOMPtr<nsPIDOMWindowInner> inner;
- nsresult rv = UnwrapArg<nsPIDOMWindowInner>(src, getter_AddRefs(inner));
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsPIDOMWindowOuter> outer = inner->GetOuterWindow();
- outer.forget(ppArg);
- return NS_OK;
- }
- bool
- SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
- JS::Handle<jsid> id, bool* resolvedp)
- {
- if (!ResolveGlobal(cx, obj, id, resolvedp)) {
- return false;
- }
- if (*resolvedp) {
- return true;
- }
- return ResolveSystemBinding(cx, obj, id, resolvedp);
- }
- bool
- SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj)
- {
- bool ignored = false;
- return EnumerateGlobal(cx, obj) &&
- ResolveSystemBinding(cx, obj, JSID_VOIDHANDLE, &ignored);
- }
- template<decltype(JS::NewMapObject) Method>
- bool
- GetMaplikeSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
- size_t aSlotIndex,
- JS::MutableHandle<JSObject*> aBackingObj,
- bool* aBackingObjCreated)
- {
- JS::Rooted<JSObject*> reflector(aCx);
- reflector = IsDOMObject(aObj) ? aObj : js::UncheckedUnwrap(aObj,
- /* stopAtWindowProxy = */ false);
- // Retrieve the backing object from the reserved slot on the maplike/setlike
- // object. If it doesn't exist yet, create it.
- JS::Rooted<JS::Value> slotValue(aCx);
- slotValue = js::GetReservedSlot(reflector, aSlotIndex);
- if (slotValue.isUndefined()) {
- // Since backing object access can happen in non-originating compartments,
- // make sure to create the backing object in reflector compartment.
- {
- JSAutoCompartment ac(aCx, reflector);
- JS::Rooted<JSObject*> newBackingObj(aCx);
- newBackingObj.set(Method(aCx));
- if (NS_WARN_IF(!newBackingObj)) {
- return false;
- }
- js::SetReservedSlot(reflector, aSlotIndex, JS::ObjectValue(*newBackingObj));
- }
- slotValue = js::GetReservedSlot(reflector, aSlotIndex);
- *aBackingObjCreated = true;
- } else {
- *aBackingObjCreated = false;
- }
- if (!MaybeWrapNonDOMObjectValue(aCx, &slotValue)) {
- return false;
- }
- aBackingObj.set(&slotValue.toObject());
- return true;
- }
- bool
- GetMaplikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
- size_t aSlotIndex,
- JS::MutableHandle<JSObject*> aBackingObj,
- bool* aBackingObjCreated)
- {
- return GetMaplikeSetlikeBackingObject<JS::NewMapObject>(aCx, aObj, aSlotIndex,
- aBackingObj,
- aBackingObjCreated);
- }
- bool
- GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
- size_t aSlotIndex,
- JS::MutableHandle<JSObject*> aBackingObj,
- bool* aBackingObjCreated)
- {
- return GetMaplikeSetlikeBackingObject<JS::NewSetObject>(aCx, aObj, aSlotIndex,
- aBackingObj,
- aBackingObjCreated);
- }
- bool
- ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
- {
- JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
- // Unpack callback and object from slots
- JS::Rooted<JS::Value>
- callbackFn(aCx, js::GetFunctionNativeReserved(&args.callee(),
- FOREACH_CALLBACK_SLOT));
- JS::Rooted<JS::Value>
- maplikeOrSetlikeObj(aCx,
- js::GetFunctionNativeReserved(&args.callee(),
- FOREACH_MAPLIKEORSETLIKEOBJ_SLOT));
- MOZ_ASSERT(aArgc == 3);
- JS::AutoValueVector newArgs(aCx);
- // Arguments are passed in as value, key, object. Keep value and key, replace
- // object with the maplike/setlike object.
- if (!newArgs.append(args.get(0))) {
- return false;
- }
- if (!newArgs.append(args.get(1))) {
- return false;
- }
- if (!newArgs.append(maplikeOrSetlikeObj)) {
- return false;
- }
- JS::Rooted<JS::Value> rval(aCx, JS::UndefinedValue());
- // Now actually call the user specified callback
- return JS::Call(aCx, args.thisv(), callbackFn, newArgs, &rval);
- }
- static inline prototypes::ID
- GetProtoIdForNewtarget(JS::Handle<JSObject*> aNewTarget)
- {
- const js::Class* newTargetClass = js::GetObjectClass(aNewTarget);
- if (IsDOMIfaceAndProtoClass(newTargetClass)) {
- const DOMIfaceAndProtoJSClass* newTargetIfaceClass =
- DOMIfaceAndProtoJSClass::FromJSClass(newTargetClass);
- if (newTargetIfaceClass->mType == eInterface) {
- return newTargetIfaceClass->mPrototypeID;
- }
- } else if (JS_IsNativeFunction(aNewTarget, Constructor)) {
- return GetNativePropertyHooksFromConstructorFunction(aNewTarget)->mPrototypeID;
- }
- return prototypes::id::_ID_Count;
- }
- bool
- GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
- JS::MutableHandle<JSObject*> aDesiredProto)
- {
- if (!aCallArgs.isConstructing()) {
- aDesiredProto.set(nullptr);
- return true;
- }
- // The desired prototype depends on the actual constructor that was invoked,
- // which is passed to us as the newTarget in the callargs. We want to do
- // something akin to the ES6 specification's GetProtototypeFromConstructor (so
- // get .prototype on the newTarget, with a fallback to some sort of default).
- // First, a fast path for the case when the the constructor is in fact one of
- // our DOM constructors. This is safe because on those the "constructor"
- // property is non-configurable and non-writable, so we don't have to do the
- // slow JS_GetProperty call.
- JS::Rooted<JSObject*> newTarget(aCx, &aCallArgs.newTarget().toObject());
- JS::Rooted<JSObject*> originalNewTarget(aCx, newTarget);
- // See whether we have a known DOM constructor here, such that we can take a
- // fast path.
- prototypes::ID protoID = GetProtoIdForNewtarget(newTarget);
- if (protoID == prototypes::id::_ID_Count) {
- // We might still have a cross-compartment wrapper for a known DOM
- // constructor.
- newTarget = js::CheckedUnwrap(newTarget);
- if (newTarget && newTarget != originalNewTarget) {
- protoID = GetProtoIdForNewtarget(newTarget);
- }
- }
- if (protoID != prototypes::id::_ID_Count) {
- ProtoAndIfaceCache& protoAndIfaceCache =
- *GetProtoAndIfaceCache(js::GetGlobalForObjectCrossCompartment(newTarget));
- aDesiredProto.set(protoAndIfaceCache.EntrySlotMustExist(protoID));
- if (newTarget != originalNewTarget) {
- return JS_WrapObject(aCx, aDesiredProto);
- }
- return true;
- }
- // Slow path. This basically duplicates the ES6 spec's
- // GetPrototypeFromConstructor except that instead of taking a string naming
- // the fallback prototype we just fall back to using null and assume that our
- // caller will then pick the right default. The actual defaulting behavior
- // here still needs to be defined in the Web IDL specification.
- //
- // Note that it's very important to do this property get on originalNewTarget,
- // not our unwrapped newTarget, since we want to get Xray behavior here as
- // needed.
- // XXXbz for speed purposes, using a preinterned id here sure would be nice.
- JS::Rooted<JS::Value> protoVal(aCx);
- if (!JS_GetProperty(aCx, originalNewTarget, "prototype", &protoVal)) {
- return false;
- }
- if (!protoVal.isObject()) {
- aDesiredProto.set(nullptr);
- return true;
- }
- aDesiredProto.set(&protoVal.toObject());
- return true;
- }
- // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor
- already_AddRefed<nsGenericHTMLElement>
- CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
- JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv)
- {
- // Step 1.
- nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
- if (!window) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return nullptr;
- }
- nsIDocument* doc = window->GetExtantDoc();
- if (!doc) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return nullptr;
- }
- RefPtr<mozilla::dom::CustomElementRegistry> registry(window->CustomElements());
- if (!registry) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return nullptr;
- }
- // Step 2 is in the code output by CGClassConstructor.
- // Step 3.
- JSContext* cx = aGlobal.Context();
- JS::Rooted<JSObject*> newTarget(cx, &aCallArgs.newTarget().toObject());
- CustomElementDefinition* definition =
- registry->LookupCustomElementDefinition(cx, newTarget);
- if (!definition) {
- aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
- return nullptr;
- }
- // The callee might be an Xray. Unwrap it to get actual callee.
- JS::Rooted<JSObject*> callee(cx, js::CheckedUnwrap(&aCallArgs.callee()));
- if (!callee) {
- aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
- return nullptr;
- }
- // And the actual callee might be in different compartment, so enter its
- // compartment before getting the standard constructor object to compare to,
- // so we get it from the same global as callee itself.
- JSAutoCompartment ac(cx, callee);
- int32_t tag = eHTMLTag_userdefined;
- if (!definition->IsCustomBuiltIn()) {
- // Step 4.
- // If the definition is for an autonomous custom element, the active
- // function should be HTMLElement.
- JS::Rooted<JSObject*> constructor(cx, HTMLElementBinding::GetConstructorObject(cx));
- if (!constructor) {
- aRv.NoteJSContextException(cx);
- return nullptr;
- }
- if (callee != constructor) {
- aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
- return nullptr;
- }
- } else {
- // Step 5.
- // If the definition is for a customized built-in element, the localName
- // should be defined in the specification.
- tag = nsHTMLTags::CaseSensitiveAtomTagToId(definition->mLocalName);
- if (tag == eHTMLTag_userdefined) {
- aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
- return nullptr;
- }
- MOZ_ASSERT(tag <= NS_HTML_TAG_MAX, "tag is out of bounds");
- // If the definition is for a customized built-in element, the active
- // function should be the localname's element interface.
- constructorGetterCallback cb = sConstructorGetterCallback[tag];
- if (!cb) {
- aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
- return nullptr;
- }
- JS::Rooted<JSObject*> constructor(cx, cb(cx));
- if (!constructor) {
- aRv.NoteJSContextException(cx);
- return nullptr;
- }
- if (callee != constructor) {
- aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
- return nullptr;
- }
- }
- RefPtr<mozilla::dom::NodeInfo> nodeInfo =
- doc->NodeInfoManager()->GetNodeInfo(definition->mLocalName,
- nullptr,
- kNameSpaceID_XHTML,
- nsIDOMNode::ELEMENT_NODE);
- if (!nodeInfo) {
- aRv.Throw(NS_ERROR_UNEXPECTED);
- return nullptr;
- }
- // Step 6 and Step 7 are in the code output by CGClassConstructor.
- // Step 8.
- nsTArray<RefPtr<nsGenericHTMLElement>>& constructionStack =
- definition->mConstructionStack;
- if (constructionStack.IsEmpty()) {
- RefPtr<nsGenericHTMLElement> newElement;
- if (tag == eHTMLTag_userdefined) {
- // Autonomous custom element.
- newElement = NS_NewHTMLElement(nodeInfo.forget());
- } else {
- // Customized built-in element.
- newElement = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
- }
- newElement->SetCustomElementData(
- new CustomElementData(definition->mType, CustomElementData::State::eCustom));
- newElement->SetCustomElementDefinition(definition);
- return newElement.forget();
- }
- // Step 9.
- RefPtr<nsGenericHTMLElement>& element = constructionStack.LastElement();
- // Step 10.
- if (element == ALEADY_CONSTRUCTED_MARKER) {
- aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
- return nullptr;
- }
- // Step 11.
- // Do prototype swizzling for upgrading a custom element here, for cases when
- // we have a reflector already. If we don't have one yet, our caller will
- // create it with the right proto (by calling DoGetOrCreateDOMReflector with
- // that proto).
- JS::Rooted<JSObject*> reflector(cx, element->GetWrapper());
- if (reflector) {
- // reflector might be in different compartment.
- JSAutoCompartment ac(cx, reflector);
- JS::Rooted<JSObject*> givenProto(cx, aGivenProto);
- if (!JS_WrapObject(cx, &givenProto) ||
- !JS_SetPrototype(cx, reflector, givenProto)) {
- aRv.NoteJSContextException(cx);
- return nullptr;
- }
- }
- // Step 12 and Step 13.
- return element.forget();
- }
- #ifdef DEBUG
- namespace binding_detail {
- void
- AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
- JS::Handle<JSObject*> aGivenProto)
- {
- if (!aGivenProto) {
- // Nothing to assert here
- return;
- }
- JS::Rooted<JSObject*> reflector(aCx, aReflector);
- JSAutoCompartment ac(aCx, reflector);
- JS::Rooted<JSObject*> reflectorProto(aCx);
- bool ok = JS_GetPrototype(aCx, reflector, &reflectorProto);
- MOZ_ASSERT(ok);
- // aGivenProto may not be in the right compartment here, so we
- // have to wrap it to compare.
- JS::Rooted<JSObject*> givenProto(aCx, aGivenProto);
- ok = JS_WrapObject(aCx, &givenProto);
- MOZ_ASSERT(ok);
- MOZ_ASSERT(givenProto == reflectorProto,
- "How are we supposed to change the proto now?");
- }
- } // namespace binding_detail
- #endif // DEBUG
- void
- SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject,
- UseCounter aUseCounter)
- {
- nsGlobalWindow* win = xpc::WindowGlobalOrNull(js::UncheckedUnwrap(aObject));
- if (win && win->GetDocument()) {
- win->GetDocument()->SetDocumentAndPageUseCounter(aUseCounter);
- }
- }
- namespace {
- // This runnable is used to write a deprecation message from a worker to the
- // console running on the main-thread.
- class DeprecationWarningRunnable final : public WorkerProxyToMainThreadRunnable
- {
- nsIDocument::DeprecatedOperations mOperation;
- public:
- DeprecationWarningRunnable(WorkerPrivate* aWorkerPrivate,
- nsIDocument::DeprecatedOperations aOperation)
- : WorkerProxyToMainThreadRunnable(aWorkerPrivate)
- , mOperation(aOperation)
- {
- MOZ_ASSERT(aWorkerPrivate);
- aWorkerPrivate->AssertIsOnWorkerThread();
- }
- private:
- void
- RunOnMainThread() override
- {
- MOZ_ASSERT(NS_IsMainThread());
- // Walk up to our containing page
- WorkerPrivate* wp = mWorkerPrivate;
- while (wp->GetParent()) {
- wp = wp->GetParent();
- }
- nsPIDOMWindowInner* window = wp->GetWindow();
- if (window && window->GetExtantDoc()) {
- window->GetExtantDoc()->WarnOnceAbout(mOperation);
- }
- }
- void
- RunBackOnWorkerThread() override
- {}
- };
- } // anonymous namespace
- void
- DeprecationWarning(JSContext* aCx, JSObject* aObject,
- nsIDocument::DeprecatedOperations aOperation)
- {
- GlobalObject global(aCx, aObject);
- if (global.Failed()) {
- NS_ERROR("Could not create global for DeprecationWarning");
- return;
- }
- if (NS_IsMainThread()) {
- nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global.GetAsSupports());
- if (window && window->GetExtantDoc()) {
- window->GetExtantDoc()->WarnOnceAbout(aOperation);
- }
- return;
- }
- WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
- if (!workerPrivate) {
- return;
- }
- RefPtr<DeprecationWarningRunnable> runnable =
- new DeprecationWarningRunnable(workerPrivate, aOperation);
- runnable->Dispatch();
- }
- namespace binding_detail {
- JSObject*
- UnprivilegedJunkScopeOrWorkerGlobal()
- {
- if (NS_IsMainThread()) {
- return xpc::UnprivilegedJunkScope();
- }
- return GetCurrentThreadWorkerGlobal();
- }
- } // namespace binding_detail
- } // namespace dom
- } // namespace mozilla
|