LCOV - code coverage report
Current view: top level - dom/bindings - WebIDLGlobalNameHash.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 82 118 69.5 %
Date: 2017-07-14 16:53:18 Functions: 16 22 72.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       5             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "WebIDLGlobalNameHash.h"
       8             : #include "js/GCAPI.h"
       9             : #include "mozilla/HashFunctions.h"
      10             : #include "mozilla/Maybe.h"
      11             : #include "mozilla/dom/DOMJSClass.h"
      12             : #include "mozilla/dom/DOMJSProxyHandler.h"
      13             : #include "mozilla/dom/PrototypeList.h"
      14             : #include "mozilla/dom/RegisterBindings.h"
      15             : #include "nsIMemoryReporter.h"
      16             : #include "nsTHashtable.h"
      17             : 
      18             : namespace mozilla {
      19             : namespace dom {
      20             : 
      21        5291 : struct MOZ_STACK_CLASS WebIDLNameTableKey
      22             : {
      23        3158 :   explicit WebIDLNameTableKey(JSFlatString* aJSString)
      24        3158 :     : mLength(js::GetFlatStringLength(aJSString))
      25             :   {
      26        3158 :     mNogc.emplace();
      27        3158 :     JSLinearString* jsString = js::FlatStringToLinearString(aJSString);
      28        3158 :     if (js::LinearStringHasLatin1Chars(jsString)) {
      29        3158 :       mLatin1String = reinterpret_cast<const char*>(
      30        3158 :         js::GetLatin1LinearStringChars(*mNogc, jsString));
      31        3158 :       mTwoBytesString = nullptr;
      32        3158 :       mHash = mLatin1String ? HashString(mLatin1String, mLength) : 0;
      33             :     } else {
      34           0 :       mLatin1String = nullptr;
      35           0 :       mTwoBytesString = js::GetTwoByteLinearStringChars(*mNogc, jsString);
      36           0 :       mHash = mTwoBytesString ? HashString(mTwoBytesString, mLength) : 0;
      37             :     }
      38        3158 :   }
      39        2133 :   explicit WebIDLNameTableKey(const char* aString, size_t aLength)
      40        2133 :     : mLatin1String(aString),
      41             :       mTwoBytesString(nullptr),
      42             :       mLength(aLength),
      43        2133 :       mHash(HashString(aString, aLength))
      44             :   {
      45        2133 :     MOZ_ASSERT(aString[aLength] == '\0');
      46        2133 :   }
      47             : 
      48             :   Maybe<JS::AutoCheckCannotGC> mNogc;
      49             :   const char* mLatin1String;
      50             :   const char16_t* mTwoBytesString;
      51             :   size_t mLength;
      52             :   PLDHashNumber mHash;
      53             : };
      54             : 
      55             : struct WebIDLNameTableEntry : public PLDHashEntryHdr
      56             : {
      57             :   typedef const WebIDLNameTableKey& KeyType;
      58             :   typedef const WebIDLNameTableKey* KeyTypePointer;
      59             : 
      60        2070 :   explicit WebIDLNameTableEntry(KeyTypePointer aKey)
      61        2070 :     : mNameOffset(0),
      62             :       mNameLength(0),
      63             :       mConstructorId(constructors::id::_ID_Count),
      64             :       mDefine(nullptr),
      65        2070 :       mEnabled(nullptr)
      66        2070 :   {}
      67             :   WebIDLNameTableEntry(WebIDLNameTableEntry&& aEntry)
      68             :     : mNameOffset(aEntry.mNameOffset),
      69             :       mNameLength(aEntry.mNameLength),
      70             :       mConstructorId(aEntry.mConstructorId),
      71             :       mDefine(aEntry.mDefine),
      72             :       mEnabled(aEntry.mEnabled)
      73             :   {}
      74           0 :   ~WebIDLNameTableEntry()
      75           0 :   {}
      76             : 
      77          28 :   bool KeyEquals(KeyTypePointer aKey) const
      78             :   {
      79          28 :     if (mNameLength != aKey->mLength) {
      80           0 :       return false;
      81             :     }
      82             : 
      83          28 :     const char* name = WebIDLGlobalNameHash::sNames + mNameOffset;
      84             : 
      85          28 :     if (aKey->mLatin1String) {
      86          28 :       return PodEqual(aKey->mLatin1String, name, aKey->mLength);
      87             :     }
      88             : 
      89           0 :     return nsCharTraits<char16_t>::compareASCII(aKey->mTwoBytesString, name,
      90           0 :                                                 aKey->mLength) == 0;
      91             :   }
      92             : 
      93        5291 :   static KeyTypePointer KeyToPointer(KeyType aKey)
      94             :   {
      95        5291 :     return &aKey;
      96             :   }
      97             : 
      98        5291 :   static PLDHashNumber HashKey(KeyTypePointer aKey)
      99             :   {
     100        5291 :     return aKey->mHash;
     101             :   }
     102             : 
     103             :   enum { ALLOW_MEMMOVE = true };
     104             : 
     105             :   uint16_t mNameOffset;
     106             :   uint16_t mNameLength;
     107             :   constructors::id::ID mConstructorId;
     108             :   WebIDLGlobalNameHash::DefineGlobalName mDefine;
     109             :   // May be null if enabled unconditionally
     110             :   WebIDLGlobalNameHash::ConstructorEnabled* mEnabled;
     111             : };
     112             : 
     113             : static nsTHashtable<WebIDLNameTableEntry>* sWebIDLGlobalNames;
     114             : 
     115           3 : class WebIDLGlobalNamesHashReporter final : public nsIMemoryReporter
     116             : {
     117           0 :   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
     118             : 
     119           0 :   ~WebIDLGlobalNamesHashReporter() {}
     120             : 
     121             : public:
     122             :   NS_DECL_ISUPPORTS
     123             : 
     124           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
     125             :                             nsISupports* aData, bool aAnonymize) override
     126             :   {
     127             :     int64_t amount =
     128           0 :       sWebIDLGlobalNames ?
     129           0 :       sWebIDLGlobalNames->ShallowSizeOfIncludingThis(MallocSizeOf) : 0;
     130             : 
     131           0 :     MOZ_COLLECT_REPORT(
     132             :       "explicit/dom/webidl-globalnames", KIND_HEAP, UNITS_BYTES, amount,
     133           0 :       "Memory used by the hash table for WebIDL's global names.");
     134             : 
     135           0 :     return NS_OK;
     136             :   }
     137             : };
     138             : 
     139          39 : NS_IMPL_ISUPPORTS(WebIDLGlobalNamesHashReporter, nsIMemoryReporter)
     140             : 
     141             : /* static */
     142             : void
     143           3 : WebIDLGlobalNameHash::Init()
     144             : {
     145           3 :   sWebIDLGlobalNames = new nsTHashtable<WebIDLNameTableEntry>(sCount);
     146           3 :   RegisterWebIDLGlobalNames();
     147             : 
     148           3 :   RegisterStrongMemoryReporter(new WebIDLGlobalNamesHashReporter());
     149           3 : }
     150             : 
     151             : /* static */
     152             : void
     153           0 : WebIDLGlobalNameHash::Shutdown()
     154             : {
     155           0 :   delete sWebIDLGlobalNames;
     156           0 : }
     157             : 
     158             : /* static */
     159             : void
     160        2070 : WebIDLGlobalNameHash::Register(uint16_t aNameOffset, uint16_t aNameLength,
     161             :                                DefineGlobalName aDefine,
     162             :                                ConstructorEnabled* aEnabled,
     163             :                                constructors::id::ID aConstructorId)
     164             : {
     165        2070 :   const char* name = sNames + aNameOffset;
     166        4140 :   WebIDLNameTableKey key(name, aNameLength);
     167        2070 :   WebIDLNameTableEntry* entry = sWebIDLGlobalNames->PutEntry(key);
     168        2070 :   entry->mNameOffset = aNameOffset;
     169        2070 :   entry->mNameLength = aNameLength;
     170        2070 :   entry->mDefine = aDefine;
     171        2070 :   entry->mEnabled = aEnabled;
     172        2070 :   entry->mConstructorId = aConstructorId;
     173        2070 : }
     174             : 
     175             : /* static */
     176             : void
     177          63 : WebIDLGlobalNameHash::Remove(const char* aName, uint32_t aLength)
     178             : {
     179         126 :   WebIDLNameTableKey key(aName, aLength);
     180          63 :   sWebIDLGlobalNames->RemoveEntry(key);
     181          63 : }
     182             : 
     183             : /* static */
     184             : bool
     185        3150 : WebIDLGlobalNameHash::DefineIfEnabled(JSContext* aCx,
     186             :                                       JS::Handle<JSObject*> aObj,
     187             :                                       JS::Handle<jsid> aId,
     188             :                                       JS::MutableHandle<JS::PropertyDescriptor> aDesc,
     189             :                                       bool* aFound)
     190             : {
     191        3150 :   MOZ_ASSERT(JSID_IS_STRING(aId), "Check for string id before calling this!");
     192             : 
     193             :   const WebIDLNameTableEntry* entry;
     194             :   {
     195        6300 :     WebIDLNameTableKey key(JSID_TO_FLAT_STRING(aId));
     196             :     // Rooting analysis thinks nsTHashtable<...>::GetEntry may GC because it
     197             :     // ends up calling through PLDHashTableOps' matchEntry function pointer, but
     198             :     // we know WebIDLNameTableEntry::KeyEquals can't cause a GC.
     199        6300 :     JS::AutoSuppressGCAnalysis suppress;
     200        3150 :     entry = sWebIDLGlobalNames->GetEntry(key);
     201             :   }
     202             : 
     203        3150 :   if (!entry) {
     204        3130 :     *aFound = false;
     205        3130 :     return true;
     206             :   }
     207             : 
     208          20 :   *aFound = true;
     209             : 
     210          20 :   ConstructorEnabled* checkEnabledForScope = entry->mEnabled;
     211             :   // We do the enabled check on the current compartment of aCx, but for the
     212             :   // actual object we pass in the underlying object in the Xray case.  That
     213             :   // way the callee can decide whether to allow access based on the caller
     214             :   // or the window being touched.
     215             :   JS::Rooted<JSObject*> global(aCx,
     216          40 :     js::CheckedUnwrap(aObj, /* stopAtWindowProxy = */ false));
     217          20 :   if (!global) {
     218           0 :     return Throw(aCx, NS_ERROR_DOM_SECURITY_ERR);
     219             :   }
     220             : 
     221             :   {
     222             :     // It's safe to pass "&global" here, because we've already unwrapped it, but
     223             :     // for general sanity better to not have debug code even having the
     224             :     // appearance of mutating things that opt code uses.
     225             : #ifdef DEBUG
     226          40 :     JS::Rooted<JSObject*> temp(aCx, global);
     227          40 :     DebugOnly<nsGlobalWindow*> win;
     228          20 :     MOZ_ASSERT(NS_SUCCEEDED(UNWRAP_OBJECT(Window, &temp, win)));
     229             : #endif
     230             :   }
     231             : 
     232          20 :   if (checkEnabledForScope && !checkEnabledForScope(aCx, global)) {
     233           0 :     return true;
     234             :   }
     235             : 
     236             :   // The DOM constructor resolve machinery interacts with Xrays in tricky
     237             :   // ways, and there are some asymmetries that are important to understand.
     238             :   //
     239             :   // In the regular (non-Xray) case, we only want to resolve constructors
     240             :   // once (so that if they're deleted, they don't reappear). We do this by
     241             :   // stashing the constructor in a slot on the global, such that we can see
     242             :   // during resolve whether we've created it already. This is rather
     243             :   // memory-intensive, so we don't try to maintain these semantics when
     244             :   // manipulating a global over Xray (so the properties just re-resolve if
     245             :   // they've been deleted).
     246             :   //
     247             :   // Unfortunately, there's a bit of an impedance-mismatch between the Xray
     248             :   // and non-Xray machinery. The Xray machinery wants an API that returns a
     249             :   // JS::PropertyDescriptor, so that the resolve hook doesn't have to get
     250             :   // snared up with trying to define a property on the Xray holder. At the
     251             :   // same time, the DefineInterface callbacks are set up to define things
     252             :   // directly on the global.  And re-jiggering them to return property
     253             :   // descriptors is tricky, because some DefineInterface callbacks define
     254             :   // multiple things (like the Image() alias for HTMLImageElement).
     255             :   //
     256             :   // So the setup is as-follows:
     257             :   //
     258             :   // * The resolve function takes a JS::PropertyDescriptor, but in the
     259             :   //   non-Xray case, callees may define things directly on the global, and
     260             :   //   set the value on the property descriptor to |undefined| to indicate
     261             :   //   that there's nothing more for the caller to do. We assert against
     262             :   //   this behavior in the Xray case.
     263             :   //
     264             :   // * We make sure that we do a non-Xray resolve first, so that all the
     265             :   //   slots are set up. In the Xray case, this means unwrapping and doing
     266             :   //   a non-Xray resolve before doing the Xray resolve.
     267             :   //
     268             :   // This all could use some grand refactoring, but for now we just limp
     269             :   // along.
     270          20 :   if (xpc::WrapperFactory::IsXrayWrapper(aObj)) {
     271          12 :     JS::Rooted<JSObject*> interfaceObject(aCx);
     272             :     {
     273          12 :       JSAutoCompartment ac(aCx, global);
     274           6 :       interfaceObject = entry->mDefine(aCx, global, aId, false);
     275             :     }
     276           6 :     if (NS_WARN_IF(!interfaceObject)) {
     277           0 :       return Throw(aCx, NS_ERROR_FAILURE);
     278             :     }
     279           6 :     if (!JS_WrapObject(aCx, &interfaceObject)) {
     280           0 :       return Throw(aCx, NS_ERROR_FAILURE);
     281             :     }
     282             : 
     283           6 :     FillPropertyDescriptor(aDesc, aObj, 0, JS::ObjectValue(*interfaceObject));
     284           6 :     return true;
     285             :   }
     286             : 
     287             :   JS::Rooted<JSObject*> interfaceObject(aCx,
     288          28 :                                         entry->mDefine(aCx, aObj, aId, true));
     289          14 :   if (NS_WARN_IF(!interfaceObject)) {
     290           0 :     return Throw(aCx, NS_ERROR_FAILURE);
     291             :   }
     292             : 
     293             :   // We've already defined the property.  We indicate this to the caller
     294             :   // by filling a property descriptor with JS::UndefinedValue() as the
     295             :   // value.  We still have to fill in a property descriptor, though, so
     296             :   // that the caller knows the property is in fact on this object.  It
     297             :   // doesn't matter what we pass for the "readonly" argument here.
     298          14 :   FillPropertyDescriptor(aDesc, aObj, JS::UndefinedValue(), false);
     299             : 
     300          14 :   return true;
     301             : }
     302             : 
     303             : /* static */
     304             : bool
     305           8 : WebIDLGlobalNameHash::MayResolve(jsid aId)
     306             : {
     307          16 :   WebIDLNameTableKey key(JSID_TO_FLAT_STRING(aId));
     308             :   // Rooting analysis thinks nsTHashtable<...>::Contains may GC because it ends
     309             :   // up calling through PLDHashTableOps' matchEntry function pointer, but we
     310             :   // know WebIDLNameTableEntry::KeyEquals can't cause a GC.
     311          16 :   JS::AutoSuppressGCAnalysis suppress;
     312          16 :   return sWebIDLGlobalNames->Contains(key);
     313             : }
     314             : 
     315             : /* static */
     316             : bool
     317           0 : WebIDLGlobalNameHash::GetNames(JSContext* aCx, JS::Handle<JSObject*> aObj,
     318             :                                NameType aNameType, JS::AutoIdVector& aNames)
     319             : {
     320             :   // aObj is always a Window here, so GetProtoAndIfaceCache on it is safe.
     321           0 :   ProtoAndIfaceCache* cache = GetProtoAndIfaceCache(aObj);
     322           0 :   for (auto iter = sWebIDLGlobalNames->Iter(); !iter.Done(); iter.Next()) {
     323           0 :     const WebIDLNameTableEntry* entry = iter.Get();
     324             :     // If aNameType is not AllNames, only include things whose entry slot in the
     325             :     // ProtoAndIfaceCache is null.
     326           0 :     if ((aNameType == AllNames ||
     327           0 :          !cache->HasEntryInSlot(entry->mConstructorId)) &&
     328           0 :         (!entry->mEnabled || entry->mEnabled(aCx, aObj))) {
     329           0 :       JSString* str = JS_AtomizeStringN(aCx, sNames + entry->mNameOffset,
     330           0 :                                         entry->mNameLength);
     331           0 :       if (!str || !aNames.append(NON_INTEGER_ATOM_TO_JSID(str))) {
     332           0 :         return false;
     333             :       }
     334             :     }
     335             :   }
     336             : 
     337           0 :   return true;
     338             : }
     339             : 
     340             : } // namespace dom
     341             : } // namespace mozilla

Generated by: LCOV version 1.13