Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/extensions/spellcheck/hunspell/glue/./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp
Warning:line 245, column 7
Value stored to 'rv' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_hunspell_glue0.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/extensions/spellcheck/hunspell/glue -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/extensions/spellcheck/hunspell/glue -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -include hunspell_alloc_hooks.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D HUNSPELL_STATIC -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/extensions/spellcheck/hunspell/glue -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/extensions/spellcheck/hunspell/glue -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/security/rlbox -I /root/firefox-clang/extensions/spellcheck/hunspell/src -I /root/firefox-clang/extensions/spellcheck/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/third_party/abseil-cpp -I /root/firefox-clang/toolkit/components/telemetry -I /root/firefox-clang/xpcom/base -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ Unified_cpp_hunspell_glue0.cpp
1/******* BEGIN LICENSE BLOCK *******
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Initial Developers of the Original Code are Kevin Hendricks (MySpell)
15 * and László Németh (Hunspell). Portions created by the Initial Developers
16 * are Copyright (C) 2002-2005 the Initial Developers. All Rights Reserved.
17 *
18 * Contributor(s): Kevin Hendricks (kevin.hendricks@sympatico.ca)
19 * David Einstein (deinst@world.std.com)
20 * Michiel van Leeuwen (mvl@exedo.nl)
21 * Caolan McNamara (cmc@openoffice.org)
22 * László Németh (nemethl@gyorsposta.hu)
23 * Davide Prina
24 * Giuseppe Modugno
25 * Gianluca Turconi
26 * Simon Brouwer
27 * Noll Janos
28 * Biro Arpad
29 * Goldman Eleonora
30 * Sarlos Tamas
31 * Bencsath Boldizsar
32 * Halacsy Peter
33 * Dvornik Laszlo
34 * Gefferth Andras
35 * Nagy Viktor
36 * Varga Daniel
37 * Chris Halls
38 * Rene Engelhard
39 * Bram Moolenaar
40 * Dafydd Jones
41 * Harri Pitkanen
42 * Andras Timar
43 * Tor Lillqvist
44 * Jesper Kristensen (mail@jesperkristensen.dk)
45 *
46 * Alternatively, the contents of this file may be used under the terms of
47 * either the GNU General Public License Version 2 or later (the "GPL"), or
48 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
49 * in which case the provisions of the GPL or the LGPL are applicable instead
50 * of those above. If you wish to allow use of your version of this file only
51 * under the terms of either the GPL or the LGPL, and not to allow others to
52 * use your version of this file under the terms of the MPL, indicate your
53 * decision by deleting the provisions above and replace them with the notice
54 * and other provisions required by the GPL or the LGPL. If you do not delete
55 * the provisions above, a recipient may use your version of this file under
56 * the terms of any one of the MPL, the GPL or the LGPL.
57 *
58 ******* END LICENSE BLOCK *******/
59
60#include "mozHunspell.h"
61#include "nsReadableUtils.h"
62#include "nsString.h"
63#include "nsIObserverService.h"
64#include "nsIDirectoryEnumerator.h"
65#include "nsIFile.h"
66#include "nsUnicharUtils.h"
67#include "nsCRT.h"
68#include "mozInlineSpellChecker.h"
69#include "nsIPrefBranch.h"
70#include "nsIPrefService.h"
71#include "nsNetUtil.h"
72#include "prenv.h"
73#include "mozilla/Components.h"
74#include "mozilla/Services.h"
75#include "mozilla/dom/ContentParent_NotifyUpdatedDictionaries.h"
76
77#include <tuple>
78
79using namespace mozilla;
80
81NS_IMPL_CYCLE_COLLECTING_ADDREF(mozHunspell)MozExternalRefCountType mozHunspell::AddRef(void) { static_assert
(!std::is_destructible_v<mozHunspell>, "Reference-counted class "
"mozHunspell" " should not have a public destructor. " "Make this class's destructor non-public"
); do { static_assert( mozilla::detail::AssertionConditionType
<decltype(int32_t(mRefCnt) >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0"
" (" "illegal refcnt" ")", "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 81); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0"
") (" "illegal refcnt" ")"); do { MOZ_CrashSequence(__null, 81
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); _mOwningThread.AssertOwnership("mozHunspell" " not thread-safe"
); nsISupports* base = mozHunspell::cycleCollection::Upcast(this
); nsrefcnt count = mRefCnt.incr(base); NS_LogAddRef((this), (
count), ("mozHunspell"), (uint32_t)(sizeof(*this))); return count
; }
82NS_IMPL_CYCLE_COLLECTING_RELEASE(mozHunspell)MozExternalRefCountType mozHunspell::Release(void) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(int32_t
(mRefCnt) > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) > 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0"
" (" "dup release" ")", "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 82); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0"
") (" "dup release" ")"); do { MOZ_CrashSequence(__null, 82)
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); _mOwningThread.AssertOwnership("mozHunspell" " not thread-safe"
); nsISupports* base = mozHunspell::cycleCollection::Upcast(this
); nsrefcnt count = mRefCnt.decr(base); if (count == 0) { NS_CycleCollectableHasRefCntZero
(); } NS_LogRelease((this), (count), ("mozHunspell")); return
count; } void mozHunspell::DeleteCycleCollectable(void) { delete
(this); }
83
84NS_INTERFACE_MAP_BEGIN(mozHunspell)nsresult mozHunspell::QueryInterface(const nsIID& aIID, void
** aInstancePtr) { do { if (!(aInstancePtr)) { NS_DebugBreak(
NS_DEBUG_ASSERTION, "QueryInterface requires a non-NULL destination!"
, "aInstancePtr", "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 84); MOZ_PretendNoReturn(); } } while (0); nsISupports* foundInterface
;
85 NS_INTERFACE_MAP_ENTRY(mozISpellCheckingEngine)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, mozISpellCheckingEngine>)) foundInterface
= static_cast<mozISpellCheckingEngine*>(this); else
86 NS_INTERFACE_MAP_ENTRY(nsIObserver)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsIObserver>)) foundInterface = static_cast
<nsIObserver*>(this); else
87 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsISupportsWeakReference>)) foundInterface
= static_cast<nsISupportsWeakReference*>(this); else
88 NS_INTERFACE_MAP_ENTRY(nsIMemoryReporter)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsIMemoryReporter>)) foundInterface
= static_cast<nsIMemoryReporter*>(this); else
89 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozISpellCheckingEngine)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsISupports>)) foundInterface = static_cast
<nsISupports*>(static_cast<mozISpellCheckingEngine*>
(this)); else
90 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozHunspell)if (TopThreeWordsEquals( aIID, (nsXPCOMCycleCollectionParticipant
::kIID), (nsCycleCollectionISupports::kIID)) && (LowWordEquals
(aIID, (nsXPCOMCycleCollectionParticipant::kIID)) || LowWordEquals
(aIID, (nsCycleCollectionISupports::kIID)))) { if (LowWordEquals
(aIID, (nsXPCOMCycleCollectionParticipant::kIID))) { *aInstancePtr
= mozHunspell::cycleCollection::GetParticipant(); return NS_OK
; } if (LowWordEquals(aIID, (nsCycleCollectionISupports::kIID
))) { *aInstancePtr = mozHunspell::cycleCollection::Upcast(this
); return NS_OK; } foundInterface = nullptr; } else
91NS_INTERFACE_MAP_ENDfoundInterface = 0; nsresult status; if (!foundInterface) { do
{ static_assert( mozilla::detail::AssertionConditionType<
decltype(!aIID.Equals((nsISupports::kIID)))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aIID.Equals((nsISupports::kIID
))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!aIID.Equals((nsISupports::kIID))", "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 91); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aIID.Equals((nsISupports::kIID))"
")"); do { MOZ_CrashSequence(__null, 91); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); status = NS_NOINTERFACE
; } else { (foundInterface)->AddRef(); status = NS_OK; } *
aInstancePtr = foundInterface; return status; }
92
93NS_IMPL_CYCLE_COLLECTION_WEAK(mozHunspell, mPersonalDictionary)mozHunspell::cycleCollection mozHunspell::_cycleCollectorGlobal
; void mozHunspell::cycleCollection::Unlink(void* p) { mozHunspell
* tmp = DowncastCCParticipant<mozHunspell>(p); ImplCycleCollectionUnlink
(tmp->mPersonalDictionary); tmp->ClearWeakReferences();
(void)tmp; } nsresult mozHunspell::cycleCollection::TraverseNative
( void* p, nsCycleCollectionTraversalCallback& cb) { mozHunspell
* tmp = DowncastCCParticipant<mozHunspell>(p); cb.DescribeRefCountedNode
(tmp->mRefCnt.get(), "mozHunspell"); ImplCycleCollectionTraverse
(cb, tmp->mPersonalDictionary, "mPersonalDictionary", 0); (
void)tmp; return NS_OK; }
94
95NS_IMPL_COMPONENT_FACTORY(mozHunspell)template <> already_AddRefed<nsISupports> mozCreateComponent
<mozHunspell>()
{
96 auto hunspell = MakeRefPtr<mozHunspell>();
97 if (NS_SUCCEEDED(hunspell->Init())((bool)(__builtin_expect(!!(!NS_FAILED_impl(hunspell->Init
())), 1)))
) {
98 return hunspell.forget().downcast<mozISpellCheckingEngine>();
99 }
100 return nullptr;
101}
102
103mozHunspell::mozHunspell() {
104#ifdef DEBUG1
105 // There must be only one instance of this class: it reports memory based on
106 // a single static count in HunspellAllocator.
107 static bool hasRun = false;
108 MOZ_ASSERT(!hasRun)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!hasRun)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!hasRun))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!hasRun", "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 108); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!hasRun" ")"
); do { MOZ_CrashSequence(__null, 108); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
109 hasRun = true;
110#endif
111}
112
113nsresult mozHunspell::Init() {
114 LoadDictionaryList(false);
115
116 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
117 if (obs) {
118 obs->AddObserver(this, "profile-do-change", true);
119 obs->AddObserver(this, "profile-after-change", true);
120 }
121
122 mozilla::RegisterWeakMemoryReporter(this);
123
124 return NS_OK;
125}
126
127mozHunspell::~mozHunspell() {
128 mozilla::UnregisterWeakMemoryReporter(this);
129
130 mPersonalDictionary = nullptr;
131 mHunspells.Clear();
132}
133
134NS_IMETHODIMPnsresult
135mozHunspell::GetDictionaries(nsTArray<nsCString>& aDictionaries) {
136 MOZ_ASSERT(aDictionaries.IsEmpty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aDictionaries.IsEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aDictionaries.IsEmpty()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("aDictionaries.IsEmpty()"
, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 136); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aDictionaries.IsEmpty()"
")"); do { MOZ_CrashSequence(__null, 136); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
137 for (auto iter = mHunspells.ConstIter(); !iter.Done(); iter.Next()) {
138 if (iter.Data().mEnabled) {
139 aDictionaries.AppendElement(iter.Key());
140 }
141 }
142 return NS_OK;
143}
144
145/* Set the Dictionaries.
146 * This also Loads the dictionaries and initializes the converter using the
147 * dictionaries converter
148 */
149NS_IMETHODIMPnsresult
150mozHunspell::SetDictionaries(const nsTArray<nsCString>& aDictionaries) {
151 if (aDictionaries.IsEmpty()) {
152 mHunspells.Clear();
153 return NS_OK;
154 }
155
156 // Disable any dictionaries we've already loaded that we're not
157 // going to use.
158 for (auto iter = mHunspells.Iter(); !iter.Done(); iter.Next()) {
159 if (!aDictionaries.Contains(iter.Key())) {
160 iter.Data().mEnabled = false;
161 }
162 }
163
164 bool firstDictionary = true;
165 for (const auto& dictionary : aDictionaries) {
166 NS_ConvertUTF8toUTF16 dict(dictionary);
167 nsIURI* affFile = mDictionaries.GetWeak(dict);
168 if (!affFile) {
169 return NS_ERROR_FILE_NOT_FOUND;
170 }
171
172 nsAutoCString affFileName;
173 nsresult rv = affFile->GetSpec(affFileName);
174 NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 174); return rv; } } while (false)
;
175
176 if (auto entry = mHunspells.Lookup(dictionary)) {
177 if (entry.Data().mAffixFileName == affFileName) {
178 entry.Data().mEnabled = true;
179 continue;
180 }
181 }
182
183 DictionaryData dictionaryData;
184 dictionaryData.mAffixFileName = affFileName;
185
186 // Load the first dictionary now, we'll load the others lazily during
187 // checking.
188 if (firstDictionary) {
189 rv = dictionaryData.LoadIfNecessary();
190 NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 190); return rv; } } while (false)
;
191 firstDictionary = false;
192 }
193
194 mHunspells.InsertOrUpdate(dictionary, std::move(dictionaryData));
195 }
196
197 // If we have a large number of dictionaries loaded, try freeing any disabled
198 // dictionaries to limit memory use.
199 if (mHunspells.Count() > 10) {
200 mHunspells.RemoveIf([](const auto& iter) { return !iter.Data().mEnabled; });
201 }
202
203 return NS_OK;
204}
205
206NS_IMETHODIMPnsresult mozHunspell::GetPersonalDictionary(
207 mozIPersonalDictionary** aPersonalDictionary) {
208 *aPersonalDictionary = mPersonalDictionary;
209 NS_IF_ADDREF(*aPersonalDictionary)ns_if_addref(*aPersonalDictionary);
210 return NS_OK;
211}
212
213NS_IMETHODIMPnsresult mozHunspell::SetPersonalDictionary(
214 mozIPersonalDictionary* aPersonalDictionary) {
215 mPersonalDictionary = aPersonalDictionary;
216 return NS_OK;
217}
218
219NS_IMETHODIMPnsresult mozHunspell::GetDictionaryList(
220 nsTArray<nsCString>& aDictionaries) {
221 MOZ_ASSERT(aDictionaries.IsEmpty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aDictionaries.IsEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aDictionaries.IsEmpty()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("aDictionaries.IsEmpty()"
, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 221); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aDictionaries.IsEmpty()"
")"); do { MOZ_CrashSequence(__null, 221); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
222 for (const auto& key : mDictionaries.Keys()) {
223 aDictionaries.AppendElement(NS_ConvertUTF16toUTF8(key));
224 }
225
226 return NS_OK;
227}
228
229void mozHunspell::LoadDictionaryList(bool aNotifyChildProcesses) {
230 mDictionaries.Clear();
231
232 nsresult rv;
233
234 // find built in dictionaries, or dictionaries specified in
235 // spellchecker.dictionary_path in prefs
236 nsCOMPtr<nsIFile> dictDir;
237
238 // check preferences first
239 nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID"@mozilla.org/preferences-service;1"));
240 if (prefs) {
241 nsAutoCString extDictPath;
242 rv = prefs->GetCharPref("spellchecker.dictionary_path", extDictPath);
243 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) {
244 // set the spellchecker.dictionary_path
245 rv = NS_NewNativeLocalFile(extDictPath, getter_AddRefs(dictDir));
Value stored to 'rv' is never read
246 }
247 if (dictDir) {
248 LoadDictionariesFromDir(dictDir);
249 }
250 }
251
252 // find dictionaries in DICPATH
253 char* dicEnv = PR_GetEnv("DICPATH");
254 if (dicEnv) {
255 // do a two-pass dance so dictionaries are loaded right-to-left as
256 // preference
257 nsTArray<nsCOMPtr<nsIFile>> dirs;
258 nsAutoCString env(dicEnv); // assume dicEnv is UTF-8
259
260 char* currPath = nullptr;
261 char* nextPaths = env.BeginWriting();
262 while ((currPath = NS_strtok(":", &nextPaths))) {
263 nsCOMPtr<nsIFile> dir;
264 rv = NS_NewNativeLocalFile(nsCString(currPath), getter_AddRefs(dir));
265 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) {
266 dirs.AppendElement(dir);
267 }
268 }
269
270 // load them in reverse order so they override each other properly
271 for (int32_t i = dirs.Length() - 1; i >= 0; i--) {
272 LoadDictionariesFromDir(dirs[i]);
273 }
274 }
275
276 // find dictionaries from restartless extensions
277 for (int32_t i = 0; i < mDynamicDirectories.Count(); i++) {
278 LoadDictionariesFromDir(mDynamicDirectories[i]);
279 }
280
281 for (const auto& dictionaryEntry : mDynamicDictionaries) {
282 mDictionaries.InsertOrUpdate(dictionaryEntry.GetKey(),
283 dictionaryEntry.GetData());
284 }
285
286 DictionariesChanged(aNotifyChildProcesses);
287}
288
289void mozHunspell::DictionariesChanged(bool aNotifyChildProcesses) {
290 // Now we have finished updating the list of dictionaries, update the current
291 // dictionary and any editors which may use it.
292 mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
293
294 if (aNotifyChildProcesses) {
295 mozilla::dom::ContentParent_NotifyUpdatedDictionaries();
296 }
297
298 // Check if the current dictionaries are still available.
299 // If not, try to replace it with other dictionaries of the same language.
300 if (!mHunspells.IsEmpty()) {
301 nsTArray<nsCString> dictionaries;
302 for (auto iter = mHunspells.ConstIter(); !iter.Done(); iter.Next()) {
303 if (iter.Data().mEnabled) {
304 dictionaries.AppendElement(iter.Key());
305 }
306 }
307 nsresult rv = SetDictionaries(dictionaries);
308 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) return;
309 }
310
311 // If the current dictionaries are gone, and we don't have a good replacement,
312 // set no current dictionary.
313 if (!mHunspells.IsEmpty()) {
314 nsTArray<nsCString> empty;
315 SetDictionaries(empty);
316 }
317}
318
319NS_IMETHODIMPnsresult
320mozHunspell::LoadDictionariesFromDir(nsIFile* aDir) {
321 nsresult rv;
322
323 bool check = false;
324 rv = aDir->Exists(&check);
325 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) || !check) return NS_ERROR_UNEXPECTED;
326
327 rv = aDir->IsDirectory(&check);
328 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) || !check) return NS_ERROR_UNEXPECTED;
329
330 nsCOMPtr<nsIDirectoryEnumerator> files;
331 rv = aDir->GetDirectoryEntries(getter_AddRefs(files));
332 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) return NS_ERROR_UNEXPECTED;
333
334 nsCOMPtr<nsIFile> file;
335 while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file)))((bool)(__builtin_expect(!!(!NS_FAILED_impl(files->GetNextFile
(getter_AddRefs(file)))), 1)))
&& file) {
336 nsAutoString leafName;
337 file->GetLeafName(leafName);
338 if (!StringEndsWith(leafName, u".dic"_ns)) continue;
339
340 nsAutoString dict(leafName);
341 dict.SetLength(dict.Length() - 4); // magic length of ".dic"
342
343 // check for the presence of the .aff file
344 leafName = dict;
345 leafName.AppendLiteral(".aff");
346 file->SetLeafName(leafName);
347 rv = file->Exists(&check);
348 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) || !check) continue;
349
350 // Replace '_' separator with '-'
351 dict.ReplaceChar('_', '-');
352
353 nsCOMPtr<nsIURI> uri;
354 rv = NS_NewFileURI(getter_AddRefs(uri), file);
355 NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 355); return rv; } } while (false)
;
356
357 mDictionaries.InsertOrUpdate(dict, uri);
358 }
359
360 return NS_OK;
361}
362
363nsresult mozHunspell::DictionaryData::ConvertCharset(const nsAString& aStr,
364 std::string& aDst) {
365 if (NS_WARN_IF(!mEncoder)NS_warn_if_impl(!mEncoder, "!mEncoder", "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 365)
) {
366 return NS_ERROR_NOT_INITIALIZED;
367 }
368
369 auto src = Span(aStr.BeginReading(), aStr.Length());
370 CheckedInt<size_t> needed =
371 mEncoder->MaxBufferLengthFromUTF16WithoutReplacement(src.Length());
372 if (!needed.isValid()) {
373 return NS_ERROR_OUT_OF_MEMORY;
374 }
375
376 aDst.resize(needed.value());
377
378 char* dstPtr = &aDst[0];
379 auto dst = Span(reinterpret_cast<uint8_t*>(dstPtr), needed.value());
380
381 uint32_t result;
382 size_t written;
383 std::tie(result, std::ignore, written) =
384 mEncoder->EncodeFromUTF16WithoutReplacement(src, dst, true);
385 MOZ_ASSERT(result != kOutputFull)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(result != kOutputFull)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(result != kOutputFull))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("result != kOutputFull"
, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 385); AnnotateMozCrashReason("MOZ_ASSERT" "(" "result != kOutputFull"
")"); do { MOZ_CrashSequence(__null, 385); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
386 if (result != kInputEmpty) {
387 return NS_ERROR_UENC_NOMAPPING;
388 }
389 aDst.resize(written);
390 mEncoder->Encoding()->NewEncoderInto(*mEncoder);
391 return NS_OK;
392}
393
394nsresult mozHunspell::DictionaryData::LoadIfNecessary() {
395 if (mHunspell && mEncoder && mDecoder) {
396 return NS_OK;
397 }
398
399 if (mLoadFailed) {
400 return NS_ERROR_FAILURE;
401 }
402
403 nsCString dictFileName = mAffixFileName;
404 int32_t dotPos = dictFileName.RFindChar('.');
405 if (dotPos == -1) {
406 mLoadFailed = true;
407 return NS_ERROR_FAILURE;
408 }
409 dictFileName.SetLength(dotPos);
410 dictFileName.AppendLiteral(".dic");
411
412 UniquePtr<RLBoxHunspell> hunspell(
413 RLBoxHunspell::Create(mAffixFileName, dictFileName));
414 if (!hunspell) {
415 mLoadFailed = true;
416 // TODO Bug 1788857: Verify error propagation in case of inaccessible file
417 return NS_ERROR_OUT_OF_MEMORY;
418 }
419 mHunspell = std::move(hunspell);
420 auto encoding =
421 Encoding::ForLabelNoReplacement(mHunspell->get_dict_encoding());
422 if (!encoding) {
423 mLoadFailed = true;
424 return NS_ERROR_UCONV_NOCONV;
425 }
426 mEncoder = encoding->NewEncoder();
427 mDecoder = encoding->NewDecoderWithoutBOMHandling();
428 return NS_OK;
429}
430
431NS_IMETHODIMPnsresult
432mozHunspell::CollectReports(nsIHandleReportCallback* aHandleReport,
433 nsISupports* aData, bool aAnonymize) {
434 MOZ_COLLECT_REPORT("explicit/spell-check", KIND_HEAP, UNITS_BYTES,(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/spell-check"
), KIND_HEAP, UNITS_BYTES, HunspellAllocator::MemoryAllocated
(), nsLiteralCString("Memory used by the spell-checking engine."
), aData)
435 HunspellAllocator::MemoryAllocated(),(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/spell-check"
), KIND_HEAP, UNITS_BYTES, HunspellAllocator::MemoryAllocated
(), nsLiteralCString("Memory used by the spell-checking engine."
), aData)
436 "Memory used by the spell-checking engine.")(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/spell-check"
), KIND_HEAP, UNITS_BYTES, HunspellAllocator::MemoryAllocated
(), nsLiteralCString("Memory used by the spell-checking engine."
), aData)
;
437
438 return NS_OK;
439}
440
441NS_IMETHODIMPnsresult
442mozHunspell::Check(const nsAString& aWord, bool* aResult) {
443 if (NS_WARN_IF(!aResult)NS_warn_if_impl(!aResult, "!aResult", "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 443)
) {
444 return NS_ERROR_INVALID_ARG;
445 }
446
447 if (NS_WARN_IF(mHunspells.IsEmpty())NS_warn_if_impl(mHunspells.IsEmpty(), "mHunspells.IsEmpty()",
"./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 447)
) {
448 return NS_ERROR_FAILURE;
449 }
450
451 *aResult = true;
452 for (auto iter = mHunspells.Iter(); !iter.Done(); iter.Next()) {
453 if (!iter.Data().mEnabled) {
454 continue;
455 }
456
457 nsresult rv = iter.Data().LoadIfNecessary();
458 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
459 continue;
460 }
461
462 std::string charsetWord;
463 rv = iter.Data().ConvertCharset(aWord, charsetWord);
464 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
465 continue;
466 }
467
468 // Depending upon the encoding, we might end up with a string that begins
469 // with the null byte. Since the hunspell interface uses C-style strings,
470 // this appears like an empty string, and hunspell marks empty strings as
471 // spelled correctly. Skip these cases to allow another dictionary to have
472 // the chance to spellcheck them.
473 if (charsetWord.empty() || charsetWord[0] == 0) {
474 continue;
475 }
476
477 *aResult = iter.Data().mHunspell->spell(charsetWord);
478 if (*aResult) {
479 break;
480 }
481 }
482
483 if (!*aResult && mPersonalDictionary) {
484 return mPersonalDictionary->Check(aWord, aResult);
485 }
486
487 return NS_OK;
488}
489
490NS_IMETHODIMPnsresult
491mozHunspell::Suggest(const nsAString& aWord, nsTArray<nsString>& aSuggestions) {
492 if (NS_WARN_IF(mHunspells.IsEmpty())NS_warn_if_impl(mHunspells.IsEmpty(), "mHunspells.IsEmpty()",
"./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 492)
) {
493 return NS_ERROR_FAILURE;
494 }
495
496 MOZ_ASSERT(aSuggestions.IsEmpty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSuggestions.IsEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSuggestions.IsEmpty()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("aSuggestions.IsEmpty()"
, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 496); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSuggestions.IsEmpty()"
")"); do { MOZ_CrashSequence(__null, 496); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
497
498 for (auto iter = mHunspells.Iter(); !iter.Done(); iter.Next()) {
499 if (!iter.Data().mEnabled) {
500 continue;
501 }
502
503 nsresult rv = iter.Data().LoadIfNecessary();
504 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
505 continue;
506 }
507
508 std::string charsetWord;
509 rv = iter.Data().ConvertCharset(aWord, charsetWord);
510 NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 510); return rv; } } while (false)
;
511
512 std::vector<std::string> suggestions =
513 iter.Data().mHunspell->suggest(charsetWord);
514 if (!suggestions.empty()) {
515 aSuggestions.SetCapacity(aSuggestions.Length() + suggestions.size());
516 for (Span<const char> charSrc : suggestions) {
517 // Convert the suggestion to utf16
518 auto src = AsBytes(charSrc);
519 nsresult rv =
520 iter.Data().mDecoder->Encoding()->DecodeWithoutBOMHandling(
521 src, *aSuggestions.AppendElement());
522 NS_ENSURE_SUCCESS(rv, rv)do { nsresult __rv = rv; if (((bool)(__builtin_expect(!!(NS_FAILED_impl
(__rv)), 0)))) { const char* name = mozilla::GetStaticErrorName
(__rv); mozilla::SmprintfPointer msg = mozilla::Smprintf( "NS_ENSURE_SUCCESS(%s, %s) failed with "
"result 0x%" "X" "%s%s%s", "rv", "rv", static_cast<uint32_t
>(__rv), name ? " (" : "", name ? name : "", name ? ")" : ""
); NS_DebugBreak(NS_DEBUG_WARNING, msg.get(), nullptr, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 522); return rv; } } while (false)
;
523 iter.Data().mDecoder->Encoding()->NewDecoderWithoutBOMHandlingInto(
524 *iter.Data().mDecoder);
525 }
526 }
527 }
528
529 return NS_OK;
530}
531
532NS_IMETHODIMPnsresult
533mozHunspell::Observe(nsISupports* aSubj, const char* aTopic,
534 const char16_t* aData) {
535 NS_ASSERTION(!strcmp(aTopic, "profile-do-change") ||do { if (!(!strcmp(aTopic, "profile-do-change") || !strcmp(aTopic
, "profile-after-change"))) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "Unexpected observer topic", "!strcmp(aTopic, \"profile-do-change\") || !strcmp(aTopic, \"profile-after-change\")"
, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 537); MOZ_PretendNoReturn(); } } while (0)
536 !strcmp(aTopic, "profile-after-change"),do { if (!(!strcmp(aTopic, "profile-do-change") || !strcmp(aTopic
, "profile-after-change"))) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "Unexpected observer topic", "!strcmp(aTopic, \"profile-do-change\") || !strcmp(aTopic, \"profile-after-change\")"
, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 537); MOZ_PretendNoReturn(); } } while (0)
537 "Unexpected observer topic")do { if (!(!strcmp(aTopic, "profile-do-change") || !strcmp(aTopic
, "profile-after-change"))) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "Unexpected observer topic", "!strcmp(aTopic, \"profile-do-change\") || !strcmp(aTopic, \"profile-after-change\")"
, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 537); MOZ_PretendNoReturn(); } } while (0)
;
538
539 LoadDictionaryList(false);
540
541 return NS_OK;
542}
543
544NS_IMETHODIMPnsresult mozHunspell::AddDirectory(nsIFile* aDir) {
545 mDynamicDirectories.AppendObject(aDir);
546 LoadDictionaryList(true);
547 return NS_OK;
548}
549
550NS_IMETHODIMPnsresult mozHunspell::RemoveDirectory(nsIFile* aDir) {
551 mDynamicDirectories.RemoveObject(aDir);
552 LoadDictionaryList(true);
553
554#ifdef MOZ_THUNDERBIRD
555 /*
556 * This notification is needed for Thunderbird. Thunderbird derives the
557 * dictionary from the document's "lang" attribute. If a dictionary is
558 * removed, we need to change the "lang" attribute.
559 */
560 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
561 if (obs) {
562 obs->NotifyObservers(nullptr, SPELLCHECK_DICTIONARY_REMOVE_NOTIFICATION"spellcheck-dictionary-remove",
563 nullptr);
564 }
565#endif
566 return NS_OK;
567}
568
569NS_IMETHODIMPnsresult mozHunspell::AddDictionary(const nsAString& aLang,
570 nsIURI* aFile) {
571 NS_ENSURE_TRUE(aFile, NS_ERROR_INVALID_ARG)do { if ((__builtin_expect(!!(!(aFile)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aFile" ") failed", nullptr
, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 571); return NS_ERROR_INVALID_ARG; } } while (false)
;
572
573 mDynamicDictionaries.InsertOrUpdate(aLang, aFile);
574 mDictionaries.InsertOrUpdate(aLang, aFile);
575 DictionariesChanged(true);
576 return NS_OK;
577}
578
579NS_IMETHODIMPnsresult mozHunspell::RemoveDictionary(const nsAString& aLang,
580 nsIURI* aFile, bool* aRetVal) {
581 NS_ENSURE_TRUE(aFile, NS_ERROR_INVALID_ARG)do { if ((__builtin_expect(!!(!(aFile)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aFile" ") failed", nullptr
, "./../../../../../extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 581); return NS_ERROR_INVALID_ARG; } } while (false)
;
582 *aRetVal = false;
583
584 nsCOMPtr<nsIURI> file = mDynamicDictionaries.Get(aLang);
585 bool equal;
586 if (file && NS_SUCCEEDED(file->Equals(aFile, &equal))((bool)(__builtin_expect(!!(!NS_FAILED_impl(file->Equals(aFile
, &equal))), 1)))
&& equal) {
587 mDynamicDictionaries.Remove(aLang);
588 LoadDictionaryList(true);
589 *aRetVal = true;
590 }
591 return NS_OK;
592}