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
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 #include "XULPersist.h"
9 #include "nsIXULStore.h"
10 #include "nsIStringEnumerator.h"
11 #include "nsServiceManagerUtils.h"
12 #include "mozilla/BasePrincipal.h"
13 #include "mozilla/dom/Document.h"
14 #include "mozilla/dom/Element.h"
15 #include "nsContentUtils.h"
16 #include "nsIAppWindow.h"
18 namespace mozilla::dom
{
20 static bool IsRootElement(Element
* aElement
) {
21 return aElement
->OwnerDoc()->GetRootElement() == aElement
;
24 // FIXME: This is a hack to differentiate "attribute is missing" from "attribute
25 // is present but empty". Use a newline to avoid virtually all collisions.
26 // Ideally the XUL store would be able to store this more reasonably.
27 constexpr auto kMissingAttributeToken
= u
"-moz-missing\n"_ns
;
29 static bool ShouldPersistAttribute(Element
* aElement
, nsAtom
* aAttribute
) {
30 if (IsRootElement(aElement
)) {
31 // This is not an element of the top document, its owner is
32 // not an AppWindow. Persist it.
33 if (aElement
->OwnerDoc()->GetInProcessParentDocument()) {
36 // The following attributes of xul:window should be handled in
37 // AppWindow::SavePersistentAttributes instead of here.
38 if (aAttribute
== nsGkAtoms::screenX
|| aAttribute
== nsGkAtoms::screenY
||
39 aAttribute
== nsGkAtoms::width
|| aAttribute
== nsGkAtoms::height
||
40 aAttribute
== nsGkAtoms::sizemode
) {
47 NS_IMPL_ISUPPORTS(XULPersist
, nsIDocumentObserver
)
49 XULPersist::XULPersist(Document
* aDocument
)
50 : nsStubDocumentObserver(), mDocument(aDocument
) {}
52 XULPersist::~XULPersist() = default;
54 void XULPersist::Init() {
55 ApplyPersistentAttributes();
56 mDocument
->AddObserver(this);
59 void XULPersist::DropDocumentReference() {
60 mDocument
->RemoveObserver(this);
64 void XULPersist::AttributeChanged(dom::Element
* aElement
, int32_t aNameSpaceID
,
65 nsAtom
* aAttribute
, int32_t aModType
,
66 const nsAttrValue
* aOldValue
) {
67 NS_ASSERTION(aElement
->OwnerDoc() == mDocument
, "unexpected doc");
69 if (aNameSpaceID
!= kNameSpaceID_None
) {
73 // See if there is anything we need to persist in the localstore.
75 // Persistence of attributes of xul:window is handled in AppWindow.
76 if (aElement
->GetAttr(nsGkAtoms::persist
, persist
) &&
77 ShouldPersistAttribute(aElement
, aAttribute
) &&
78 // XXXldb This should check that it's a token, not just a substring.
79 persist
.Find(nsDependentAtomString(aAttribute
)) >= 0) {
80 // Might not need this, but be safe for now.
81 nsCOMPtr
<nsIDocumentObserver
> kungFuDeathGrip(this);
82 nsContentUtils::AddScriptRunner(NewRunnableMethod
<Element
*, nsAtom
*>(
83 "dom::XULPersist::Persist", this, &XULPersist::Persist
, aElement
,
88 void XULPersist::Persist(Element
* aElement
, nsAtom
* aAttribute
) {
92 // For non-chrome documents, persistance is simply broken
93 if (!mDocument
->NodePrincipal()->IsSystemPrincipal()) {
98 mLocalStore
= do_GetService("@mozilla.org/xul/xulstore;1");
99 if (NS_WARN_IF(!mLocalStore
)) {
106 aElement
->GetAttr(nsGkAtoms::id
, id
);
107 nsAtomString
attrstr(aAttribute
);
109 nsAutoCString utf8uri
;
110 nsresult rv
= mDocument
->GetDocumentURI()->GetSpec(utf8uri
);
111 if (NS_WARN_IF(NS_FAILED(rv
))) {
115 // Persisting attributes to top level windows is handled by AppWindow.
116 if (IsRootElement(aElement
)) {
117 if (nsCOMPtr
<nsIAppWindow
> win
=
118 mDocument
->GetAppWindowIfToplevelChrome()) {
123 NS_ConvertUTF8toUTF16
uri(utf8uri
);
124 nsAutoString valuestr
;
125 if (!aElement
->GetAttr(aAttribute
, valuestr
)) {
126 valuestr
= kMissingAttributeToken
;
129 mLocalStore
->SetValue(uri
, id
, attrstr
, valuestr
);
130 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "value set");
133 nsresult
XULPersist::ApplyPersistentAttributes() {
135 return NS_ERROR_NOT_AVAILABLE
;
137 // For non-chrome documents, persistance is simply broken
138 if (!mDocument
->NodePrincipal()->IsSystemPrincipal()) {
139 return NS_ERROR_NOT_AVAILABLE
;
142 // Add all of the 'persisted' attributes into the content
145 mLocalStore
= do_GetService("@mozilla.org/xul/xulstore;1");
146 if (NS_WARN_IF(!mLocalStore
)) {
147 return NS_ERROR_NOT_INITIALIZED
;
151 nsCOMArray
<Element
> elements
;
153 nsAutoCString utf8uri
;
154 nsresult rv
= mDocument
->GetDocumentURI()->GetSpec(utf8uri
);
155 if (NS_WARN_IF(NS_FAILED(rv
))) {
158 NS_ConvertUTF8toUTF16
uri(utf8uri
);
160 // Get a list of element IDs for which persisted values are available
161 nsCOMPtr
<nsIStringEnumerator
> ids
;
162 rv
= mLocalStore
->GetIDsEnumerator(uri
, getter_AddRefs(ids
));
163 if (NS_WARN_IF(NS_FAILED(rv
))) {
168 while (NS_SUCCEEDED(ids
->HasMore(&hasmore
)) && hasmore
) {
172 // We want to hold strong refs to the elements while applying
173 // persistent attributes, just in case.
174 const nsTArray
<Element
*>* allElements
= mDocument
->GetAllElementsForId(id
);
179 elements
.SetCapacity(allElements
->Length());
180 for (Element
* element
: *allElements
) {
181 elements
.AppendObject(element
);
184 rv
= ApplyPersistentAttributesToElements(id
, uri
, elements
);
185 if (NS_WARN_IF(NS_FAILED(rv
))) {
193 nsresult
XULPersist::ApplyPersistentAttributesToElements(
194 const nsAString
& aID
, const nsAString
& aDocURI
,
195 nsCOMArray
<Element
>& aElements
) {
197 // Get a list of attributes for which persisted values are available
198 nsCOMPtr
<nsIStringEnumerator
> attrs
;
199 rv
= mLocalStore
->GetAttributeEnumerator(aDocURI
, aID
, getter_AddRefs(attrs
));
200 if (NS_WARN_IF(NS_FAILED(rv
))) {
205 while (NS_SUCCEEDED(attrs
->HasMore(&hasmore
)) && hasmore
) {
206 nsAutoString attrstr
;
207 attrs
->GetNext(attrstr
);
210 rv
= mLocalStore
->GetValue(aDocURI
, aID
, attrstr
, value
);
211 if (NS_WARN_IF(NS_FAILED(rv
))) {
215 RefPtr
<nsAtom
> attr
= NS_Atomize(attrstr
);
216 if (NS_WARN_IF(!attr
)) {
217 return NS_ERROR_OUT_OF_MEMORY
;
220 uint32_t cnt
= aElements
.Length();
221 for (int32_t i
= int32_t(cnt
) - 1; i
>= 0; --i
) {
222 Element
* element
= aElements
.SafeElementAt(i
);
227 // Applying persistent attributes to top level windows is handled
229 if (IsRootElement(element
)) {
230 if (nsCOMPtr
<nsIAppWindow
> win
=
231 mDocument
->GetAppWindowIfToplevelChrome()) {
236 if (value
== kMissingAttributeToken
) {
237 Unused
<< element
->UnsetAttr(kNameSpaceID_None
, attr
, true);
239 Unused
<< element
->SetAttr(kNameSpaceID_None
, attr
, value
, true);
247 } // namespace mozilla::dom