/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "XULPersist.h" #ifdef MOZ_NEW_XULSTORE # include "mozilla/XULStore.h" #else # include "nsIXULStore.h" # include "nsIStringEnumerator.h" #endif #include "mozilla/BasePrincipal.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/Element.h" #include "nsContentUtils.h" #include "nsIAppWindow.h" namespace mozilla { namespace dom { static bool IsRootElement(Element* aElement) { return aElement->OwnerDoc()->GetRootElement() == aElement; } static bool ShouldPersistAttribute(Element* aElement, nsAtom* aAttribute) { if (IsRootElement(aElement)) { // This is not an element of the top document, its owner is // not an AppWindow. Persist it. if (aElement->OwnerDoc()->GetInProcessParentDocument()) { return true; } // The following attributes of xul:window should be handled in // AppWindow::SavePersistentAttributes instead of here. if (aAttribute == nsGkAtoms::screenX || aAttribute == nsGkAtoms::screenY || aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height || aAttribute == nsGkAtoms::sizemode) { return false; } } return true; } NS_IMPL_ISUPPORTS(XULPersist, nsIDocumentObserver) XULPersist::XULPersist(Document* aDocument) : nsStubDocumentObserver(), mDocument(aDocument) {} XULPersist::~XULPersist() = default; void XULPersist::Init() { ApplyPersistentAttributes(); mDocument->AddObserver(this); } void XULPersist::DropDocumentReference() { mDocument->RemoveObserver(this); mDocument = nullptr; } void XULPersist::AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute, int32_t aModType, const nsAttrValue* aOldValue) { NS_ASSERTION(aElement->OwnerDoc() == mDocument, "unexpected doc"); // See if there is anything we need to persist in the localstore. // // XXX Namespace handling broken :-( nsAutoString persist; // Persistence of attributes of xul:window is handled in AppWindow. if (aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist) && ShouldPersistAttribute(aElement, aAttribute) && !persist.IsEmpty() && // XXXldb This should check that it's a token, not just a substring. persist.Find(nsDependentAtomString(aAttribute)) >= 0) { // Might not need this, but be safe for now. nsCOMPtr kungFuDeathGrip(this); nsContentUtils::AddScriptRunner( NewRunnableMethod( "dom::XULPersist::Persist", this, &XULPersist::Persist, aElement, kNameSpaceID_None, aAttribute)); } } void XULPersist::Persist(Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute) { if (!mDocument) { return; } // For non-chrome documents, persistance is simply broken if (!mDocument->NodePrincipal()->IsSystemPrincipal()) { return; } #ifndef MOZ_NEW_XULSTORE if (!mLocalStore) { mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1"); if (NS_WARN_IF(!mLocalStore)) { return; } } #endif nsAutoString id; aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id); nsAtomString attrstr(aAttribute); nsAutoString valuestr; aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr); nsAutoCString utf8uri; nsresult rv = mDocument->GetDocumentURI()->GetSpec(utf8uri); if (NS_WARN_IF(NS_FAILED(rv))) { return; } NS_ConvertUTF8toUTF16 uri(utf8uri); bool hasAttr; #ifdef MOZ_NEW_XULSTORE rv = XULStore::HasValue(uri, id, attrstr, hasAttr); #else rv = mLocalStore->HasValue(uri, id, attrstr, &hasAttr); #endif if (NS_WARN_IF(NS_FAILED(rv))) { return; } if (hasAttr && valuestr.IsEmpty()) { #ifdef MOZ_NEW_XULSTORE rv = XULStore::RemoveValue(uri, id, attrstr); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "value removed"); #else mLocalStore->RemoveValue(uri, id, attrstr); #endif return; } // Persisting attributes to top level windows is handled by AppWindow. if (IsRootElement(aElement)) { if (nsCOMPtr win = mDocument->GetAppWindowIfToplevelChrome()) { return; } } #ifdef MOZ_NEW_XULSTORE rv = XULStore::SetValue(uri, id, attrstr, valuestr); #else mLocalStore->SetValue(uri, id, attrstr, valuestr); #endif NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "value set"); } nsresult XULPersist::ApplyPersistentAttributes() { if (!mDocument) { return NS_ERROR_NOT_AVAILABLE; } // For non-chrome documents, persistance is simply broken if (!mDocument->NodePrincipal()->IsSystemPrincipal()) { return NS_ERROR_NOT_AVAILABLE; } // Add all of the 'persisted' attributes into the content // model. #ifndef MOZ_NEW_XULSTORE if (!mLocalStore) { mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1"); if (NS_WARN_IF(!mLocalStore)) { return NS_ERROR_NOT_INITIALIZED; } } #endif ApplyPersistentAttributesInternal(); return NS_OK; } nsresult XULPersist::ApplyPersistentAttributesInternal() { nsCOMArray elements; nsAutoCString utf8uri; nsresult rv = mDocument->GetDocumentURI()->GetSpec(utf8uri); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } NS_ConvertUTF8toUTF16 uri(utf8uri); // Get a list of element IDs for which persisted values are available #ifdef MOZ_NEW_XULSTORE UniquePtr ids; rv = XULStore::GetIDs(uri, ids); #else nsCOMPtr ids; rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids)); #endif if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } #ifdef MOZ_NEW_XULSTORE while (ids->HasMore()) { nsAutoString id; rv = ids->GetNext(&id); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } #else while (1) { bool hasmore = false; ids->HasMore(&hasmore); if (!hasmore) { break; } nsAutoString id; ids->GetNext(id); #endif // We want to hold strong refs to the elements while applying // persistent attributes, just in case. const nsTArray* allElements = mDocument->GetAllElementsForId(id); if (!allElements) { continue; } elements.Clear(); elements.SetCapacity(allElements->Length()); for (Element* element : *allElements) { elements.AppendObject(element); } rv = ApplyPersistentAttributesToElements(id, elements); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } return NS_OK; } nsresult XULPersist::ApplyPersistentAttributesToElements( const nsAString& aID, nsCOMArray& aElements) { nsAutoCString utf8uri; nsresult rv = mDocument->GetDocumentURI()->GetSpec(utf8uri); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } NS_ConvertUTF8toUTF16 uri(utf8uri); // Get a list of attributes for which persisted values are available #ifdef MOZ_NEW_XULSTORE UniquePtr attrs; rv = XULStore::GetAttrs(uri, aID, attrs); #else nsCOMPtr attrs; rv = mLocalStore->GetAttributeEnumerator(uri, aID, getter_AddRefs(attrs)); #endif if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } #ifdef MOZ_NEW_XULSTORE while (attrs->HasMore()) { nsAutoString attrstr; rv = attrs->GetNext(&attrstr); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsAutoString value; rv = XULStore::GetValue(uri, aID, attrstr, value); #else while (1) { bool hasmore = PR_FALSE; attrs->HasMore(&hasmore); if (!hasmore) { break; } nsAutoString attrstr; attrs->GetNext(attrstr); nsAutoString value; rv = mLocalStore->GetValue(uri, aID, attrstr, value); #endif if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } RefPtr attr = NS_Atomize(attrstr); if (NS_WARN_IF(!attr)) { return NS_ERROR_OUT_OF_MEMORY; } uint32_t cnt = aElements.Length(); for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) { Element* element = aElements.SafeElementAt(i); if (!element) { continue; } // Applying persistent attributes to top level windows is handled // by AppWindow. if (IsRootElement(element)) { if (nsCOMPtr win = mDocument->GetAppWindowIfToplevelChrome()) { continue; } } Unused << element->SetAttr(kNameSpaceID_None, attr, value, true); } } return NS_OK; } } // namespace dom } // namespace mozilla