Bug 1546432 - Introduce mozilla::dom::l10n::Mutations. r=smaug

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Zibi Braniecki 2019-05-21 19:21:54 +00:00
Родитель 7a94de539b
Коммит fbc6e0304a
5 изменённых файлов: 261 добавлений и 1 удалений

178
dom/l10n/Mutations.cpp Normal file
Просмотреть файл

@ -0,0 +1,178 @@
#include "Mutations.h"
#include "mozilla/dom/DocumentInlines.h"
namespace mozilla {
namespace dom {
namespace l10n {
NS_IMPL_CYCLE_COLLECTION_CLASS(Mutations)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Mutations)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingElements)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingElementsHash)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Mutations)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingElements)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingElementsHash)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Mutations)
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Mutations)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Mutations)
Mutations::Mutations(DocumentL10n* aDocumentL10n)
: mDocumentL10n(aDocumentL10n) {
mObserving = true;
}
void Mutations::AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID,
nsAtom* aAttribute, int32_t aModType,
const nsAttrValue* aOldValue) {
if (!mObserving) {
return;
}
Document* uncomposedDoc = aElement->GetUncomposedDoc();
if (uncomposedDoc) {
if (aNameSpaceID == kNameSpaceID_None &&
(aAttribute == nsGkAtoms::datal10nid ||
aAttribute == nsGkAtoms::datal10nargs)) {
L10nElementChanged(aElement);
}
}
}
void Mutations::ContentAppended(nsIContent* aChild) {
if (!mObserving) {
return;
}
ErrorResult rv;
Sequence<OwningNonNull<Element>> elements;
nsINode* node = aChild;
while (node) {
if (node->IsElement()) {
Element* elem = node->AsElement();
Document* uncomposedDoc = elem->GetUncomposedDoc();
if (uncomposedDoc) {
mDocumentL10n->GetTranslatables(*node, elements, rv);
}
}
node = node->GetNextSibling();
}
for (auto& elem : elements) {
L10nElementChanged(elem);
}
}
void Mutations::ContentInserted(nsIContent* aChild) {
if (!mObserving) {
return;
}
ErrorResult rv;
Sequence<OwningNonNull<Element>> elements;
if (!aChild->IsElement()) {
return;
}
Element* elem = aChild->AsElement();
Document* uncomposedDoc = elem->GetUncomposedDoc();
if (!uncomposedDoc) {
return;
}
mDocumentL10n->GetTranslatables(*aChild, elements, rv);
for (auto& elem : elements) {
L10nElementChanged(elem);
}
}
void Mutations::L10nElementChanged(Element* aElement) {
if (!mPendingElementsHash.Contains(aElement)) {
mPendingElements.AppendElement(aElement);
mPendingElementsHash.PutEntry(aElement);
}
if (!mRefreshObserver) {
StartRefreshObserver();
}
}
void Mutations::PauseObserving() { mObserving = false; }
void Mutations::ResumeObserving() { mObserving = true; }
void Mutations::WillRefresh(mozilla::TimeStamp aTime) {
StopRefreshObserver();
FlushPendingTranslations();
}
void Mutations::FlushPendingTranslations() {
if (!mDocumentL10n) {
return;
}
ErrorResult rv;
Sequence<OwningNonNull<Element>> elements;
for (auto& elem : mPendingElements) {
if (!elem->HasAttr(kNameSpaceID_None, nsGkAtoms::datal10nid)) {
continue;
}
elements.AppendElement(*elem, fallible);
}
mPendingElementsHash.Clear();
mPendingElements.Clear();
RefPtr<Promise> promise = mDocumentL10n->TranslateElements(elements, rv);
}
void Mutations::Disconnect() {
StopRefreshObserver();
mDocumentL10n = nullptr;
}
void Mutations::StartRefreshObserver() {
if (!mDocumentL10n) {
return;
}
if (!mRefreshDriver) {
nsPresContext* ctx = mDocumentL10n->GetDocument()->GetPresContext();
if (!ctx) {
return;
}
mRefreshDriver = ctx->RefreshDriver();
}
if (mRefreshDriver) {
mRefreshDriver->AddRefreshObserver(this, FlushType::Style);
mRefreshObserver = true;
}
}
void Mutations::StopRefreshObserver() {
if (!mDocumentL10n) {
return;
}
if (mRefreshDriver) {
mRefreshDriver->RemoveRefreshObserver(this, FlushType::Style);
mRefreshObserver = false;
}
}
} // namespace l10n
} // namespace dom
} // namespace mozilla

78
dom/l10n/Mutations.h Normal file
Просмотреть файл

@ -0,0 +1,78 @@
#ifndef mozilla_dom_l10n_Mutations_h__
#define mozilla_dom_l10n_Mutations_h__
#include "nsRefreshDriver.h"
#include "nsStubMutationObserver.h"
#include "nsTHashtable.h"
#include "mozilla/dom/DocumentL10n.h"
namespace mozilla {
namespace dom {
namespace l10n {
/**
* Mutations manage observing roots for localization
* changes and coalescing pending translations into
* batches - one per animation frame.
*/
class Mutations final : public nsStubMutationObserver,
public nsARefreshObserver {
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(Mutations, nsIMutationObserver)
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
explicit Mutations(DocumentL10n* aDocumentL10n);
/**
* Pause root observation.
* This is useful for injecting already-translated
* content into an observed root, without causing
* superflues translation.
*/
void PauseObserving();
/**
* Resume root observation.
*/
void ResumeObserving();
/**
* Disconnect roots, stop refresh observer
* and break the cycle collection deadlock
* by removing the reference to mDocumentL10n.
*/
void Disconnect();
protected:
bool mObserving = false;
bool mRefreshObserver = false;
RefPtr<nsRefreshDriver> mRefreshDriver;
DocumentL10n* mDocumentL10n;
// The hash is used to speed up lookups into mPendingElements.
nsTHashtable<nsRefPtrHashKey<Element>> mPendingElementsHash;
nsTArray<RefPtr<Element>> mPendingElements;
virtual void WillRefresh(mozilla::TimeStamp aTime) override;
void StartRefreshObserver();
void StopRefreshObserver();
void L10nElementChanged(Element* aElement);
void FlushPendingTranslations();
private:
~Mutations() {
StopRefreshObserver();
MOZ_ASSERT(!mDocumentL10n,
"DocumentL10n<-->Mutations cycle should be broken.");
}
};
} // namespace l10n
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_l10n_Mutations_h__

Просмотреть файл

@ -9,10 +9,12 @@ with Files("**"):
EXPORTS.mozilla.dom.l10n += [
'DOMOverlays.h',
'Mutations.h',
]
UNIFIED_SOURCES += [
'DOMOverlays.cpp',
'Mutations.cpp',
]
LOCAL_INCLUDES += [

Просмотреть файл

@ -16,7 +16,7 @@ using mozilla::NullPrincipal;
using namespace mozilla::dom;
using namespace mozilla::dom::l10n;
already_AddRefed<Document> SetUpDocument() {
static already_AddRefed<Document> SetUpDocument() {
nsCOMPtr<nsIURI> uri;
NS_NewURI(getter_AddRefs(uri), "about:blank");
nsCOMPtr<nsIPrincipal> principal =

Просмотреть файл

@ -134,6 +134,8 @@ class DocumentL10n final : public nsIObserver,
void TriggerInitialDocumentTranslation();
void InitialDocumentTranslationCompleted();
Document* GetDocument() { return mDocument; };
};
} // namespace dom