Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp
Warning:line 246, 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 -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=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/extensions/spellcheck/hunspell/glue -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/extensions/spellcheck/hunspell/glue -resource-dir /usr/lib/llvm-20/lib/clang/20 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/mozilla-config.h -include hunspell_alloc_hooks.h -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D _GLIBCXX_ASSERTIONS -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 /var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/extensions/spellcheck/hunspell/glue -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/security/rlbox -I /var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/src -I /var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/src -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /var/lib/jenkins/workspace/firefox-scan-build/ipc/chromium/src -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-20/lib/clang/20/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -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-deprecated-this-capture -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 -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 -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-01-20-090804-167946-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 <stdlib.h>
78#include <tuple>
79
80using namespace mozilla;
81
82NS_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" ")", "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 82); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0"
") (" "illegal refcnt" ")"); do { *((volatile int*)__null) =
82; __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
; }
83NS_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" ")", "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 83); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0"
") (" "dup release" ")"); do { *((volatile int*)__null) = 83
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); _mOwningThread.AssertOwnership("mozHunspell" " not thread-safe"
); nsISupports* base = mozHunspell::cycleCollection::Upcast(this
); nsrefcnt count = mRefCnt.decr(base); NS_LogRelease((this),
(count), ("mozHunspell")); return count; } void mozHunspell::
DeleteCycleCollectable(void) { delete (this); }
84
85NS_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", "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 85); MOZ_PretendNoReturn(); } } while (0); nsISupports* foundInterface
;
86 NS_INTERFACE_MAP_ENTRY(mozISpellCheckingEngine)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, mozISpellCheckingEngine>)) foundInterface
= static_cast<mozISpellCheckingEngine*>(this); else
87 NS_INTERFACE_MAP_ENTRY(nsIObserver)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsIObserver>)) foundInterface = static_cast
<nsIObserver*>(this); else
88 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsISupportsWeakReference>)) foundInterface
= static_cast<nsISupportsWeakReference*>(this); else
89 NS_INTERFACE_MAP_ENTRY(nsIMemoryReporter)if (aIID.Equals(mozilla::detail::kImplementedIID<std::remove_reference_t
<decltype(*this)>, nsIMemoryReporter>)) foundInterface
= static_cast<nsIMemoryReporter*>(this); else
90 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
91 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozHunspell)if (TopThreeWordsEquals( aIID, (nsXPCOMCycleCollectionParticipant
::COMTypeInfo<nsXPCOMCycleCollectionParticipant, void>::
kIID), (nsCycleCollectionISupports::COMTypeInfo<nsCycleCollectionISupports
, void>::kIID)) && (LowWordEquals(aIID, (nsXPCOMCycleCollectionParticipant
::COMTypeInfo<nsXPCOMCycleCollectionParticipant, void>::
kIID)) || LowWordEquals(aIID, (nsCycleCollectionISupports::COMTypeInfo
<nsCycleCollectionISupports, void>::kIID)))) { if (LowWordEquals
(aIID, (nsXPCOMCycleCollectionParticipant::COMTypeInfo<nsXPCOMCycleCollectionParticipant
, void>::kIID))) { *aInstancePtr = mozHunspell::cycleCollection
::GetParticipant(); return NS_OK; } if (LowWordEquals(aIID, (
nsCycleCollectionISupports::COMTypeInfo<nsCycleCollectionISupports
, void>::kIID))) { *aInstancePtr = mozHunspell::cycleCollection
::Upcast(this); return NS_OK; } foundInterface = nullptr; } else
92NS_INTERFACE_MAP_ENDfoundInterface = 0; nsresult status; if (!foundInterface) { do
{ static_assert( mozilla::detail::AssertionConditionType<
decltype(!aIID.Equals((nsISupports::COMTypeInfo<nsISupports
, void>::kIID)))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aIID.Equals((nsISupports::COMTypeInfo
<nsISupports, void>::kIID))))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("!aIID.Equals((nsISupports::COMTypeInfo<nsISupports, void>::kIID))"
, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 92); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aIID.Equals((nsISupports::COMTypeInfo<nsISupports, void>::kIID))"
")"); do { *((volatile int*)__null) = 92; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); status = NS_NOINTERFACE
; } else { (foundInterface)->AddRef(); status = NS_OK; } *
aInstancePtr = foundInterface; return status; }
93
94NS_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; }
95
96NS_IMPL_COMPONENT_FACTORY(mozHunspell)template <> already_AddRefed<nsISupports> mozCreateComponent
<mozHunspell>()
{
97 auto hunspell = MakeRefPtr<mozHunspell>();
98 if (NS_SUCCEEDED(hunspell->Init())((bool)(__builtin_expect(!!(!NS_FAILED_impl(hunspell->Init
())), 1)))
) {
99 return hunspell.forget().downcast<mozISpellCheckingEngine>();
100 }
101 return nullptr;
102}
103
104mozHunspell::mozHunspell() {
105#ifdef DEBUG1
106 // There must be only one instance of this class: it reports memory based on
107 // a single static count in HunspellAllocator.
108 static bool hasRun = false;
109 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", "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 109); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!hasRun" ")"
); do { *((volatile int*)__null) = 109; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
110 hasRun = true;
111#endif
112}
113
114nsresult mozHunspell::Init() {
115 LoadDictionaryList(false);
116
117 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
118 if (obs) {
119 obs->AddObserver(this, "profile-do-change", true);
120 obs->AddObserver(this, "profile-after-change", true);
121 }
122
123 mozilla::RegisterWeakMemoryReporter(this);
124
125 return NS_OK;
126}
127
128mozHunspell::~mozHunspell() {
129 mozilla::UnregisterWeakMemoryReporter(this);
130
131 mPersonalDictionary = nullptr;
132 mHunspells.Clear();
133}
134
135NS_IMETHODIMPnsresult
136mozHunspell::GetDictionaries(nsTArray<nsCString>& aDictionaries) {
137 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aDictionaries.IsEmpty()"
")"); do { *((volatile int*)__null) = 137; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
138 for (auto iter = mHunspells.ConstIter(); !iter.Done(); iter.Next()) {
139 if (iter.Data().mEnabled) {
140 aDictionaries.AppendElement(iter.Key());
141 }
142 }
143 return NS_OK;
144}
145
146/* Set the Dictionaries.
147 * This also Loads the dictionaries and initializes the converter using the
148 * dictionaries converter
149 */
150NS_IMETHODIMPnsresult
151mozHunspell::SetDictionaries(const nsTArray<nsCString>& aDictionaries) {
152 if (aDictionaries.IsEmpty()) {
153 mHunspells.Clear();
154 return NS_OK;
155 }
156
157 // Disable any dictionaries we've already loaded that we're not
158 // going to use.
159 for (auto iter = mHunspells.Iter(); !iter.Done(); iter.Next()) {
160 if (!aDictionaries.Contains(iter.Key())) {
161 iter.Data().mEnabled = false;
162 }
163 }
164
165 bool firstDictionary = true;
166 for (const auto& dictionary : aDictionaries) {
167 NS_ConvertUTF8toUTF16 dict(dictionary);
168 nsIURI* affFile = mDictionaries.GetWeak(dict);
169 if (!affFile) {
170 return NS_ERROR_FILE_NOT_FOUND;
171 }
172
173 nsAutoCString affFileName;
174 nsresult rv = affFile->GetSpec(affFileName);
175 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, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 175); return rv; } } while (false)
;
176
177 if (auto entry = mHunspells.Lookup(dictionary)) {
178 if (entry.Data().mAffixFileName == affFileName) {
179 entry.Data().mEnabled = true;
180 continue;
181 }
182 }
183
184 DictionaryData dictionaryData;
185 dictionaryData.mAffixFileName = affFileName;
186
187 // Load the first dictionary now, we'll load the others lazily during
188 // checking.
189 if (firstDictionary) {
190 rv = dictionaryData.LoadIfNecessary();
191 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, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 191); return rv; } } while (false)
;
192 firstDictionary = false;
193 }
194
195 mHunspells.InsertOrUpdate(dictionary, std::move(dictionaryData));
196 }
197
198 // If we have a large number of dictionaries loaded, try freeing any disabled
199 // dictionaries to limit memory use.
200 if (mHunspells.Count() > 10) {
201 mHunspells.RemoveIf([](const auto& iter) { return !iter.Data().mEnabled; });
202 }
203
204 return NS_OK;
205}
206
207NS_IMETHODIMPnsresult mozHunspell::GetPersonalDictionary(
208 mozIPersonalDictionary** aPersonalDictionary) {
209 *aPersonalDictionary = mPersonalDictionary;
210 NS_IF_ADDREF(*aPersonalDictionary)ns_if_addref(*aPersonalDictionary);
211 return NS_OK;
212}
213
214NS_IMETHODIMPnsresult mozHunspell::SetPersonalDictionary(
215 mozIPersonalDictionary* aPersonalDictionary) {
216 mPersonalDictionary = aPersonalDictionary;
217 return NS_OK;
218}
219
220NS_IMETHODIMPnsresult mozHunspell::GetDictionaryList(
221 nsTArray<nsCString>& aDictionaries) {
222 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 222); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aDictionaries.IsEmpty()"
")"); do { *((volatile int*)__null) = 222; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
223 for (const auto& key : mDictionaries.Keys()) {
224 aDictionaries.AppendElement(NS_ConvertUTF16toUTF8(key));
225 }
226
227 return NS_OK;
228}
229
230void mozHunspell::LoadDictionaryList(bool aNotifyChildProcesses) {
231 mDictionaries.Clear();
232
233 nsresult rv;
234
235 // find built in dictionaries, or dictionaries specified in
236 // spellchecker.dictionary_path in prefs
237 nsCOMPtr<nsIFile> dictDir;
238
239 // check preferences first
240 nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID"@mozilla.org/preferences-service;1"));
241 if (prefs) {
242 nsAutoCString extDictPath;
243 rv = prefs->GetCharPref("spellchecker.dictionary_path", extDictPath);
244 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) {
245 // set the spellchecker.dictionary_path
246 rv = NS_NewNativeLocalFile(extDictPath, getter_AddRefs(dictDir));
Value stored to 'rv' is never read
247 }
248 if (dictDir) {
249 LoadDictionariesFromDir(dictDir);
250 }
251 }
252
253 // find dictionaries in DICPATH
254 char* dicEnv = PR_GetEnv("DICPATH");
255 if (dicEnv) {
256 // do a two-pass dance so dictionaries are loaded right-to-left as
257 // preference
258 nsTArray<nsCOMPtr<nsIFile>> dirs;
259 nsAutoCString env(dicEnv); // assume dicEnv is UTF-8
260
261 char* currPath = nullptr;
262 char* nextPaths = env.BeginWriting();
263 while ((currPath = NS_strtok(":", &nextPaths))) {
264 nsCOMPtr<nsIFile> dir;
265 rv = NS_NewNativeLocalFile(nsCString(currPath), getter_AddRefs(dir));
266 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) {
267 dirs.AppendElement(dir);
268 }
269 }
270
271 // load them in reverse order so they override each other properly
272 for (int32_t i = dirs.Length() - 1; i >= 0; i--) {
273 LoadDictionariesFromDir(dirs[i]);
274 }
275 }
276
277 // find dictionaries from restartless extensions
278 for (int32_t i = 0; i < mDynamicDirectories.Count(); i++) {
279 LoadDictionariesFromDir(mDynamicDirectories[i]);
280 }
281
282 for (const auto& dictionaryEntry : mDynamicDictionaries) {
283 mDictionaries.InsertOrUpdate(dictionaryEntry.GetKey(),
284 dictionaryEntry.GetData());
285 }
286
287 DictionariesChanged(aNotifyChildProcesses);
288}
289
290void mozHunspell::DictionariesChanged(bool aNotifyChildProcesses) {
291 // Now we have finished updating the list of dictionaries, update the current
292 // dictionary and any editors which may use it.
293 mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
294
295 if (aNotifyChildProcesses) {
296 mozilla::dom::ContentParent_NotifyUpdatedDictionaries();
297 }
298
299 // Check if the current dictionaries are still available.
300 // If not, try to replace it with other dictionaries of the same language.
301 if (!mHunspells.IsEmpty()) {
302 nsTArray<nsCString> dictionaries;
303 for (auto iter = mHunspells.ConstIter(); !iter.Done(); iter.Next()) {
304 if (iter.Data().mEnabled) {
305 dictionaries.AppendElement(iter.Key());
306 }
307 }
308 nsresult rv = SetDictionaries(dictionaries);
309 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) return;
310 }
311
312 // If the current dictionaries are gone, and we don't have a good replacement,
313 // set no current dictionary.
314 if (!mHunspells.IsEmpty()) {
315 nsTArray<nsCString> empty;
316 SetDictionaries(empty);
317 }
318}
319
320NS_IMETHODIMPnsresult
321mozHunspell::LoadDictionariesFromDir(nsIFile* aDir) {
322 nsresult rv;
323
324 bool check = false;
325 rv = aDir->Exists(&check);
326 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) || !check) return NS_ERROR_UNEXPECTED;
327
328 rv = aDir->IsDirectory(&check);
329 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) || !check) return NS_ERROR_UNEXPECTED;
330
331 nsCOMPtr<nsIDirectoryEnumerator> files;
332 rv = aDir->GetDirectoryEntries(getter_AddRefs(files));
333 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) return NS_ERROR_UNEXPECTED;
334
335 nsCOMPtr<nsIFile> file;
336 while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file)))((bool)(__builtin_expect(!!(!NS_FAILED_impl(files->GetNextFile
(getter_AddRefs(file)))), 1)))
&& file) {
337 nsAutoString leafName;
338 file->GetLeafName(leafName);
339 if (!StringEndsWith(leafName, u".dic"_ns)) continue;
340
341 nsAutoString dict(leafName);
342 dict.SetLength(dict.Length() - 4); // magic length of ".dic"
343
344 // check for the presence of the .aff file
345 leafName = dict;
346 leafName.AppendLiteral(".aff");
347 file->SetLeafName(leafName);
348 rv = file->Exists(&check);
349 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) || !check) continue;
350
351 // Replace '_' separator with '-'
352 dict.ReplaceChar('_', '-');
353
354 nsCOMPtr<nsIURI> uri;
355 rv = NS_NewFileURI(getter_AddRefs(uri), file);
356 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, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 356); return rv; } } while (false)
;
357
358 mDictionaries.InsertOrUpdate(dict, uri);
359 }
360
361 return NS_OK;
362}
363
364nsresult mozHunspell::DictionaryData::ConvertCharset(const nsAString& aStr,
365 std::string& aDst) {
366 if (NS_WARN_IF(!mEncoder)NS_warn_if_impl(!mEncoder, "!mEncoder", "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 366)
) {
367 return NS_ERROR_NOT_INITIALIZED;
368 }
369
370 auto src = Span(aStr.BeginReading(), aStr.Length());
371 CheckedInt<size_t> needed =
372 mEncoder->MaxBufferLengthFromUTF16WithoutReplacement(src.Length());
373 if (!needed.isValid()) {
374 return NS_ERROR_OUT_OF_MEMORY;
375 }
376
377 aDst.resize(needed.value());
378
379 char* dstPtr = &aDst[0];
380 auto dst = Span(reinterpret_cast<uint8_t*>(dstPtr), needed.value());
381
382 uint32_t result;
383 size_t written;
384 std::tie(result, std::ignore, written) =
385 mEncoder->EncodeFromUTF16WithoutReplacement(src, dst, true);
386 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 386); AnnotateMozCrashReason("MOZ_ASSERT" "(" "result != kOutputFull"
")"); do { *((volatile int*)__null) = 386; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
387 if (result != kInputEmpty) {
388 return NS_ERROR_UENC_NOMAPPING;
389 }
390 aDst.resize(written);
391 mEncoder->Encoding()->NewEncoderInto(*mEncoder);
392 return NS_OK;
393}
394
395nsresult mozHunspell::DictionaryData::LoadIfNecessary() {
396 if (mHunspell && mEncoder && mDecoder) {
397 return NS_OK;
398 }
399
400 if (mLoadFailed) {
401 return NS_ERROR_FAILURE;
402 }
403
404 nsCString dictFileName = mAffixFileName;
405 int32_t dotPos = dictFileName.RFindChar('.');
406 if (dotPos == -1) {
407 mLoadFailed = true;
408 return NS_ERROR_FAILURE;
409 }
410 dictFileName.SetLength(dotPos);
411 dictFileName.AppendLiteral(".dic");
412
413 UniquePtr<RLBoxHunspell> hunspell(
414 RLBoxHunspell::Create(mAffixFileName, dictFileName));
415 if (!hunspell) {
416 mLoadFailed = true;
417 // TODO Bug 1788857: Verify error propagation in case of inaccessible file
418 return NS_ERROR_OUT_OF_MEMORY;
419 }
420 mHunspell = std::move(hunspell);
421 auto encoding =
422 Encoding::ForLabelNoReplacement(mHunspell->get_dict_encoding());
423 if (!encoding) {
424 mLoadFailed = true;
425 return NS_ERROR_UCONV_NOCONV;
426 }
427 mEncoder = encoding->NewEncoder();
428 mDecoder = encoding->NewDecoderWithoutBOMHandling();
429 return NS_OK;
430}
431
432NS_IMETHODIMPnsresult
433mozHunspell::CollectReports(nsIHandleReportCallback* aHandleReport,
434 nsISupports* aData, bool aAnonymize) {
435 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)
436 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)
437 "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)
;
438
439 return NS_OK;
440}
441
442NS_IMETHODIMPnsresult
443mozHunspell::Check(const nsAString& aWord, bool* aResult) {
444 if (NS_WARN_IF(!aResult)NS_warn_if_impl(!aResult, "!aResult", "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 444)
) {
445 return NS_ERROR_INVALID_ARG;
446 }
447
448 if (NS_WARN_IF(mHunspells.IsEmpty())NS_warn_if_impl(mHunspells.IsEmpty(), "mHunspells.IsEmpty()",
"/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 448)
) {
449 return NS_ERROR_FAILURE;
450 }
451
452 *aResult = true;
453 for (auto iter = mHunspells.Iter(); !iter.Done(); iter.Next()) {
454 if (!iter.Data().mEnabled) {
455 continue;
456 }
457
458 nsresult rv = iter.Data().LoadIfNecessary();
459 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
460 continue;
461 }
462
463 std::string charsetWord;
464 rv = iter.Data().ConvertCharset(aWord, charsetWord);
465 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
466 continue;
467 }
468
469 // Depending upon the encoding, we might end up with a string that begins
470 // with the null byte. Since the hunspell interface uses C-style strings,
471 // this appears like an empty string, and hunspell marks empty strings as
472 // spelled correctly. Skip these cases to allow another dictionary to have
473 // the chance to spellcheck them.
474 if (charsetWord.empty() || charsetWord[0] == 0) {
475 continue;
476 }
477
478 *aResult = iter.Data().mHunspell->spell(charsetWord);
479 if (*aResult) {
480 break;
481 }
482 }
483
484 if (!*aResult && mPersonalDictionary) {
485 return mPersonalDictionary->Check(aWord, aResult);
486 }
487
488 return NS_OK;
489}
490
491NS_IMETHODIMPnsresult
492mozHunspell::Suggest(const nsAString& aWord, nsTArray<nsString>& aSuggestions) {
493 if (NS_WARN_IF(mHunspells.IsEmpty())NS_warn_if_impl(mHunspells.IsEmpty(), "mHunspells.IsEmpty()",
"/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 493)
) {
494 return NS_ERROR_FAILURE;
495 }
496
497 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 497); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSuggestions.IsEmpty()"
")"); do { *((volatile int*)__null) = 497; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
498
499 for (auto iter = mHunspells.Iter(); !iter.Done(); iter.Next()) {
500 if (!iter.Data().mEnabled) {
501 continue;
502 }
503
504 nsresult rv = iter.Data().LoadIfNecessary();
505 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
506 continue;
507 }
508
509 std::string charsetWord;
510 rv = iter.Data().ConvertCharset(aWord, charsetWord);
511 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, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 511); return rv; } } while (false)
;
512
513 std::vector<std::string> suggestions =
514 iter.Data().mHunspell->suggest(charsetWord);
515 if (!suggestions.empty()) {
516 aSuggestions.SetCapacity(aSuggestions.Length() + suggestions.size());
517 for (Span<const char> charSrc : suggestions) {
518 // Convert the suggestion to utf16
519 auto src = AsBytes(charSrc);
520 nsresult rv =
521 iter.Data().mDecoder->Encoding()->DecodeWithoutBOMHandling(
522 src, *aSuggestions.AppendElement());
523 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, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 523); return rv; } } while (false)
;
524 iter.Data().mDecoder->Encoding()->NewDecoderWithoutBOMHandlingInto(
525 *iter.Data().mDecoder);
526 }
527 }
528 }
529
530 return NS_OK;
531}
532
533NS_IMETHODIMPnsresult
534mozHunspell::Observe(nsISupports* aSubj, const char* aTopic,
535 const char16_t* aData) {
536 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\")"
, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 538); MOZ_PretendNoReturn(); } } while (0)
537 !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\")"
, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 538); MOZ_PretendNoReturn(); } } while (0)
538 "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\")"
, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 538); MOZ_PretendNoReturn(); } } while (0)
;
539
540 LoadDictionaryList(false);
541
542 return NS_OK;
543}
544
545NS_IMETHODIMPnsresult mozHunspell::AddDirectory(nsIFile* aDir) {
546 mDynamicDirectories.AppendObject(aDir);
547 LoadDictionaryList(true);
548 return NS_OK;
549}
550
551NS_IMETHODIMPnsresult mozHunspell::RemoveDirectory(nsIFile* aDir) {
552 mDynamicDirectories.RemoveObject(aDir);
553 LoadDictionaryList(true);
554
555#ifdef MOZ_THUNDERBIRD
556 /*
557 * This notification is needed for Thunderbird. Thunderbird derives the
558 * dictionary from the document's "lang" attribute. If a dictionary is
559 * removed, we need to change the "lang" attribute.
560 */
561 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
562 if (obs) {
563 obs->NotifyObservers(nullptr, SPELLCHECK_DICTIONARY_REMOVE_NOTIFICATION"spellcheck-dictionary-remove",
564 nullptr);
565 }
566#endif
567 return NS_OK;
568}
569
570NS_IMETHODIMPnsresult mozHunspell::AddDictionary(const nsAString& aLang,
571 nsIURI* aFile) {
572 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
, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 572); return NS_ERROR_INVALID_ARG; } } while (false)
;
573
574 mDynamicDictionaries.InsertOrUpdate(aLang, aFile);
575 mDictionaries.InsertOrUpdate(aLang, aFile);
576 DictionariesChanged(true);
577 return NS_OK;
578}
579
580NS_IMETHODIMPnsresult mozHunspell::RemoveDictionary(const nsAString& aLang,
581 nsIURI* aFile, bool* aRetVal) {
582 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
, "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 582); return NS_ERROR_INVALID_ARG; } } while (false)
;
583 *aRetVal = false;
584
585 nsCOMPtr<nsIURI> file = mDynamicDictionaries.Get(aLang);
586 bool equal;
587 if (file && NS_SUCCEEDED(file->Equals(aFile, &equal))((bool)(__builtin_expect(!!(!NS_FAILED_impl(file->Equals(aFile
, &equal))), 1)))
&& equal) {
588 mDynamicDictionaries.Remove(aLang);
589 LoadDictionaryList(true);
590 *aRetVal = true;
591 }
592 return NS_OK;
593}