123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- /* -*- 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 "jswrapper.h"
- #include "WebIDLGlobalNameHash.h"
- #include "js/GCAPI.h"
- #include "XrayWrapper.h"
- #include "XPCWrapper.h"
- #include "mozilla/dom/Selection.h"
- #include "mozilla/HashFunctions.h"
- #include "mozilla/Maybe.h"
- #include "mozilla/dom/DOMJSProxyHandler.h"
- #include "mozilla/dom/RegisterBindings.h"
- #include "nsIMemoryReporter.h"
- #include "nsTHashtable.h"
- namespace mozilla {
- namespace dom {
- struct MOZ_STACK_CLASS WebIDLNameTableKey
- {
- explicit WebIDLNameTableKey(JSFlatString* aJSString)
- : mLength(js::GetFlatStringLength(aJSString))
- {
- mNogc.emplace();
- JSLinearString* jsString = js::FlatStringToLinearString(aJSString);
- if (js::LinearStringHasLatin1Chars(jsString)) {
- mLatin1String = reinterpret_cast<const char*>(
- js::GetLatin1LinearStringChars(*mNogc, jsString));
- mTwoBytesString = nullptr;
- mHash = mLatin1String ? HashString(mLatin1String, mLength) : 0;
- } else {
- mLatin1String = nullptr;
- mTwoBytesString = js::GetTwoByteLinearStringChars(*mNogc, jsString);
- mHash = mTwoBytesString ? HashString(mTwoBytesString, mLength) : 0;
- }
- }
- explicit WebIDLNameTableKey(const char* aString, size_t aLength)
- : mLatin1String(aString),
- mTwoBytesString(nullptr),
- mLength(aLength),
- mHash(HashString(aString, aLength))
- {
- MOZ_ASSERT(aString[aLength] == '\0');
- }
- Maybe<JS::AutoCheckCannotGC> mNogc;
- const char* mLatin1String;
- const char16_t* mTwoBytesString;
- size_t mLength;
- uint32_t mHash;
- };
- struct WebIDLNameTableEntry : public PLDHashEntryHdr
- {
- typedef const WebIDLNameTableKey& KeyType;
- typedef const WebIDLNameTableKey* KeyTypePointer;
- explicit WebIDLNameTableEntry(KeyTypePointer aKey)
- : mNameOffset(0),
- mNameLength(0),
- mDefine(nullptr),
- mEnabled(nullptr)
- {}
- WebIDLNameTableEntry(WebIDLNameTableEntry&& aEntry)
- : mNameOffset(aEntry.mNameOffset),
- mNameLength(aEntry.mNameLength),
- mDefine(aEntry.mDefine),
- mEnabled(aEntry.mEnabled)
- {}
- ~WebIDLNameTableEntry()
- {}
- bool KeyEquals(KeyTypePointer aKey) const
- {
- if (mNameLength != aKey->mLength) {
- return false;
- }
- const char* name = WebIDLGlobalNameHash::sNames + mNameOffset;
- if (aKey->mLatin1String) {
- return PodEqual(aKey->mLatin1String, name, aKey->mLength);
- }
- return nsCharTraits<char16_t>::compareASCII(aKey->mTwoBytesString, name,
- aKey->mLength) == 0;
- }
- static KeyTypePointer KeyToPointer(KeyType aKey)
- {
- return &aKey;
- }
- static PLDHashNumber HashKey(KeyTypePointer aKey)
- {
- return aKey->mHash;
- }
- enum { ALLOW_MEMMOVE = true };
- uint16_t mNameOffset;
- uint16_t mNameLength;
- WebIDLGlobalNameHash::DefineGlobalName mDefine;
- // May be null if enabled unconditionally
- WebIDLGlobalNameHash::ConstructorEnabled* mEnabled;
- };
- static nsTHashtable<WebIDLNameTableEntry>* sWebIDLGlobalNames;
- class WebIDLGlobalNamesHashReporter final : public nsIMemoryReporter
- {
- MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
- ~WebIDLGlobalNamesHashReporter() {}
- public:
- NS_DECL_ISUPPORTS
- NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
- nsISupports* aData, bool aAnonymize) override
- {
- int64_t amount =
- sWebIDLGlobalNames ?
- sWebIDLGlobalNames->ShallowSizeOfIncludingThis(MallocSizeOf) : 0;
- MOZ_COLLECT_REPORT(
- "explicit/dom/webidl-globalnames", KIND_HEAP, UNITS_BYTES, amount,
- "Memory used by the hash table for WebIDL's global names.");
- return NS_OK;
- }
- };
- NS_IMPL_ISUPPORTS(WebIDLGlobalNamesHashReporter, nsIMemoryReporter)
- /* static */
- void
- WebIDLGlobalNameHash::Init()
- {
- sWebIDLGlobalNames = new nsTHashtable<WebIDLNameTableEntry>(sCount);
- RegisterWebIDLGlobalNames();
- RegisterStrongMemoryReporter(new WebIDLGlobalNamesHashReporter());
- }
- /* static */
- void
- WebIDLGlobalNameHash::Shutdown()
- {
- delete sWebIDLGlobalNames;
- }
- /* static */
- void
- WebIDLGlobalNameHash::Register(uint16_t aNameOffset, uint16_t aNameLength,
- DefineGlobalName aDefine,
- ConstructorEnabled* aEnabled)
- {
- const char* name = sNames + aNameOffset;
- WebIDLNameTableKey key(name, aNameLength);
- WebIDLNameTableEntry* entry = sWebIDLGlobalNames->PutEntry(key);
- entry->mNameOffset = aNameOffset;
- entry->mNameLength = aNameLength;
- entry->mDefine = aDefine;
- entry->mEnabled = aEnabled;
- }
- /* static */
- void
- WebIDLGlobalNameHash::Remove(const char* aName, uint32_t aLength)
- {
- WebIDLNameTableKey key(aName, aLength);
- sWebIDLGlobalNames->RemoveEntry(key);
- }
- /* static */
- bool
- WebIDLGlobalNameHash::DefineIfEnabled(JSContext* aCx,
- JS::Handle<JSObject*> aObj,
- JS::Handle<jsid> aId,
- JS::MutableHandle<JS::PropertyDescriptor> aDesc,
- bool* aFound)
- {
- MOZ_ASSERT(JSID_IS_STRING(aId), "Check for string id before calling this!");
- const WebIDLNameTableEntry* entry;
- {
- WebIDLNameTableKey key(JSID_TO_FLAT_STRING(aId));
- // Rooting analysis thinks nsTHashtable<...>::GetEntry may GC because it
- // ends up calling through PLDHashTableOps' matchEntry function pointer, but
- // we know WebIDLNameTableEntry::KeyEquals can't cause a GC.
- JS::AutoSuppressGCAnalysis suppress;
- entry = sWebIDLGlobalNames->GetEntry(key);
- }
- if (!entry) {
- *aFound = false;
- return true;
- }
- *aFound = true;
- ConstructorEnabled* checkEnabledForScope = entry->mEnabled;
- // We do the enabled check on the current compartment of aCx, but for the
- // actual object we pass in the underlying object in the Xray case. That
- // way the callee can decide whether to allow access based on the caller
- // or the window being touched.
- JS::Rooted<JSObject*> global(aCx,
- js::CheckedUnwrap(aObj, /* stopAtWindowProxy = */ false));
- if (!global) {
- return Throw(aCx, NS_ERROR_DOM_SECURITY_ERR);
- }
- {
- // It's safe to pass "&global" here, because we've already unwrapped it, but
- // for general sanity better to not have debug code even having the
- // appearance of mutating things that opt code uses.
- #ifdef DEBUG
- JS::Rooted<JSObject*> temp(aCx, global);
- DebugOnly<nsGlobalWindow*> win;
- MOZ_ASSERT(NS_SUCCEEDED(UNWRAP_OBJECT(Window, &temp, win)));
- #endif
- }
- if (checkEnabledForScope && !checkEnabledForScope(aCx, global)) {
- return true;
- }
- // The DOM constructor resolve machinery interacts with Xrays in tricky
- // ways, and there are some asymmetries that are important to understand.
- //
- // In the regular (non-Xray) case, we only want to resolve constructors
- // once (so that if they're deleted, they don't reappear). We do this by
- // stashing the constructor in a slot on the global, such that we can see
- // during resolve whether we've created it already. This is rather
- // memory-intensive, so we don't try to maintain these semantics when
- // manipulating a global over Xray (so the properties just re-resolve if
- // they've been deleted).
- //
- // Unfortunately, there's a bit of an impedance-mismatch between the Xray
- // and non-Xray machinery. The Xray machinery wants an API that returns a
- // JS::PropertyDescriptor, so that the resolve hook doesn't have to get
- // snared up with trying to define a property on the Xray holder. At the
- // same time, the DefineInterface callbacks are set up to define things
- // directly on the global. And re-jiggering them to return property
- // descriptors is tricky, because some DefineInterface callbacks define
- // multiple things (like the Image() alias for HTMLImageElement).
- //
- // So the setup is as-follows:
- //
- // * The resolve function takes a JS::PropertyDescriptor, but in the
- // non-Xray case, callees may define things directly on the global, and
- // set the value on the property descriptor to |undefined| to indicate
- // that there's nothing more for the caller to do. We assert against
- // this behavior in the Xray case.
- //
- // * We make sure that we do a non-Xray resolve first, so that all the
- // slots are set up. In the Xray case, this means unwrapping and doing
- // a non-Xray resolve before doing the Xray resolve.
- //
- // This all could use some grand refactoring, but for now we just limp
- // along.
- if (xpc::WrapperFactory::IsXrayWrapper(aObj)) {
- JS::Rooted<JSObject*> interfaceObject(aCx);
- {
- JSAutoCompartment ac(aCx, global);
- interfaceObject = entry->mDefine(aCx, global, aId, false);
- }
- if (NS_WARN_IF(!interfaceObject)) {
- return Throw(aCx, NS_ERROR_FAILURE);
- }
- if (!JS_WrapObject(aCx, &interfaceObject)) {
- return Throw(aCx, NS_ERROR_FAILURE);
- }
- FillPropertyDescriptor(aDesc, aObj, 0, JS::ObjectValue(*interfaceObject));
- return true;
- }
- JS::Rooted<JSObject*> interfaceObject(aCx,
- entry->mDefine(aCx, aObj, aId, true));
- if (NS_WARN_IF(!interfaceObject)) {
- return Throw(aCx, NS_ERROR_FAILURE);
- }
- // We've already defined the property. We indicate this to the caller
- // by filling a property descriptor with JS::UndefinedValue() as the
- // value. We still have to fill in a property descriptor, though, so
- // that the caller knows the property is in fact on this object. It
- // doesn't matter what we pass for the "readonly" argument here.
- FillPropertyDescriptor(aDesc, aObj, JS::UndefinedValue(), false);
- return true;
- }
- /* static */
- bool
- WebIDLGlobalNameHash::MayResolve(jsid aId)
- {
- WebIDLNameTableKey key(JSID_TO_FLAT_STRING(aId));
- // Rooting analysis thinks nsTHashtable<...>::Contains may GC because it ends
- // up calling through PLDHashTableOps' matchEntry function pointer, but we
- // know WebIDLNameTableEntry::KeyEquals can't cause a GC.
- JS::AutoSuppressGCAnalysis suppress;
- return sWebIDLGlobalNames->Contains(key);
- }
- /* static */
- void
- WebIDLGlobalNameHash::GetNames(JSContext* aCx, JS::Handle<JSObject*> aObj,
- nsTArray<nsString>& aNames)
- {
- for (auto iter = sWebIDLGlobalNames->Iter(); !iter.Done(); iter.Next()) {
- const WebIDLNameTableEntry* entry = iter.Get();
- if (!entry->mEnabled || entry->mEnabled(aCx, aObj)) {
- AppendASCIItoUTF16(nsDependentCString(sNames + entry->mNameOffset,
- entry->mNameLength),
- *aNames.AppendElement());
- }
- }
- }
- } // namespace dom
- } // namespace mozilla
|