123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- /* -*- 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 "nsDOMStringMap.h"
- #include "jsapi.h"
- #include "nsError.h"
- #include "nsGenericHTMLElement.h"
- #include "nsContentUtils.h"
- #include "mozilla/dom/DOMStringMapBinding.h"
- #include "nsIDOMMutationEvent.h"
- using namespace mozilla;
- using namespace mozilla::dom;
- NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMStringMap)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMStringMap)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMStringMap)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
- // Check that mElement exists in case the unlink code is run more than once.
- if (tmp->mElement) {
- // Call back to element to null out weak reference to this object.
- tmp->mElement->ClearDataset();
- tmp->mElement->RemoveMutationObserver(tmp);
- tmp->mElement = nullptr;
- }
- tmp->mExpandoAndGeneration.OwnerUnlinked();
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDOMStringMap)
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStringMap)
- NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
- NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
- NS_INTERFACE_MAP_ENTRY(nsISupports)
- NS_INTERFACE_MAP_END
- NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMStringMap)
- NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMStringMap)
- nsDOMStringMap::nsDOMStringMap(Element* aElement)
- : mElement(aElement),
- mRemovingProp(false)
- {
- mElement->AddMutationObserver(this);
- }
- nsDOMStringMap::~nsDOMStringMap()
- {
- // Check if element still exists, may have been unlinked by cycle collector.
- if (mElement) {
- // Call back to element to null out weak reference to this object.
- mElement->ClearDataset();
- mElement->RemoveMutationObserver(this);
- }
- }
- DocGroup*
- nsDOMStringMap::GetDocGroup() const
- {
- return mElement ? mElement->GetDocGroup() : nullptr;
- }
- /* virtual */
- JSObject*
- nsDOMStringMap::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
- {
- return DOMStringMapBinding::Wrap(cx, this, aGivenProto);
- }
- void
- nsDOMStringMap::NamedGetter(const nsAString& aProp, bool& found,
- DOMString& aResult) const
- {
- nsAutoString attr;
- if (!DataPropToAttr(aProp, attr)) {
- found = false;
- return;
- }
- found = mElement->GetAttr(attr, aResult);
- }
- void
- nsDOMStringMap::NamedSetter(const nsAString& aProp,
- const nsAString& aValue,
- ErrorResult& rv)
- {
- nsAutoString attr;
- if (!DataPropToAttr(aProp, attr)) {
- rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
- return;
- }
- nsresult res = nsContentUtils::CheckQName(attr, false);
- if (NS_FAILED(res)) {
- rv.Throw(res);
- return;
- }
- nsCOMPtr<nsIAtom> attrAtom = NS_Atomize(attr);
- MOZ_ASSERT(attrAtom, "Should be infallible");
- res = mElement->SetAttr(kNameSpaceID_None, attrAtom, aValue, true);
- if (NS_FAILED(res)) {
- rv.Throw(res);
- }
- }
- void
- nsDOMStringMap::NamedDeleter(const nsAString& aProp, bool& found)
- {
- // Currently removing property, attribute is already removed.
- if (mRemovingProp) {
- found = false;
- return;
- }
-
- nsAutoString attr;
- if (!DataPropToAttr(aProp, attr)) {
- found = false;
- return;
- }
- nsCOMPtr<nsIAtom> attrAtom = NS_Atomize(attr);
- MOZ_ASSERT(attrAtom, "Should be infallible");
- found = mElement->HasAttr(kNameSpaceID_None, attrAtom);
- if (found) {
- mRemovingProp = true;
- mElement->UnsetAttr(kNameSpaceID_None, attrAtom, true);
- mRemovingProp = false;
- }
- }
- void
- nsDOMStringMap::GetSupportedNames(nsTArray<nsString>& aNames)
- {
- uint32_t attrCount = mElement->GetAttrCount();
- // Iterate through all the attributes and add property
- // names corresponding to data attributes to return array.
- for (uint32_t i = 0; i < attrCount; ++i) {
- const nsAttrName* attrName = mElement->GetAttrNameAt(i);
- // Skip the ones that are not in the null namespace
- if (attrName->NamespaceID() != kNameSpaceID_None) {
- continue;
- }
- nsAutoString prop;
- if (!AttrToDataProp(nsDependentAtomString(attrName->LocalName()),
- prop)) {
- continue;
- }
- aNames.AppendElement(prop);
- }
- }
- /**
- * Converts a dataset property name to the corresponding data attribute name.
- * (ex. aBigFish to data-a-big-fish).
- */
- bool nsDOMStringMap::DataPropToAttr(const nsAString& aProp,
- nsAutoString& aResult)
- {
- // aResult is an autostring, so don't worry about setting its capacity:
- // SetCapacity is slow even when it's a no-op and we already have enough
- // storage there for most cases, probably.
- aResult.AppendLiteral("data-");
- // Iterate property by character to form attribute name.
- // Return syntax error if there is a sequence of "-" followed by a character
- // in the range "a" to "z".
- // Replace capital characters with "-" followed by lower case character.
- // Otherwise, simply append character to attribute name.
- const char16_t* start = aProp.BeginReading();
- const char16_t* end = aProp.EndReading();
- const char16_t* cur = start;
- for (; cur < end; ++cur) {
- const char16_t* next = cur + 1;
- if (char16_t('-') == *cur && next < end &&
- char16_t('a') <= *next && *next <= char16_t('z')) {
- // Syntax error if character following "-" is in range "a" to "z".
- return false;
- }
- if (char16_t('A') <= *cur && *cur <= char16_t('Z')) {
- // Append the characters in the range [start, cur)
- aResult.Append(start, cur - start);
- // Uncamel-case characters in the range of "A" to "Z".
- aResult.Append(char16_t('-'));
- aResult.Append(*cur - 'A' + 'a');
- start = next; // We've already appended the thing at *cur
- }
- }
- aResult.Append(start, cur - start);
- return true;
- }
- /**
- * Converts a data attribute name to the corresponding dataset property name.
- * (ex. data-a-big-fish to aBigFish).
- */
- bool nsDOMStringMap::AttrToDataProp(const nsAString& aAttr,
- nsAutoString& aResult)
- {
- // If the attribute name does not begin with "data-" then it can not be
- // a data attribute.
- if (!StringBeginsWith(aAttr, NS_LITERAL_STRING("data-"))) {
- return false;
- }
- // Start reading attribute from first character after "data-".
- const char16_t* cur = aAttr.BeginReading() + 5;
- const char16_t* end = aAttr.EndReading();
- // Don't try to mess with aResult's capacity: the probably-no-op SetCapacity()
- // call is not that fast.
- // Iterate through attrName by character to form property name.
- // If there is a sequence of "-" followed by a character in the range "a" to
- // "z" then replace with upper case letter.
- // Otherwise append character to property name.
- for (; cur < end; ++cur) {
- const char16_t* next = cur + 1;
- if (char16_t('-') == *cur && next < end &&
- char16_t('a') <= *next && *next <= char16_t('z')) {
- // Upper case the lower case letters that follow a "-".
- aResult.Append(*next - 'a' + 'A');
- // Consume character to account for "-" character.
- ++cur;
- } else {
- // Simply append character if camel case is not necessary.
- aResult.Append(*cur);
- }
- }
- return true;
- }
- void
- nsDOMStringMap::AttributeChanged(nsIDocument *aDocument, Element* aElement,
- int32_t aNameSpaceID, nsIAtom* aAttribute,
- int32_t aModType,
- const nsAttrValue* aOldValue)
- {
- if ((aModType == nsIDOMMutationEvent::ADDITION ||
- aModType == nsIDOMMutationEvent::REMOVAL) &&
- aNameSpaceID == kNameSpaceID_None &&
- StringBeginsWith(nsDependentAtomString(aAttribute),
- NS_LITERAL_STRING("data-"))) {
- ++mExpandoAndGeneration.generation;
- }
- }
|