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-19/lib/clang/19 -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 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-19/lib/clang/19/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-2024-09-22-115206-3586786-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, true, 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 =
266 NS_NewNativeLocalFile(nsCString(currPath), true, getter_AddRefs(dir));
267 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) {
268 dirs.AppendElement(dir);
269 }
270 }
271
272 // load them in reverse order so they override each other properly
273 for (int32_t i = dirs.Length() - 1; i >= 0; i--) {
274 LoadDictionariesFromDir(dirs[i]);
275 }
276 }
277
278 // find dictionaries from restartless extensions
279 for (int32_t i = 0; i < mDynamicDirectories.Count(); i++) {
280 LoadDictionariesFromDir(mDynamicDirectories[i]);
281 }
282
283 for (const auto& dictionaryEntry : mDynamicDictionaries) {
284 mDictionaries.InsertOrUpdate(dictionaryEntry.GetKey(),
285 dictionaryEntry.GetData());
286 }
287
288 DictionariesChanged(aNotifyChildProcesses);
289}
290
291void mozHunspell::DictionariesChanged(bool aNotifyChildProcesses) {
292 // Now we have finished updating the list of dictionaries, update the current
293 // dictionary and any editors which may use it.
294 mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
295
296 if (aNotifyChildProcesses) {
297 mozilla::dom::ContentParent_NotifyUpdatedDictionaries();
298 }
299
300 // Check if the current dictionaries are still available.
301 // If not, try to replace it with other dictionaries of the same language.
302 if (!mHunspells.IsEmpty()) {
303 nsTArray<nsCString> dictionaries;
304 for (auto iter = mHunspells.ConstIter(); !iter.Done(); iter.Next()) {
305 if (iter.Data().mEnabled) {
306 dictionaries.AppendElement(iter.Key());
307 }
308 }
309 nsresult rv = SetDictionaries(dictionaries);
310 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) return;
311 }
312
313 // If the current dictionaries are gone, and we don't have a good replacement,
314 // set no current dictionary.
315 if (!mHunspells.IsEmpty()) {
316 nsTArray<nsCString> empty;
317 SetDictionaries(empty);
318 }
319}
320
321NS_IMETHODIMPnsresult
322mozHunspell::LoadDictionariesFromDir(nsIFile* aDir) {
323 nsresult rv;
324
325 bool check = false;
326 rv = aDir->Exists(&check);
327 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) || !check) return NS_ERROR_UNEXPECTED;
328
329 rv = aDir->IsDirectory(&check);
330 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) || !check) return NS_ERROR_UNEXPECTED;
331
332 nsCOMPtr<nsIDirectoryEnumerator> files;
333 rv = aDir->GetDirectoryEntries(getter_AddRefs(files));
334 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) return NS_ERROR_UNEXPECTED;
335
336 nsCOMPtr<nsIFile> file;
337 while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file)))((bool)(__builtin_expect(!!(!NS_FAILED_impl(files->GetNextFile
(getter_AddRefs(file)))), 1)))
&& file) {
338 nsAutoString leafName;
339 file->GetLeafName(leafName);
340 if (!StringEndsWith(leafName, u".dic"_ns)) continue;
341
342 nsAutoString dict(leafName);
343 dict.SetLength(dict.Length() - 4); // magic length of ".dic"
344
345 // check for the presence of the .aff file
346 leafName = dict;
347 leafName.AppendLiteral(".aff");
348 file->SetLeafName(leafName);
349 rv = file->Exists(&check);
350 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) || !check) continue;
351
352 // Replace '_' separator with '-'
353 dict.ReplaceChar('_', '-');
354
355 nsCOMPtr<nsIURI> uri;
356 rv = NS_NewFileURI(getter_AddRefs(uri), file);
357 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"
, 357); return rv; } } while (false)
;
358
359 mDictionaries.InsertOrUpdate(dict, uri);
360 }
361
362 return NS_OK;
363}
364
365nsresult mozHunspell::DictionaryData::ConvertCharset(const nsAString& aStr,
366 std::string& aDst) {
367 if (NS_WARN_IF(!mEncoder)NS_warn_if_impl(!mEncoder, "!mEncoder", "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 367)
) {
368 return NS_ERROR_NOT_INITIALIZED;
369 }
370
371 auto src = Span(aStr.BeginReading(), aStr.Length());
372 CheckedInt<size_t> needed =
373 mEncoder->MaxBufferLengthFromUTF16WithoutReplacement(src.Length());
374 if (!needed.isValid()) {
375 return NS_ERROR_OUT_OF_MEMORY;
376 }
377
378 aDst.resize(needed.value());
379
380 char* dstPtr = &aDst[0];
381 auto dst = Span(reinterpret_cast<uint8_t*>(dstPtr), needed.value());
382
383 uint32_t result;
384 size_t written;
385 std::tie(result, std::ignore, written) =
386 mEncoder->EncodeFromUTF16WithoutReplacement(src, dst, true);
387 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"
, 387); AnnotateMozCrashReason("MOZ_ASSERT" "(" "result != kOutputFull"
")"); do { *((volatile int*)__null) = 387; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
388 if (result != kInputEmpty) {
389 return NS_ERROR_UENC_NOMAPPING;
390 }
391 aDst.resize(written);
392 mEncoder->Encoding()->NewEncoderInto(*mEncoder);
393 return NS_OK;
394}
395
396nsresult mozHunspell::DictionaryData::LoadIfNecessary() {
397 if (mHunspell && mEncoder && mDecoder) {
398 return NS_OK;
399 }
400
401 if (mLoadFailed) {
402 return NS_ERROR_FAILURE;
403 }
404
405 nsCString dictFileName = mAffixFileName;
406 int32_t dotPos = dictFileName.RFindChar('.');
407 if (dotPos == -1) {
408 mLoadFailed = true;
409 return NS_ERROR_FAILURE;
410 }
411 dictFileName.SetLength(dotPos);
412 dictFileName.AppendLiteral(".dic");
413
414 UniquePtr<RLBoxHunspell> hunspell(
415 RLBoxHunspell::Create(mAffixFileName, dictFileName));
416 if (!hunspell) {
417 mLoadFailed = true;
418 // TODO Bug 1788857: Verify error propagation in case of inaccessible file
419 return NS_ERROR_OUT_OF_MEMORY;
420 }
421 mHunspell = std::move(hunspell);
422 auto encoding =
423 Encoding::ForLabelNoReplacement(mHunspell->get_dict_encoding());
424 if (!encoding) {
425 mLoadFailed = true;
426 return NS_ERROR_UCONV_NOCONV;
427 }
428 mEncoder = encoding->NewEncoder();
429 mDecoder = encoding->NewDecoderWithoutBOMHandling();
430 return NS_OK;
431}
432
433NS_IMETHODIMPnsresult
434mozHunspell::CollectReports(nsIHandleReportCallback* aHandleReport,
435 nsISupports* aData, bool aAnonymize) {
436 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)
437 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)
438 "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)
;
439
440 return NS_OK;
441}
442
443NS_IMETHODIMPnsresult
444mozHunspell::Check(const nsAString& aWord, bool* aResult) {
445 if (NS_WARN_IF(!aResult)NS_warn_if_impl(!aResult, "!aResult", "/var/lib/jenkins/workspace/firefox-scan-build/extensions/spellcheck/hunspell/glue/mozHunspell.cpp"
, 445)
) {
446 return NS_ERROR_INVALID_ARG;
447 }
448
449 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"
, 449)
) {
450 return NS_ERROR_FAILURE;
451 }
452
453 *aResult = true;
454 for (auto iter = mHunspells.Iter(); !iter.Done(); iter.Next()) {
455 if (!iter.Data().mEnabled) {
456 continue;
457 }
458
459 nsresult rv = iter.Data().LoadIfNecessary();
460 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
461 continue;
462 }
463
464 std::string charsetWord;
465 rv = iter.Data().ConvertCharset(aWord, charsetWord);
466 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
467 continue;
468 }
469
470 // Depending upon the encoding, we might end up with a string that begins
471 // with the null byte. Since the hunspell interface uses C-style strings,
472 // this appears like an empty string, and hunspell marks empty strings as
473 // spelled correctly. Skip these cases to allow another dictionary to have
474 // the chance to spellcheck them.
475 if (charsetWord.empty() || charsetWord[0] == 0) {
476 continue;
477 }
478
479 *aResult = iter.Data().mHunspell->spell(charsetWord);
480 if (*aResult) {
481 break;
482 }
483 }
484
485 if (!*aResult && mPersonalDictionary) {
486 return mPersonalDictionary->Check(aWord, aResult);
487 }
488
489 return NS_OK;
490}
491
492NS_IMETHODIMPnsresult
493mozHunspell::Suggest(const nsAString& aWord, nsTArray<nsString>& aSuggestions) {
494 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"
, 494)
) {
495 return NS_ERROR_FAILURE;
496 }
497
498 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"
, 498); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSuggestions.IsEmpty()"
")"); do { *((volatile int*)__null) = 498; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
499
500 for (auto iter = mHunspells.Iter(); !iter.Done(); iter.Next()) {
501 if (!iter.Data().mEnabled) {
502 continue;
503 }
504
505 nsresult rv = iter.Data().LoadIfNecessary();
506 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
507 continue;
508 }
509
510 std::string charsetWord;
511 rv = iter.Data().ConvertCharset(aWord, charsetWord);
512 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"
, 512); return rv; } } while (false)
;
513
514 std::vector<std::string> suggestions =
515 iter.Data().mHunspell->suggest(charsetWord);
516 if (!suggestions.empty()) {
517 aSuggestions.SetCapacity(aSuggestions.Length() + suggestions.size());
518 for (Span<const char> charSrc : suggestions) {
519 // Convert the suggestion to utf16
520 auto src = AsBytes(charSrc);
521 nsresult rv =
522 iter.Data().mDecoder->Encoding()->DecodeWithoutBOMHandling(
523 src, *aSuggestions.AppendElement());
524 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"
, 524); return rv; } } while (false)
;
525 iter.Data().mDecoder->Encoding()->NewDecoderWithoutBOMHandlingInto(
526 *iter.Data().mDecoder);
527 }
528 }
529 }
530
531 return NS_OK;
532}
533
534NS_IMETHODIMPnsresult
535mozHunspell::Observe(nsISupports* aSubj, const char* aTopic,
536 const char16_t* aData) {
537 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"
, 539); MOZ_PretendNoReturn(); } } while (0)
538 !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"
, 539); MOZ_PretendNoReturn(); } } while (0)
539 "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"
, 539); MOZ_PretendNoReturn(); } } while (0)
;
540
541 LoadDictionaryList(false);
542
543 return NS_OK;
544}
545
546NS_IMETHODIMPnsresult mozHunspell::AddDirectory(nsIFile* aDir) {
547 mDynamicDirectories.AppendObject(aDir);
548 LoadDictionaryList(true);
549 return NS_OK;
550}
551
552NS_IMETHODIMPnsresult mozHunspell::RemoveDirectory(nsIFile* aDir) {
553 mDynamicDirectories.RemoveObject(aDir);
554 LoadDictionaryList(true);
555
556#ifdef MOZ_THUNDERBIRD
557 /*
558 * This notification is needed for Thunderbird. Thunderbird derives the
559 * dictionary from the document's "lang" attribute. If a dictionary is
560 * removed, we need to change the "lang" attribute.
561 */
562 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
563 if (obs) {
564 obs->NotifyObservers(nullptr, SPELLCHECK_DICTIONARY_REMOVE_NOTIFICATION"spellcheck-dictionary-remove",
565 nullptr);
566 }
567#endif
568 return NS_OK;
569}
570
571NS_IMETHODIMPnsresult mozHunspell::AddDictionary(const nsAString& aLang,
572 nsIURI* aFile) {
573 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"
, 573); return NS_ERROR_INVALID_ARG; } } while (false)
;
574
575 mDynamicDictionaries.InsertOrUpdate(aLang, aFile);
576 mDictionaries.InsertOrUpdate(aLang, aFile);
577 DictionariesChanged(true);
578 return NS_OK;
579}
580
581NS_IMETHODIMPnsresult mozHunspell::RemoveDictionary(const nsAString& aLang,
582 nsIURI* aFile, bool* aRetVal) {
583 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"
, 583); return NS_ERROR_INVALID_ARG; } } while (false)
;
584 *aRetVal = false;
585
586 nsCOMPtr<nsIURI> file = mDynamicDictionaries.Get(aLang);
587 bool equal;
588 if (file && NS_SUCCEEDED(file->Equals(aFile, &equal))((bool)(__builtin_expect(!!(!NS_FAILED_impl(file->Equals(aFile
, &equal))), 1)))
&& equal) {
589 mDynamicDictionaries.Remove(aLang);
590 LoadDictionaryList(true);
591 *aRetVal = true;
592 }
593 return NS_OK;
594}