зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
7a94de539b
Коммит
fbc6e0304a
|
@ -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
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче