Bug 1490464 - Fix XUL attribute persistence for browser.xhtml. r=smaug

Move XUL persistence handling into it's own class and make it a separate
nsIDocumentObserver so it can also be used in non-XUL documents.
To avoid adding persistence to all non-XUL documents, a document must add
the "mozpersist" attribute to the root element if it wants enable the
feature.

Differential Revision: https://phabricator.services.mozilla.com/D6802

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Brendan Dahl 2018-11-06 21:45:20 +00:00
parent 1f08e736e4
commit 9058324771
9 changed files with 355 additions and 261 deletions

View File

@ -73,6 +73,7 @@
persist="screenX screenY width height sizemode"
#ifdef BROWSER_XHTML
hidden="true"
mozpersist=""
#endif
>

View File

@ -259,6 +259,7 @@
#include "mozilla/dom/TabGroup.h"
#ifdef MOZ_XUL
#include "mozilla/dom/XULBroadcastManager.h"
#include "mozilla/dom/XULPersist.h"
#include "mozilla/dom/TreeBoxObject.h"
#include "nsIXULWindow.h"
#include "nsXULCommandDispatcher.h"
@ -1737,6 +1738,10 @@ nsDocument::~nsDocument()
mXULBroadcastManager->DropDocumentReference();
}
if (mXULPersist) {
mXULPersist->DropDocumentReference();
}
delete mHeaderData;
ClearAllBoxObjects();
@ -8972,6 +8977,13 @@ nsIDocument::SetReadyStateInternal(ReadyState rs)
}
if (READYSTATE_INTERACTIVE == rs) {
if (nsContentUtils::IsSystemPrincipal(NodePrincipal())) {
Element* root = GetRootElement();
if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::mozpersist)) {
mXULPersist = new XULPersist(this);
mXULPersist->Init();
}
}
TriggerInitialDocumentTranslation();
}

View File

@ -149,6 +149,7 @@ class AnonymousContent;
class Attr;
class BoxObject;
class XULBroadcastManager;
class XULPersist;
class ClientInfo;
class ClientState;
class CDATASection;
@ -4756,6 +4757,7 @@ protected:
nsCOMPtr<nsIDOMXULCommandDispatcher> mCommandDispatcher; // [OWNER] of the focus tracker
RefPtr<mozilla::dom::XULBroadcastManager> mXULBroadcastManager;
RefPtr<mozilla::dom::XULPersist> mXULPersist;
// At the moment, trackers might be blocked by Tracking Protection or FastBlock.
// In order to know the numbers of trackers detected and blocked, we add

View File

@ -82,6 +82,7 @@
#include "mozilla/dom/ProcessingInstruction.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/XULDocumentBinding.h"
#include "mozilla/dom/XULPersist.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/Preferences.h"
@ -127,7 +128,6 @@ namespace dom {
XULDocument::XULDocument(void)
: XMLDocument("application/vnd.mozilla.xul+xml"),
mNextSrcLoadWaiter(nullptr),
mApplyingPersistedAttrs(false),
mIsWritingFastLoad(false),
mDocumentLoaded(false),
mStillWalking(false),
@ -201,12 +201,10 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULDocument, XMLDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPrototype)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypes)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStore)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStore)
//XXX We should probably unlink all the objects we traverse.
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@ -423,58 +421,6 @@ XULDocument::OnPrototypeLoadDone(bool aResumeWalk)
return rv;
}
static bool
ShouldPersistAttribute(Element* aElement, nsAtom* aAttribute)
{
if (aElement->IsXULElement(nsGkAtoms::window)) {
// This is not an element of the top document, its owner is
// not an nsXULWindow. Persist it.
if (aElement->OwnerDoc()->GetParentDocument()) {
return true;
}
// The following attributes of xul:window should be handled in
// nsXULWindow::SavePersistentAttributes instead of here.
if (aAttribute == nsGkAtoms::screenX ||
aAttribute == nsGkAtoms::screenY ||
aAttribute == nsGkAtoms::width ||
aAttribute == nsGkAtoms::height ||
aAttribute == nsGkAtoms::sizemode) {
return false;
}
}
return true;
}
void
XULDocument::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
nsAtom* aAttribute, int32_t aModType,
const nsAttrValue* aOldValue)
{
NS_ASSERTION(aElement->OwnerDoc() == this, "unexpected doc");
// Might not need this, but be safe for now.
nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
// See if there is anything we need to persist in the localstore.
//
// XXX Namespace handling broken :-(
nsAutoString persist;
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
// Persistence of attributes of xul:window is handled in nsXULWindow.
if (ShouldPersistAttribute(aElement, aAttribute) && !persist.IsEmpty() &&
// XXXldb This should check that it's a token, not just a substring.
persist.Find(nsDependentAtomString(aAttribute)) >= 0) {
nsContentUtils::AddScriptRunner(
NewRunnableMethod<Element*, int32_t, nsAtom*>(
"dom::XULDocument::Persist",
this,
&XULDocument::Persist,
aElement,
kNameSpaceID_None,
aAttribute));
}
}
void
XULDocument::ContentAppended(nsIContent* aFirstNewContent)
{
@ -513,58 +459,6 @@ XULDocument::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
// nsIDocument interface
//
void
XULDocument::Persist(Element* aElement, int32_t aNameSpaceID,
nsAtom* aAttribute)
{
// For non-chrome documents, persistance is simply broken
if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
return;
if (!mLocalStore) {
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
if (NS_WARN_IF(!mLocalStore)) {
return;
}
}
nsAutoString id;
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
nsAtomString attrstr(aAttribute);
nsAutoString valuestr;
aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr);
nsAutoCString utf8uri;
nsresult rv = mDocumentURI->GetSpec(utf8uri);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
NS_ConvertUTF8toUTF16 uri(utf8uri);
bool hasAttr;
rv = mLocalStore->HasValue(uri, id, attrstr, &hasAttr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
if (hasAttr && valuestr.IsEmpty()) {
mLocalStore->RemoveValue(uri, id, attrstr);
return;
}
// Persisting attributes to top level windows is handled by nsXULWindow.
if (aElement->IsXULElement(nsGkAtoms::window)) {
if (nsCOMPtr<nsIXULWindow> win = GetXULWindowIfToplevelChrome()) {
return;
}
}
mLocalStore->SetValue(uri, id, attrstr, valuestr);
}
void
XULDocument::AddElementToDocumentPost(Element* aElement)
{
@ -721,145 +615,6 @@ XULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
}
nsresult
XULDocument::ApplyPersistentAttributes()
{
// For non-chrome documents, persistance is simply broken
if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
return NS_ERROR_NOT_AVAILABLE;
// Add all of the 'persisted' attributes into the content
// model.
if (!mLocalStore) {
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
if (NS_WARN_IF(!mLocalStore)) {
return NS_ERROR_NOT_INITIALIZED;
}
}
mApplyingPersistedAttrs = true;
ApplyPersistentAttributesInternal();
mApplyingPersistedAttrs = false;
return NS_OK;
}
nsresult
XULDocument::ApplyPersistentAttributesInternal()
{
nsCOMArray<Element> elements;
nsAutoCString utf8uri;
nsresult rv = mDocumentURI->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
nsCOMPtr<nsIStringEnumerator> ids;
rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
while (1) {
bool hasmore = false;
ids->HasMore(&hasmore);
if (!hasmore) {
break;
}
nsAutoString id;
ids->GetNext(id);
nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(id);
if (!entry) {
continue;
}
// We want to hold strong refs to the elements while applying
// persistent attributes, just in case.
elements.Clear();
elements.SetCapacity(entry->GetIdElements().Length());
for (Element* element : entry->GetIdElements()) {
elements.AppendObject(element);
}
if (elements.IsEmpty()) {
continue;
}
rv = ApplyPersistentAttributesToElements(id, elements);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;
}
nsresult
XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID,
nsCOMArray<Element>& aElements)
{
nsAutoCString utf8uri;
nsresult rv = mDocumentURI->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
nsCOMPtr<nsIStringEnumerator> attrs;
rv = mLocalStore->GetAttributeEnumerator(uri, aID, getter_AddRefs(attrs));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
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);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
RefPtr<nsAtom> attr = NS_Atomize(attrstr);
if (NS_WARN_IF(!attr)) {
return NS_ERROR_OUT_OF_MEMORY;
}
uint32_t cnt = aElements.Count();
for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
RefPtr<Element> element = aElements.SafeObjectAt(i);
if (!element) {
continue;
}
// Applying persistent attributes to top level windows is handled
// by nsXULWindow.
if (element->IsXULElement(nsGkAtoms::window)) {
if (nsCOMPtr<nsIXULWindow> win = GetXULWindowIfToplevelChrome()) {
continue;
}
}
Unused << element->SetAttr(kNameSpaceID_None, attr, value, true);
}
}
return NS_OK;
}
void
XULDocument::TraceProtos(JSTracer* aTrc)
{
@ -1282,7 +1037,8 @@ XULDocument::ResumeWalk()
// If we get here, there is nothing left for us to walk. The content
// model is built and ready for layout.
ApplyPersistentAttributes();
mXULPersist = new XULPersist(this);
mXULPersist->Init();
mStillWalking = false;
if (mPendingSheets == 0) {

View File

@ -19,7 +19,6 @@
#include "nsIStreamListener.h"
#include "nsIStreamLoader.h"
#include "nsICSSLoaderObserver.h"
#include "nsIXULStore.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/ScriptLoader.h"
@ -83,7 +82,6 @@ public:
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
/**
* Notify the XUL document that a subtree has been added
@ -146,11 +144,6 @@ protected:
nsIPrincipal* aDocumentPrincipal,
nsIParser** aResult);
nsresult ApplyPersistentAttributes();
nsresult ApplyPersistentAttributesInternal();
nsresult ApplyPersistentAttributesToElements(const nsAString &aID,
nsCOMArray<Element>& aElements);
void AddElementToDocumentPost(Element* aElement);
static void DirectionChanged(const char* aPrefName, XULDocument* aData);
@ -160,11 +153,6 @@ protected:
static LazyLogModule gXULLog;
void
Persist(mozilla::dom::Element* aElement,
int32_t aNameSpaceID,
nsAtom* aAttribute);
virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
// IMPORTANT: The ownership implicit in the following member
@ -177,8 +165,6 @@ protected:
XULDocument* mNextSrcLoadWaiter; // [OWNER] but not COMPtr
nsCOMPtr<nsIXULStore> mLocalStore;
bool mApplyingPersistedAttrs;
bool mIsWritingFastLoad;
bool mDocumentLoaded;
/**

285
dom/xul/XULPersist.cpp Normal file
View File

@ -0,0 +1,285 @@
/* -*- 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"
#include "nsIXULStore.h"
namespace mozilla {
namespace dom {
static bool
ShouldPersistAttribute(Element* aElement, nsAtom* aAttribute)
{
if (aElement->IsXULElement(nsGkAtoms::window)) {
// This is not an element of the top document, its owner is
// not an nsXULWindow. Persist it.
if (aElement->OwnerDoc()->GetParentDocument()) {
return true;
}
// The following attributes of xul:window should be handled in
// nsXULWindow::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(nsIDocument* aDocument)
: nsStubDocumentObserver(),
mDocument(aDocument)
{
}
XULPersist::~XULPersist()
{
}
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");
// Might not need this, but be safe for now.
nsCOMPtr<nsIDocumentObserver> kungFuDeathGrip(this);
// See if there is anything we need to persist in the localstore.
//
// XXX Namespace handling broken :-(
nsAutoString persist;
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
// Persistence of attributes of xul:window is handled in nsXULWindow.
if (ShouldPersistAttribute(aElement, aAttribute) && !persist.IsEmpty() &&
// XXXldb This should check that it's a token, not just a substring.
persist.Find(nsDependentAtomString(aAttribute)) >= 0) {
nsContentUtils::AddScriptRunner(
NewRunnableMethod<Element*, int32_t, nsAtom*>(
"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 (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal())) {
return;
}
if (!mLocalStore) {
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
if (NS_WARN_IF(!mLocalStore)) {
return;
}
}
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;
rv = mLocalStore->HasValue(uri, id, attrstr, &hasAttr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
if (hasAttr && valuestr.IsEmpty()) {
mLocalStore->RemoveValue(uri, id, attrstr);
return;
}
// Persisting attributes to top level windows is handled by nsXULWindow.
if (aElement->IsXULElement(nsGkAtoms::window)) {
if (nsCOMPtr<nsIXULWindow> win = mDocument->GetXULWindowIfToplevelChrome()) {
return;
}
}
mLocalStore->SetValue(uri, id, attrstr, valuestr);
}
nsresult
XULPersist::ApplyPersistentAttributes()
{
if (!mDocument) {
return NS_ERROR_NOT_AVAILABLE;
}
// For non-chrome documents, persistance is simply broken
if (!nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal())) {
return NS_ERROR_NOT_AVAILABLE;
}
// Add all of the 'persisted' attributes into the content
// model.
if (!mLocalStore) {
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
if (NS_WARN_IF(!mLocalStore)) {
return NS_ERROR_NOT_INITIALIZED;
}
}
ApplyPersistentAttributesInternal();
return NS_OK;
}
nsresult
XULPersist::ApplyPersistentAttributesInternal()
{
nsCOMArray<Element> 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
nsCOMPtr<nsIStringEnumerator> ids;
rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
while (1) {
bool hasmore = false;
ids->HasMore(&hasmore);
if (!hasmore) {
break;
}
nsAutoString id;
ids->GetNext(id);
// We want to hold strong refs to the elements while applying
// persistent attributes, just in case.
const nsTArray<Element*>* 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<Element>& 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
nsCOMPtr<nsIStringEnumerator> attrs;
rv = mLocalStore->GetAttributeEnumerator(uri, aID, getter_AddRefs(attrs));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
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);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
RefPtr<nsAtom> 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 nsXULWindow.
if (element->IsXULElement(nsGkAtoms::window)) {
if (nsCOMPtr<nsIXULWindow> win = mDocument->GetXULWindowIfToplevelChrome()) {
continue;
}
}
Unused << element->SetAttr(kNameSpaceID_None, attr, value, true);
}
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

49
dom/xul/XULPersist.h Normal file
View File

@ -0,0 +1,49 @@
/* -*- 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 http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_XULPersist_h
#define mozilla_dom_XULPersist_h
class nsIXULStore;
namespace mozilla {
namespace dom {
class XULPersist final : public nsStubDocumentObserver
{
public:
NS_DECL_ISUPPORTS
explicit XULPersist(nsIDocument* aDocument);
void Init();
void DropDocumentReference();
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
protected:
void Persist(mozilla::dom::Element* aElement,
int32_t aNameSpaceID,
nsAtom* aAttribute);
private:
~XULPersist();
nsresult ApplyPersistentAttributes();
nsresult ApplyPersistentAttributesInternal();
nsresult ApplyPersistentAttributesToElements(const nsAString &aID,
nsCOMArray<Element>& aElements);
nsCOMPtr<nsIXULStore> mLocalStore;
// A weak pointer to our document. Nulled out by DropDocumentReference.
nsIDocument* MOZ_NON_OWNING_REF mDocument;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_XULPersist_h

View File

@ -25,6 +25,7 @@ if CONFIG['MOZ_XUL']:
'XULBroadcastManager.h',
'XULFrameElement.h',
'XULMenuElement.h',
'XULPersist.h',
'XULPopupElement.h',
'XULScrollElement.h',
'XULTextElement.h',
@ -44,6 +45,7 @@ if CONFIG['MOZ_XUL']:
'XULDocument.cpp',
'XULFrameElement.cpp',
'XULMenuElement.cpp',
'XULPersist.cpp',
'XULPopupElement.cpp',
'XULScrollElement.cpp',
'XULTextElement.cpp',

View File

@ -39,6 +39,7 @@ STATIC_ATOMS = [
Atom("moztype", "_moz-type"),
Atom("mozdirty", "_moz_dirty"),
Atom("mozdisallowselectionprint", "mozdisallowselectionprint"),
Atom("mozpersist", "mozpersist"),
Atom("mozdonotsend", "moz-do-not-send"),
Atom("mozeditorbogusnode", "_moz_editor_bogus_node"),
Atom("mozgeneratedcontentbefore", "_moz_generated_content_before"),