Bug 1700051: part 26) Correct typo in comment of `mozInlineSpellWordUtil::BuildSoftTe...
[gecko.git] / dom / l10n / L10nMutations.cpp
blobb1a39cbe383199a72dcadfb1c8af0b7c829fb996
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 http://mozilla.org/MPL/2.0/. */
7 #include "L10nMutations.h"
8 #include "mozilla/dom/DocumentInlines.h"
9 #include "nsRefreshDriver.h"
11 using namespace mozilla::dom;
13 NS_IMPL_CYCLE_COLLECTION_CLASS(L10nMutations)
15 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(L10nMutations)
16 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingElements)
17 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingElementsHash)
18 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
20 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(L10nMutations)
21 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingElements)
22 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingElementsHash)
23 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
25 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(L10nMutations)
26 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
27 NS_INTERFACE_MAP_ENTRY(nsISupports)
28 NS_INTERFACE_MAP_END
30 NS_IMPL_CYCLE_COLLECTING_ADDREF(L10nMutations)
31 NS_IMPL_CYCLE_COLLECTING_RELEASE(L10nMutations)
33 L10nMutations::L10nMutations(DOMLocalization* aDOMLocalization)
34 : mDOMLocalization(aDOMLocalization) {
35 mObserving = true;
38 L10nMutations::~L10nMutations() {
39 StopRefreshObserver();
40 MOZ_ASSERT(!mDOMLocalization,
41 "DOMLocalization<-->L10nMutations cycle should be broken.");
44 void L10nMutations::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
45 nsAtom* aAttribute, int32_t aModType,
46 const nsAttrValue* aOldValue) {
47 if (!mObserving) {
48 return;
51 if (aNameSpaceID == kNameSpaceID_None &&
52 (aAttribute == nsGkAtoms::datal10nid ||
53 aAttribute == nsGkAtoms::datal10nargs)) {
54 if (IsInRoots(aElement)) {
55 L10nElementChanged(aElement);
60 void L10nMutations::ContentAppended(nsIContent* aChild) {
61 if (!mObserving) {
62 return;
65 nsINode* node = aChild;
66 if (!IsInRoots(node)) {
67 return;
70 ErrorResult rv;
71 Sequence<OwningNonNull<Element>> elements;
72 while (node) {
73 if (node->IsElement()) {
74 DOMLocalization::GetTranslatables(*node, elements, rv);
77 node = node->GetNextSibling();
80 for (auto& elem : elements) {
81 L10nElementChanged(elem);
85 void L10nMutations::ContentInserted(nsIContent* aChild) {
86 if (!mObserving) {
87 return;
89 ErrorResult rv;
90 Sequence<OwningNonNull<Element>> elements;
92 if (!aChild->IsElement()) {
93 return;
95 Element* elem = aChild->AsElement();
97 if (!IsInRoots(elem)) {
98 return;
100 DOMLocalization::GetTranslatables(*aChild, elements, rv);
102 for (auto& elem : elements) {
103 L10nElementChanged(elem);
107 void L10nMutations::L10nElementChanged(Element* aElement) {
108 if (!mPendingElementsHash.Contains(aElement)) {
109 mPendingElements.AppendElement(aElement);
110 mPendingElementsHash.Insert(aElement);
113 if (!mRefreshObserver) {
114 StartRefreshObserver();
118 void L10nMutations::PauseObserving() { mObserving = false; }
120 void L10nMutations::ResumeObserving() { mObserving = true; }
122 void L10nMutations::WillRefresh(mozilla::TimeStamp aTime) {
123 StopRefreshObserver();
124 FlushPendingTranslations();
127 void L10nMutations::FlushPendingTranslations() {
128 if (!mDOMLocalization) {
129 return;
132 ErrorResult rv;
134 Sequence<OwningNonNull<Element>> elements;
136 for (auto& elem : mPendingElements) {
137 if (!elem->HasAttr(kNameSpaceID_None, nsGkAtoms::datal10nid)) {
138 continue;
141 if (!elements.AppendElement(*elem, fallible)) {
142 mozalloc_handle_oom(0);
146 mPendingElementsHash.Clear();
147 mPendingElements.Clear();
149 RefPtr<Promise> promise = mDOMLocalization->TranslateElements(elements, rv);
152 void L10nMutations::Disconnect() {
153 StopRefreshObserver();
154 mDOMLocalization = nullptr;
157 void L10nMutations::StartRefreshObserver() {
158 if (!mDOMLocalization || mRefreshObserver) {
159 return;
162 if (!mRefreshDriver) {
163 nsPIDOMWindowInner* innerWindow =
164 mDOMLocalization->GetParentObject()->AsInnerWindow();
165 Document* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
166 if (doc) {
167 nsPresContext* ctx = doc->GetPresContext();
168 if (ctx) {
169 mRefreshDriver = ctx->RefreshDriver();
174 // If we can't start the refresh driver, it means
175 // that the presContext is not available yet.
176 // In that case, we'll trigger the flush of pending
177 // elements in Document::CreatePresShell.
178 if (mRefreshDriver) {
179 mRefreshDriver->AddRefreshObserver(this, FlushType::Style,
180 "L10n mutations");
181 mRefreshObserver = true;
182 } else {
183 NS_WARNING("[l10n][mutations] Failed to start a refresh observer.");
187 void L10nMutations::StopRefreshObserver() {
188 if (!mDOMLocalization) {
189 return;
192 if (mRefreshDriver) {
193 mRefreshDriver->RemoveRefreshObserver(this, FlushType::Style);
194 mRefreshObserver = false;
198 void L10nMutations::OnCreatePresShell() {
199 if (!mPendingElements.IsEmpty()) {
200 StartRefreshObserver();
204 bool L10nMutations::IsInRoots(nsINode* aNode) {
205 // If the root of the mutated element is in the light DOM,
206 // we know it must be covered by our observer directly.
208 // Otherwise, we need to check if its subtree root is the same
209 // as any of the `DOMLocalization::mRoots` subtree roots.
210 nsINode* root = aNode->SubtreeRoot();
212 // If element is in light DOM, it must be covered by one of
213 // the DOMLocalization roots to end up here.
214 MOZ_ASSERT_IF(!root->IsShadowRoot(),
215 mDOMLocalization->SubtreeRootInRoots(root));
217 return !root->IsShadowRoot() || mDOMLocalization->SubtreeRootInRoots(root);