diff options
Diffstat (limited to '')
17 files changed, 1424 insertions, 0 deletions
diff --git a/extensions/spellcheck/hunspell/glue/PRemoteSpellcheckEngine.ipdl b/extensions/spellcheck/hunspell/glue/PRemoteSpellcheckEngine.ipdl new file mode 100644 index 0000000000..aac3c661b9 --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/PRemoteSpellcheckEngine.ipdl @@ -0,0 +1,33 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PContent; + +namespace mozilla { + +sync protocol PRemoteSpellcheckEngine { + manager PContent; + +parent: + async __delete__(); + + async CheckAsync(nsString[] aWord) returns (bool[] aIsMisspelled); + + sync CheckAndSuggest(nsString aWord) returns (bool aIsMisspelled, nsString[] aSuggestions); + + sync SetDictionary(nsCString aDictionary) returns (bool success); + + /* + * Set current dictionary from list of dictionary name. + * + * @aList A list of dictionary name. If a string into this list is + * empty string, dictionary selection is reset + * @aSuccess true if setting dictionary is successful + * @aDictionary Return current dictionary name that set by this method. + */ + async SetDictionaryFromList(nsCString[] aList) + returns (bool aSuccess, nsCString aDictionary); +}; + +} // namespace mozilla diff --git a/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.cpp b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.cpp new file mode 100644 index 0000000000..d977565b8f --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.cpp @@ -0,0 +1,59 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/UniquePtr.h" +#include "RemoteSpellCheckEngineChild.h" + +namespace mozilla { + +RemoteSpellcheckEngineChild::RemoteSpellcheckEngineChild( + mozSpellChecker* aOwner) + : mOwner(aOwner) {} + +RemoteSpellcheckEngineChild::~RemoteSpellcheckEngineChild() { + // null out the owner's SpellcheckEngineChild to prevent state corruption + // during shutdown + mOwner->DeleteRemoteEngine(); +} + +RefPtr<GenericPromise> +RemoteSpellcheckEngineChild::SetCurrentDictionaryFromList( + const nsTArray<nsCString>& aList) { + RefPtr<mozSpellChecker> spellChecker = mOwner; + + return SendSetDictionaryFromList(aList)->Then( + GetMainThreadSerialEventTarget(), __func__, + [spellChecker](Tuple<bool, nsCString>&& aParam) { + if (!Get<0>(aParam)) { + spellChecker->mCurrentDictionary.Truncate(); + return GenericPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, + __func__); + } + spellChecker->mCurrentDictionary = std::move(Get<1>(aParam)); + return GenericPromise::CreateAndResolve(true, __func__); + }, + [spellChecker](ResponseRejectReason&& aReason) { + spellChecker->mCurrentDictionary.Truncate(); + return GenericPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, + __func__); + }); +} + +RefPtr<CheckWordPromise> RemoteSpellcheckEngineChild::CheckWords( + const nsTArray<nsString>& aWords) { + RefPtr<mozSpellChecker> kungFuDeathGrip = mOwner; + + return SendCheckAsync(aWords)->Then( + GetMainThreadSerialEventTarget(), __func__, + [kungFuDeathGrip](nsTArray<bool>&& aIsMisspelled) { + return CheckWordPromise::CreateAndResolve(std::move(aIsMisspelled), + __func__); + }, + [kungFuDeathGrip](mozilla::ipc::ResponseRejectReason&& aReason) { + return CheckWordPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, + __func__); + }); +} + +} // namespace mozilla diff --git a/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.h b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.h new file mode 100644 index 0000000000..b90a7e93a6 --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.h @@ -0,0 +1,33 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef RemoteSpellcheckEngineChild_h_ +#define RemoteSpellcheckEngineChild_h_ + +#include "mozilla/MozPromise.h" +#include "mozilla/PRemoteSpellcheckEngineChild.h" +#include "mozSpellChecker.h" + +class mozSpellChecker; + +namespace mozilla { + +class RemoteSpellcheckEngineChild + : public mozilla::PRemoteSpellcheckEngineChild { + public: + explicit RemoteSpellcheckEngineChild(mozSpellChecker* aOwner); + virtual ~RemoteSpellcheckEngineChild(); + + RefPtr<GenericPromise> SetCurrentDictionaryFromList( + const nsTArray<nsCString>& aList); + + RefPtr<CheckWordPromise> CheckWords(const nsTArray<nsString>& aWords); + + private: + mozSpellChecker* mOwner; +}; + +} // namespace mozilla + +#endif // RemoteSpellcheckEngineChild_h_ diff --git a/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineParent.cpp b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineParent.cpp new file mode 100644 index 0000000000..9e1d4da7b8 --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineParent.cpp @@ -0,0 +1,69 @@ +/* vim: set ts=2 sw=2 sts=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "RemoteSpellCheckEngineParent.h" +#include "mozilla/Unused.h" +#include "mozilla/mozSpellChecker.h" +#include "nsServiceManagerUtils.h" + +namespace mozilla { + +RemoteSpellcheckEngineParent::RemoteSpellcheckEngineParent() { + mSpellChecker = mozSpellChecker::Create(); +} + +RemoteSpellcheckEngineParent::~RemoteSpellcheckEngineParent() {} + +mozilla::ipc::IPCResult RemoteSpellcheckEngineParent::RecvSetDictionary( + const nsCString& aDictionary, bool* success) { + nsresult rv = mSpellChecker->SetCurrentDictionary(aDictionary); + *success = NS_SUCCEEDED(rv); + return IPC_OK(); +} + +mozilla::ipc::IPCResult RemoteSpellcheckEngineParent::RecvSetDictionaryFromList( + nsTArray<nsCString>&& aList, SetDictionaryFromListResolver&& aResolve) { + for (auto& dictionary : aList) { + nsresult rv = mSpellChecker->SetCurrentDictionary(dictionary); + if (NS_SUCCEEDED(rv)) { + aResolve(Tuple<const bool&, const nsCString&>(true, dictionary)); + return IPC_OK(); + } + } + aResolve(Tuple<const bool&, const nsCString&>(false, ""_ns)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult RemoteSpellcheckEngineParent::RecvCheckAsync( + nsTArray<nsString>&& aWords, CheckAsyncResolver&& aResolve) { + nsTArray<bool> misspells; + misspells.SetCapacity(aWords.Length()); + for (auto& word : aWords) { + bool misspelled; + nsresult rv = mSpellChecker->CheckWord(word, &misspelled, nullptr); + // If CheckWord failed, we can't tell whether the word is correctly spelled + if (NS_FAILED(rv)) { + misspelled = false; + } + misspells.AppendElement(misspelled); + } + aResolve(std::move(misspells)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult RemoteSpellcheckEngineParent::RecvCheckAndSuggest( + const nsString& aWord, bool* aIsMisspelled, + nsTArray<nsString>* aSuggestions) { + nsresult rv = mSpellChecker->CheckWord(aWord, aIsMisspelled, aSuggestions); + if (NS_FAILED(rv)) { + aSuggestions->Clear(); + *aIsMisspelled = false; + } + return IPC_OK(); +} + +void RemoteSpellcheckEngineParent::ActorDestroy(ActorDestroyReason aWhy) {} + +} // namespace mozilla diff --git a/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineParent.h b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineParent.h new file mode 100644 index 0000000000..f636b4a1c8 --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineParent.h @@ -0,0 +1,42 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef RemoteSpellcheckEngineParent_h_ +#define RemoteSpellcheckEngineParent_h_ + +#include "mozilla/PRemoteSpellcheckEngineParent.h" +#include "nsCOMPtr.h" +#include "nsTArray.h" + +class mozSpellChecker; + +namespace mozilla { + +class RemoteSpellcheckEngineParent : public PRemoteSpellcheckEngineParent { + public: + RemoteSpellcheckEngineParent(); + + virtual ~RemoteSpellcheckEngineParent(); + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + virtual mozilla::ipc::IPCResult RecvSetDictionary( + const nsCString& aDictionary, bool* success); + + virtual mozilla::ipc::IPCResult RecvSetDictionaryFromList( + nsTArray<nsCString>&& aList, SetDictionaryFromListResolver&& aResolve); + + virtual mozilla::ipc::IPCResult RecvCheckAsync(nsTArray<nsString>&& aWord, + CheckAsyncResolver&& aResolve); + + virtual mozilla::ipc::IPCResult RecvCheckAndSuggest( + const nsString& aWord, bool* aIsMisspelled, + nsTArray<nsString>* aSuggestions); + + private: + RefPtr<mozSpellChecker> mSpellChecker; +}; + +} // namespace mozilla + +#endif diff --git a/extensions/spellcheck/hunspell/glue/common.mozbuild b/extensions/spellcheck/hunspell/glue/common.mozbuild new file mode 100644 index 0000000000..ded414075e --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/common.mozbuild @@ -0,0 +1,9 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +@template +def HunspellIncludes(): + ForceInclude('hunspell_alloc_hooks.h', 'hunspell_fopen_hooks.h') diff --git a/extensions/spellcheck/hunspell/glue/hunspell_alloc_hooks.h b/extensions/spellcheck/hunspell/glue/hunspell_alloc_hooks.h new file mode 100644 index 0000000000..27a719d788 --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/hunspell_alloc_hooks.h @@ -0,0 +1,59 @@ +/******* BEGIN LICENSE BLOCK ******* + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Initial Developers of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developers + * are Copyright (C) 2011 the Initial Developers. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + ******* END LICENSE BLOCK *******/ + +#ifndef alloc_hooks_h__ +#define alloc_hooks_h__ + +/** + * This file is force-included in hunspell code. Its purpose is to add memory + * reporting to hunspell without modifying its code, in order to ease future + * upgrades. + * + * Currently, the memory allocated using operator new/new[] is not being + * tracked, but that's OK, since almost all of the memory used by Hunspell is + * allocated using C memory allocation functions. + */ + +#include "mozHunspellAllocator.h" + +// Ensure that malloc is imported before we set our malloc-counting hooks below. +// Otherwise, if malloc is imported afterwards, its source will be trampled +// over by the "#define"s. +#include "mozmemory.h" + +#define malloc(size) HunspellAllocator::CountingMalloc(size) +#define calloc(count, size) HunspellAllocator::CountingCalloc(count, size) +#define free(ptr) HunspellAllocator::CountingFree(ptr) +#define realloc(ptr, size) HunspellAllocator::CountingRealloc(ptr, size) + +#endif diff --git a/extensions/spellcheck/hunspell/glue/hunspell_fopen_hooks.h b/extensions/spellcheck/hunspell/glue/hunspell_fopen_hooks.h new file mode 100644 index 0000000000..7388b1daa7 --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/hunspell_fopen_hooks.h @@ -0,0 +1,81 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef fopen_hooks_h__ +#define fopen_hooks_h__ + +/** + * This file is force-included in hunspell code. Its purpose is to add + * readahead to fopen() calls in hunspell without modifying its code, in order + * to ease future upgrades. + */ + +#include "mozilla/FileUtils.h" +#include <stdio.h> +#include <string.h> + +#if defined(XP_WIN) +# include "nsNativeCharsetUtils.h" +# include "nsString.h" + +# include <fcntl.h> +# include <windows.h> +// Hunspell defines a function named near. Windef.h #defines near. +# undef near +// mozHunspell defines a function named RemoveDirectory. +# undef RemoveDirectory +#endif /* defined(XP_WIN) */ + +inline FILE* hunspell_fopen_readahead(const char* filename, const char* mode) { + if (!filename || !mode) { + return nullptr; + } + // Fall back to libc's fopen for modes not supported by ReadAheadFile + if (!strchr(mode, 'r') || strchr(mode, '+')) { + return fopen(filename, mode); + } + int fd = -1; +#if defined(XP_WIN) + // filename is obtained via the nsIFile::nativePath attribute, so + // it is using the Windows ANSI code page, NOT UTF-8! + nsAutoString utf16Filename; + nsresult rv = + NS_CopyNativeToUnicode(nsDependentCString(filename), utf16Filename); + if (NS_FAILED(rv)) { + return nullptr; + } + HANDLE handle = INVALID_HANDLE_VALUE; + mozilla::ReadAheadFile(utf16Filename.get(), 0, SIZE_MAX, &handle); + if (handle == INVALID_HANDLE_VALUE) { + return nullptr; + } + int flags = _O_RDONLY; + // MSVC CRT's _open_osfhandle only supports adding _O_TEXT, not _O_BINARY + if (strchr(mode, 't')) { + // Force translated mode + flags |= _O_TEXT; + } + // Import the Win32 fd into the CRT + fd = _open_osfhandle((intptr_t)handle, flags); + if (fd < 0) { + CloseHandle(handle); + return nullptr; + } +#else + mozilla::ReadAheadFile(filename, 0, SIZE_MAX, &fd); + if (fd < 0) { + return nullptr; + } +#endif /* defined(XP_WIN) */ + + FILE* file = fdopen(fd, mode); + if (!file) { + close(fd); + } + return file; +} + +#define fopen(filename, mode) hunspell_fopen_readahead(filename, mode) + +#endif /* fopen_hooks_h__ */ diff --git a/extensions/spellcheck/hunspell/glue/moz.build b/extensions/spellcheck/hunspell/glue/moz.build new file mode 100644 index 0000000000..7d5a110d7d --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/moz.build @@ -0,0 +1,42 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES += [ + "mozHunspell.cpp", + "mozHunspellFileMgrHost.cpp", + "RemoteSpellCheckEngineChild.cpp", + "RemoteSpellCheckEngineParent.cpp", +] + +DEFINES["HUNSPELL_STATIC"] = True + +FINAL_LIBRARY = "xul" + +LOCAL_INCLUDES += [ + "../src", + "/dom/base", + "/extensions/spellcheck/src", +] + +include("/ipc/chromium/chromium-config.mozbuild") +include("common.mozbuild") + +HunspellIncludes() + +IPDL_SOURCES = [ + "PRemoteSpellcheckEngine.ipdl", +] + +EXPORTS.mozilla += [ + "RemoteSpellCheckEngineChild.h", + "RemoteSpellCheckEngineParent.h", +] + +if CONFIG["CC_TYPE"] in ("clang", "clang-cl"): + CXXFLAGS += [ + # We force-include mozHunspellAllocator.h from third-party code. + "-Wno-undefined-var-template", + ] diff --git a/extensions/spellcheck/hunspell/glue/mozHunspell.cpp b/extensions/spellcheck/hunspell/glue/mozHunspell.cpp new file mode 100644 index 0000000000..f722d9ac21 --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/mozHunspell.cpp @@ -0,0 +1,509 @@ +/******* BEGIN LICENSE BLOCK ******* + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Initial Developers of the Original Code are Kevin Hendricks (MySpell) + * and László Németh (Hunspell). Portions created by the Initial Developers + * are Copyright (C) 2002-2005 the Initial Developers. All Rights Reserved. + * + * Contributor(s): Kevin Hendricks (kevin.hendricks@sympatico.ca) + * David Einstein (deinst@world.std.com) + * Michiel van Leeuwen (mvl@exedo.nl) + * Caolan McNamara (cmc@openoffice.org) + * László Németh (nemethl@gyorsposta.hu) + * Davide Prina + * Giuseppe Modugno + * Gianluca Turconi + * Simon Brouwer + * Noll Janos + * Biro Arpad + * Goldman Eleonora + * Sarlos Tamas + * Bencsath Boldizsar + * Halacsy Peter + * Dvornik Laszlo + * Gefferth Andras + * Nagy Viktor + * Varga Daniel + * Chris Halls + * Rene Engelhard + * Bram Moolenaar + * Dafydd Jones + * Harri Pitkanen + * Andras Timar + * Tor Lillqvist + * Jesper Kristensen (mail@jesperkristensen.dk) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + ******* END LICENSE BLOCK *******/ + +#include "mozHunspell.h" +#include "mozHunspellFileMgrGlue.h" +#include "mozHunspellFileMgrHost.h" +#include "nsReadableUtils.h" +#include "nsString.h" +#include "nsIObserverService.h" +#include "nsIDirectoryEnumerator.h" +#include "nsIFile.h" +#include "nsUnicharUtils.h" +#include "nsCRT.h" +#include "mozInlineSpellChecker.h" +#include <stdlib.h> +#include "nsIPrefBranch.h" +#include "nsIPrefService.h" +#include "nsNetUtil.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/Components.h" +#include "mozilla/Services.h" + +using mozilla::dom::ContentParent; +using namespace mozilla; + +NS_IMPL_CYCLE_COLLECTING_ADDREF(mozHunspell) +NS_IMPL_CYCLE_COLLECTING_RELEASE(mozHunspell) + +NS_INTERFACE_MAP_BEGIN(mozHunspell) + NS_INTERFACE_MAP_ENTRY(mozISpellCheckingEngine) + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY(nsIMemoryReporter) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozISpellCheckingEngine) + NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozHunspell) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_WEAK(mozHunspell, mPersonalDictionary) + +NS_IMPL_COMPONENT_FACTORY(mozHunspell) { + auto hunspell = MakeRefPtr<mozHunspell>(); + if (NS_SUCCEEDED(hunspell->Init())) { + return hunspell.forget().downcast<mozISpellCheckingEngine>(); + } + return nullptr; +} + +template <> +mozilla::CountingAllocatorBase<HunspellAllocator>::AmountType + mozilla::CountingAllocatorBase<HunspellAllocator>::sAmount(0); + +mozHunspell::mozHunspell() : mHunspell(nullptr) { +#ifdef DEBUG + // There must be only one instance of this class: it reports memory based on + // a single static count in HunspellAllocator. + static bool hasRun = false; + MOZ_ASSERT(!hasRun); + hasRun = true; +#endif +} + +nsresult mozHunspell::Init() { + LoadDictionaryList(false); + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->AddObserver(this, "profile-do-change", true); + obs->AddObserver(this, "profile-after-change", true); + } + + mozilla::RegisterWeakMemoryReporter(this); + + return NS_OK; +} + +mozHunspell::~mozHunspell() { + mozilla::UnregisterWeakMemoryReporter(this); + + mPersonalDictionary = nullptr; + delete mHunspell; +} + +NS_IMETHODIMP +mozHunspell::GetDictionary(nsACString& aDictionary) { + CopyUTF16toUTF8(mDictionary, aDictionary); + return NS_OK; +} + +/* set the Dictionary. + * This also Loads the dictionary and initializes the converter using the + * dictionaries converter + */ +NS_IMETHODIMP +mozHunspell::SetDictionary(const nsACString& aDictionary) { + if (aDictionary.IsEmpty()) { + delete mHunspell; + mHunspell = nullptr; + mDictionary.Truncate(); + mAffixFileName.Truncate(); + mDecoder = nullptr; + mEncoder = nullptr; + + return NS_OK; + } + + NS_ConvertUTF8toUTF16 dict(aDictionary); + nsIURI* affFile = mDictionaries.GetWeak(dict); + if (!affFile) { + return NS_ERROR_FILE_NOT_FOUND; + } + + nsAutoCString dictFileName, affFileName; + + nsresult rv = affFile->GetSpec(affFileName); + NS_ENSURE_SUCCESS(rv, rv); + + if (mAffixFileName.Equals(affFileName)) { + return NS_OK; + } + + dictFileName = affFileName; + int32_t dotPos = dictFileName.RFindChar('.'); + if (dotPos == -1) return NS_ERROR_FAILURE; + + dictFileName.SetLength(dotPos); + dictFileName.AppendLiteral(".dic"); + + // SetDictionary can be called multiple times, so we might have a + // valid mHunspell instance which needs cleaned up. + delete mHunspell; + + mDictionary = dict; + mAffixFileName = affFileName; + + RegisterHunspellCallbacks( + mozHunspellCallbacks::CreateFilemgr, mozHunspellCallbacks::GetLine, + mozHunspellCallbacks::GetLineNum, mozHunspellCallbacks::DestructFilemgr); + mHunspell = new Hunspell(affFileName.get(), dictFileName.get()); + if (!mHunspell) return NS_ERROR_OUT_OF_MEMORY; + + auto encoding = + Encoding::ForLabelNoReplacement(mHunspell->get_dict_encoding()); + if (!encoding) { + return NS_ERROR_UCONV_NOCONV; + } + mEncoder = encoding->NewEncoder(); + mDecoder = encoding->NewDecoderWithoutBOMHandling(); + + return NS_OK; +} + +NS_IMETHODIMP mozHunspell::GetPersonalDictionary( + mozIPersonalDictionary** aPersonalDictionary) { + *aPersonalDictionary = mPersonalDictionary; + NS_IF_ADDREF(*aPersonalDictionary); + return NS_OK; +} + +NS_IMETHODIMP mozHunspell::SetPersonalDictionary( + mozIPersonalDictionary* aPersonalDictionary) { + mPersonalDictionary = aPersonalDictionary; + return NS_OK; +} + +NS_IMETHODIMP mozHunspell::GetDictionaryList( + nsTArray<nsCString>& aDictionaries) { + MOZ_ASSERT(aDictionaries.IsEmpty()); + for (auto iter = mDictionaries.Iter(); !iter.Done(); iter.Next()) { + aDictionaries.AppendElement(NS_ConvertUTF16toUTF8(iter.Key())); + } + + return NS_OK; +} + +void mozHunspell::LoadDictionaryList(bool aNotifyChildProcesses) { + mDictionaries.Clear(); + + nsresult rv; + + // find built in dictionaries, or dictionaries specified in + // spellchecker.dictionary_path in prefs + nsCOMPtr<nsIFile> dictDir; + + // check preferences first + nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); + if (prefs) { + nsAutoCString extDictPath; + rv = prefs->GetCharPref("spellchecker.dictionary_path", extDictPath); + if (NS_SUCCEEDED(rv)) { + // set the spellchecker.dictionary_path + rv = NS_NewNativeLocalFile(extDictPath, true, getter_AddRefs(dictDir)); + } + if (dictDir) { + LoadDictionariesFromDir(dictDir); + } + } + + // find dictionaries in DICPATH + char* dicEnv = PR_GetEnv("DICPATH"); + if (dicEnv) { + // do a two-pass dance so dictionaries are loaded right-to-left as + // preference + nsTArray<nsCOMPtr<nsIFile>> dirs; + nsAutoCString env(dicEnv); // assume dicEnv is UTF-8 + + char* currPath = nullptr; + char* nextPaths = env.BeginWriting(); + while ((currPath = NS_strtok(":", &nextPaths))) { + nsCOMPtr<nsIFile> dir; + rv = + NS_NewNativeLocalFile(nsCString(currPath), true, getter_AddRefs(dir)); + if (NS_SUCCEEDED(rv)) { + dirs.AppendElement(dir); + } + } + + // load them in reverse order so they override each other properly + for (int32_t i = dirs.Length() - 1; i >= 0; i--) { + LoadDictionariesFromDir(dirs[i]); + } + } + + // find dictionaries from restartless extensions + for (int32_t i = 0; i < mDynamicDirectories.Count(); i++) { + LoadDictionariesFromDir(mDynamicDirectories[i]); + } + + for (auto iter = mDynamicDictionaries.Iter(); !iter.Done(); iter.Next()) { + mDictionaries.Put(iter.Key(), iter.Data()); + } + + DictionariesChanged(aNotifyChildProcesses); +} + +void mozHunspell::DictionariesChanged(bool aNotifyChildProcesses) { + // Now we have finished updating the list of dictionaries, update the current + // dictionary and any editors which may use it. + mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking(); + + if (aNotifyChildProcesses) { + ContentParent::NotifyUpdatedDictionaries(); + } + + // Check if the current dictionary is still available. + // If not, try to replace it with another dictionary of the same language. + if (!mDictionary.IsEmpty()) { + nsresult rv = SetDictionary(NS_ConvertUTF16toUTF8(mDictionary)); + if (NS_SUCCEEDED(rv)) return; + } + + // If the current dictionary has gone, and we don't have a good replacement, + // set no current dictionary. + if (!mDictionary.IsEmpty()) { + SetDictionary(EmptyCString()); + } +} + +NS_IMETHODIMP +mozHunspell::LoadDictionariesFromDir(nsIFile* aDir) { + nsresult rv; + + bool check = false; + rv = aDir->Exists(&check); + if (NS_FAILED(rv) || !check) return NS_ERROR_UNEXPECTED; + + rv = aDir->IsDirectory(&check); + if (NS_FAILED(rv) || !check) return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsIDirectoryEnumerator> files; + rv = aDir->GetDirectoryEntries(getter_AddRefs(files)); + if (NS_FAILED(rv)) return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsIFile> file; + while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file))) && file) { + nsAutoString leafName; + file->GetLeafName(leafName); + if (!StringEndsWith(leafName, u".dic"_ns)) continue; + + nsAutoString dict(leafName); + dict.SetLength(dict.Length() - 4); // magic length of ".dic" + + // check for the presence of the .aff file + leafName = dict; + leafName.AppendLiteral(".aff"); + file->SetLeafName(leafName); + rv = file->Exists(&check); + if (NS_FAILED(rv) || !check) continue; + +#ifdef DEBUG_bsmedberg + printf("Adding dictionary: %s\n", NS_ConvertUTF16toUTF8(dict).get()); +#endif + + // Replace '_' separator with '-' + dict.ReplaceChar("_", '-'); + + nsCOMPtr<nsIURI> uri; + rv = NS_NewFileURI(getter_AddRefs(uri), file); + NS_ENSURE_SUCCESS(rv, rv); + + mDictionaries.Put(dict, uri); + } + + return NS_OK; +} + +nsresult mozHunspell::ConvertCharset(const nsAString& aStr, std::string& aDst) { + if (NS_WARN_IF(!mEncoder)) { + return NS_ERROR_NOT_INITIALIZED; + } + + auto src = Span(aStr.BeginReading(), aStr.Length()); + CheckedInt<size_t> needed = + mEncoder->MaxBufferLengthFromUTF16WithoutReplacement(src.Length()); + if (!needed.isValid()) { + return NS_ERROR_OUT_OF_MEMORY; + } + + aDst.resize(needed.value()); + + char* dstPtr = &aDst[0]; + auto dst = Span(reinterpret_cast<uint8_t*>(dstPtr), needed.value()); + + uint32_t result; + size_t read; + size_t written; + Tie(result, read, written) = + mEncoder->EncodeFromUTF16WithoutReplacement(src, dst, true); + Unused << read; + MOZ_ASSERT(result != kOutputFull); + if (result != kInputEmpty) { + return NS_ERROR_UENC_NOMAPPING; + } + aDst.resize(written); + mEncoder->Encoding()->NewEncoderInto(*mEncoder); + return NS_OK; +} + +NS_IMETHODIMP +mozHunspell::CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) { + MOZ_COLLECT_REPORT("explicit/spell-check", KIND_HEAP, UNITS_BYTES, + HunspellAllocator::MemoryAllocated(), + "Memory used by the spell-checking engine."); + + return NS_OK; +} + +NS_IMETHODIMP +mozHunspell::Check(const nsAString& aWord, bool* aResult) { + if (NS_WARN_IF(!aResult)) { + return NS_ERROR_INVALID_ARG; + } + NS_ENSURE_TRUE(mHunspell, NS_ERROR_FAILURE); + + std::string charsetWord; + nsresult rv = ConvertCharset(aWord, charsetWord); + NS_ENSURE_SUCCESS(rv, rv); + + *aResult = mHunspell->spell(charsetWord); + + if (!*aResult && mPersonalDictionary) + rv = mPersonalDictionary->Check(aWord, aResult); + + return rv; +} + +NS_IMETHODIMP +mozHunspell::Suggest(const nsAString& aWord, nsTArray<nsString>& aSuggestions) { + NS_ENSURE_TRUE(mHunspell, NS_ERROR_FAILURE); + MOZ_ASSERT(aSuggestions.IsEmpty()); + + std::string charsetWord; + nsresult rv = ConvertCharset(aWord, charsetWord); + NS_ENSURE_SUCCESS(rv, rv); + + std::vector<std::string> suggestions = mHunspell->suggest(charsetWord); + + if (!suggestions.empty()) { + aSuggestions.SetCapacity(suggestions.size()); + for (Span<const char> charSrc : suggestions) { + // Convert the suggestion to utf16 + auto src = AsBytes(charSrc); + rv = mDecoder->Encoding()->DecodeWithoutBOMHandling( + src, *aSuggestions.AppendElement()); + NS_ENSURE_SUCCESS(rv, rv); + mDecoder->Encoding()->NewDecoderWithoutBOMHandlingInto(*mDecoder); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +mozHunspell::Observe(nsISupports* aSubj, const char* aTopic, + const char16_t* aData) { + NS_ASSERTION(!strcmp(aTopic, "profile-do-change") || + !strcmp(aTopic, "profile-after-change"), + "Unexpected observer topic"); + + LoadDictionaryList(false); + + return NS_OK; +} + +NS_IMETHODIMP mozHunspell::AddDirectory(nsIFile* aDir) { + mDynamicDirectories.AppendObject(aDir); + LoadDictionaryList(true); + return NS_OK; +} + +NS_IMETHODIMP mozHunspell::RemoveDirectory(nsIFile* aDir) { + mDynamicDirectories.RemoveObject(aDir); + LoadDictionaryList(true); + +#ifdef MOZ_THUNDERBIRD + /* + * This notification is needed for Thunderbird. Thunderbird derives the + * dictionary from the document's "lang" attribute. If a dictionary is + * removed, we need to change the "lang" attribute. + */ + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, SPELLCHECK_DICTIONARY_REMOVE_NOTIFICATION, + nullptr); + } +#endif + return NS_OK; +} + +NS_IMETHODIMP mozHunspell::AddDictionary(const nsAString& aLang, + nsIURI* aFile) { + NS_ENSURE_TRUE(aFile, NS_ERROR_INVALID_ARG); + + mDynamicDictionaries.Put(aLang, aFile); + mDictionaries.Put(aLang, aFile); + DictionariesChanged(true); + return NS_OK; +} + +NS_IMETHODIMP mozHunspell::RemoveDictionary(const nsAString& aLang, + nsIURI* aFile, bool* aRetVal) { + NS_ENSURE_TRUE(aFile, NS_ERROR_INVALID_ARG); + *aRetVal = false; + + nsCOMPtr<nsIURI> file = mDynamicDictionaries.Get(aLang); + bool equal; + if (file && NS_SUCCEEDED(file->Equals(aFile, &equal)) && equal) { + mDynamicDictionaries.Remove(aLang); + LoadDictionaryList(true); + *aRetVal = true; + } + return NS_OK; +} diff --git a/extensions/spellcheck/hunspell/glue/mozHunspell.h b/extensions/spellcheck/hunspell/glue/mozHunspell.h new file mode 100644 index 0000000000..68cba4ae70 --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/mozHunspell.h @@ -0,0 +1,129 @@ +/******* BEGIN LICENSE BLOCK ******* + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Initial Developers of the Original Code are Kevin Hendricks (MySpell) + * and László Németh (Hunspell). Portions created by the Initial Developers + * are Copyright (C) 2002-2005 the Initial Developers. All Rights Reserved. + * + * Contributor(s): Kevin Hendricks (kevin.hendricks@sympatico.ca) + * David Einstein (deinst@world.std.com) + * Michiel van Leeuwen (mvl@exedo.nl) + * Caolan McNamara (cmc@openoffice.org) + * László Németh (nemethl@gyorsposta.hu) + * Davide Prina + * Giuseppe Modugno + * Gianluca Turconi + * Simon Brouwer + * Noll Janos + * Biro Arpad + * Goldman Eleonora + * Sarlos Tamas + * Bencsath Boldizsar + * Halacsy Peter + * Dvornik Laszlo + * Gefferth Andras + * Nagy Viktor + * Varga Daniel + * Chris Halls + * Rene Engelhard + * Bram Moolenaar + * Dafydd Jones + * Harri Pitkanen + * Andras Timar + * Tor Lillqvist + * Jesper Kristensen (mail@jesperkristensen.dk) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + ******* END LICENSE BLOCK *******/ + +#ifndef mozHunspell_h__ +#define mozHunspell_h__ + +#include <hunspell.hxx> +#include "mozISpellCheckingEngine.h" +#include "mozIPersonalDictionary.h" +#include "nsString.h" +#include "nsCOMPtr.h" +#include "nsCOMArray.h" +#include "nsIMemoryReporter.h" +#include "nsIObserver.h" +#include "nsIURI.h" +#include "mozilla/Encoding.h" +#include "nsInterfaceHashtable.h" +#include "nsWeakReference.h" +#include "nsCycleCollectionParticipant.h" +#include "mozHunspellAllocator.h" + +#define MOZ_HUNSPELL_CONTRACTID "@mozilla.org/spellchecker/engine;1" +#define MOZ_HUNSPELL_CID \ + /* 56c778e4-1bee-45f3-a689-886692a97fe7 */ \ + { \ + 0x56c778e4, 0x1bee, 0x45f3, { \ + 0xa6, 0x89, 0x88, 0x66, 0x92, 0xa9, 0x7f, 0xe7 \ + } \ + } + +class mozHunspell final : public mozISpellCheckingEngine, + public nsIObserver, + public nsSupportsWeakReference, + public nsIMemoryReporter { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_MOZISPELLCHECKINGENGINE + NS_DECL_NSIOBSERVER + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozHunspell, mozISpellCheckingEngine) + + mozHunspell(); + + nsresult Init(); + + void LoadDictionaryList(bool aNotifyChildProcesses); + + // helper method for converting a word to the charset of the dictionary + nsresult ConvertCharset(const nsAString& aStr, std::string& aDst); + + NS_DECL_NSIMEMORYREPORTER + + protected: + virtual ~mozHunspell(); + + void DictionariesChanged(bool aNotifyChildProcesses); + + nsCOMPtr<mozIPersonalDictionary> mPersonalDictionary; + mozilla::UniquePtr<mozilla::Encoder> mEncoder; + mozilla::UniquePtr<mozilla::Decoder> mDecoder; + + // Hashtable matches dictionary name to .aff file + nsInterfaceHashtable<nsStringHashKey, nsIURI> mDictionaries; + nsString mDictionary; + nsCString mAffixFileName; + + // dynamic dirs used to search for dictionaries + nsCOMArray<nsIFile> mDynamicDirectories; + nsInterfaceHashtable<nsStringHashKey, nsIURI> mDynamicDictionaries; + + Hunspell* mHunspell; +}; + +#endif diff --git a/extensions/spellcheck/hunspell/glue/mozHunspellAllocator.h b/extensions/spellcheck/hunspell/glue/mozHunspellAllocator.h new file mode 100644 index 0000000000..ddcec3e28c --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/mozHunspellAllocator.h @@ -0,0 +1,15 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozHunspellAllocator_h__ +#define mozHunspellAllocator_h__ + +#include "mozilla/CountingAllocatorBase.h" + +class HunspellAllocator + : public mozilla::CountingAllocatorBase<HunspellAllocator> {}; + +#endif diff --git a/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrGlue.h b/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrGlue.h new file mode 100644 index 0000000000..d3c75dc04c --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrGlue.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozHunspellFileMgrGlue_h +#define mozHunspellFileMgrGlue_h + +#include <stdint.h> + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef uint32_t(hunspell_create_filemgr_t)(const char* aFilename, + const char* aKey); +typedef bool(hunspell_get_line_t)(uint32_t aFd, char** aLinePtr); +typedef int(hunspell_get_line_num_t)(uint32_t aFd); +typedef void(hunspell_destruct_filemgr_t)(uint32_t aFd); + +void RegisterHunspellCallbacks( + hunspell_create_filemgr_t* aHunspellCreateFilemgr, + hunspell_get_line_t* aHunspellGetLine, + hunspell_get_line_num_t* aHunspellGetLine_num, + hunspell_destruct_filemgr_t* aHunspellDestructFilemgr); + +extern hunspell_create_filemgr_t* moz_glue_hunspell_create_filemgr; +extern hunspell_get_line_t* moz_glue_hunspell_get_line; +extern hunspell_get_line_num_t* moz_glue_hunspell_get_line_num; +extern hunspell_destruct_filemgr_t* moz_glue_hunspell_destruct_filemgr; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrHost.cpp b/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrHost.cpp new file mode 100644 index 0000000000..ce8b212615 --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrHost.cpp @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <limits> + +#include "mozHunspellFileMgrHost.h" +#include "mozilla/DebugOnly.h" +#include "nsContentUtils.h" +#include "nsIChannel.h" +#include "nsILoadInfo.h" +#include "nsNetUtil.h" + +using namespace mozilla; + +mozHunspellFileMgrHost::mozHunspellFileMgrHost(const char* aFilename, + const char* aKey) { + DebugOnly<Result<Ok, nsresult>> result = Open(nsDependentCString(aFilename)); + NS_WARNING_ASSERTION(result.value.isOk(), "Failed to open Hunspell file"); +} + +Result<Ok, nsresult> mozHunspellFileMgrHost::Open(const nsACString& aPath) { + nsCOMPtr<nsIURI> uri; + MOZ_TRY(NS_NewURI(getter_AddRefs(uri), aPath)); + + nsCOMPtr<nsIChannel> channel; + MOZ_TRY(NS_NewChannel( + getter_AddRefs(channel), uri, nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT, + nsIContentPolicy::TYPE_OTHER)); + + MOZ_TRY(channel->Open(getter_AddRefs(mStream))); + return Ok(); +} + +Result<Ok, nsresult> mozHunspellFileMgrHost::ReadLine(nsACString& aLine) { + if (!mStream) { + return Err(NS_ERROR_NOT_INITIALIZED); + } + + bool ok; + MOZ_TRY(NS_ReadLine(mStream.get(), &mLineBuffer, aLine, &ok)); + if (!ok) { + mStream = nullptr; + } + + mLineNum++; + return Ok(); +} + +bool mozHunspellFileMgrHost::GetLine(std::string& aResult) { + nsAutoCString line; + auto res = ReadLine(line); + if (res.isErr()) { + return false; + } + + aResult.assign(line.BeginReading(), line.Length()); + return true; +} + +/* static */ +uint32_t mozHunspellCallbacks::sCurrentFreshId = 0; +/* static */ +mozilla::detail::StaticRWLock mozHunspellCallbacks::sFileMgrMapLock; +/* static */ +std::map<uint32_t, std::unique_ptr<mozHunspellFileMgrHost>> + mozHunspellCallbacks::sFileMgrMap; + +/* static */ +uint32_t mozHunspellCallbacks::CreateFilemgr(const char* aFilename, + const char* aKey) { + mozilla::detail::StaticAutoWriteLock lock(sFileMgrMapLock); + uint32_t freshId = GetFreshId(); + sFileMgrMap[freshId] = std::unique_ptr<mozHunspellFileMgrHost>( + new mozHunspellFileMgrHost(aFilename, aKey)); + return freshId; +} + +/* static */ +uint32_t mozHunspellCallbacks::GetFreshId() { + // i is uint64_t to prevent overflow during loop increment which would cause + // an infinite loop + for (uint64_t i = sCurrentFreshId; i < std::numeric_limits<uint32_t>::max(); + i++) { + auto it = sFileMgrMap.find(i); + if (it == sFileMgrMap.end()) { + // set sCurrentFreshId to the next (possibly) available id + sCurrentFreshId = i + 1; + return static_cast<uint32_t>(i); + } + } + + MOZ_CRASH("Ran out of unique file ids for hunspell dictionaries"); +} + +/* static */ +mozHunspellFileMgrHost& mozHunspellCallbacks::GetMozHunspellFileMgrHost( + uint32_t aFd) { + mozilla::detail::StaticAutoReadLock lock(sFileMgrMapLock); + auto iter = sFileMgrMap.find(aFd); + MOZ_RELEASE_ASSERT(iter != sFileMgrMap.end()); + return *(iter->second.get()); +} + +/* static */ +bool mozHunspellCallbacks::GetLine(uint32_t aFd, char** aLinePtr) { + mozHunspellFileMgrHost& inst = + mozHunspellCallbacks::GetMozHunspellFileMgrHost(aFd); + std::string line; + bool ok = inst.GetLine(line); + if (ok) { + *aLinePtr = static_cast<char*>(malloc(line.size() + 1)); + strcpy(*aLinePtr, line.c_str()); + } else { + *aLinePtr = nullptr; + } + return ok; +} + +/* static */ +int mozHunspellCallbacks::GetLineNum(uint32_t aFd) { + mozHunspellFileMgrHost& inst = + mozHunspellCallbacks::GetMozHunspellFileMgrHost(aFd); + int num = inst.GetLineNum(); + return num; +} + +/* static */ +void mozHunspellCallbacks::DestructFilemgr(uint32_t aFd) { + mozilla::detail::StaticAutoWriteLock lock(sFileMgrMapLock); + + auto iter = sFileMgrMap.find(aFd); + if (iter != sFileMgrMap.end()) { + sFileMgrMap.erase(iter); + } +} diff --git a/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrHost.h b/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrHost.h new file mode 100644 index 0000000000..8812416f71 --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrHost.h @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozHunspellFileMgrHost_h +#define mozHunspellFileMgrHost_h + +#include <map> +#include <memory> +#include <mutex> +#include <stdint.h> +#include <string> + +#include "mozilla/Result.h" +#include "mozilla/ResultExtensions.h" +#include "mozilla/RWLock.h" +#include "nsIInputStream.h" +#include "nsReadLine.h" + +namespace mozilla { + +class mozHunspellFileMgrHost final { + public: + /** + * aFilename must be a local file/jar URI for the file to load. + * + * aKey is the decription key for encrypted Hunzip files, and is + * unsupported. The argument is there solely for compatibility. + */ + explicit mozHunspellFileMgrHost(const char* aFilename, + const char* aKey = nullptr); + ~mozHunspellFileMgrHost() = default; + + bool GetLine(std::string& aResult); + int GetLineNum() const { return mLineNum; } + + private: + mozilla::Result<mozilla::Ok, nsresult> Open(const nsACString& aPath); + + mozilla::Result<mozilla::Ok, nsresult> ReadLine(nsACString& aLine); + + int mLineNum = 0; + nsCOMPtr<nsIInputStream> mStream; + nsLineBuffer<char> mLineBuffer; +}; + +class mozHunspellCallbacks { + public: + // APIs invoked by the sandboxed hunspell file manager + static uint32_t CreateFilemgr(const char* aFilename, const char* aKey); + static bool GetLine(uint32_t aFd, char** aLinePtr); + static int GetLineNum(uint32_t aFd); + static void DestructFilemgr(uint32_t aFd); + + private: + /** + * sFileMgrMap holds a map between unique uint32_t + * integers and mozHunspellFileMgrHost instances + */ + static std::map<uint32_t, std::unique_ptr<mozHunspellFileMgrHost>> + sFileMgrMap; + /** + * Reader-writer lock for the sFileMgrMap + */ + static mozilla::detail::StaticRWLock sFileMgrMapLock; + /** + * Tracks the next possibly unused id for sFileMgrMap + */ + static uint32_t sCurrentFreshId; + /** + * Returns an unused id for sFileMgrMap + */ + static uint32_t GetFreshId(); + /** + * Returns the mozHunspellFileMgrHost for the given uint32_t id + */ + static mozHunspellFileMgrHost& GetMozHunspellFileMgrHost(uint32_t aFd); +}; +} // namespace mozilla + +#endif diff --git a/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrSandbox.cpp b/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrSandbox.cpp new file mode 100644 index 0000000000..4458877464 --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrSandbox.cpp @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozHunspellFileMgrSandbox.h" +#include "mozHunspellFileMgrGlue.h" + +FileMgr::FileMgr(const char* aFilename, const char* aKey) : mFd(0) { + mFd = moz_glue_hunspell_create_filemgr(aFilename, aKey); +} + +bool FileMgr::getline(std::string& aResult) { + char* line = nullptr; + bool ok = moz_glue_hunspell_get_line(mFd, &line); + if (ok && line) { + aResult = line; + free(line); + } + return ok; +} + +int FileMgr::getlinenum() const { return moz_glue_hunspell_get_line_num(mFd); } + +FileMgr::~FileMgr() { moz_glue_hunspell_destruct_filemgr(mFd); } + +// Glue code to set global callbacks + +hunspell_create_filemgr_t* moz_glue_hunspell_create_filemgr = nullptr; +hunspell_get_line_t* moz_glue_hunspell_get_line = nullptr; +hunspell_get_line_num_t* moz_glue_hunspell_get_line_num = nullptr; +hunspell_destruct_filemgr_t* moz_glue_hunspell_destruct_filemgr = nullptr; + +void RegisterHunspellCallbacks( + hunspell_create_filemgr_t* aHunspellCreateFilemgr, + hunspell_get_line_t* aHunspellGetLine, + hunspell_get_line_num_t* aHunspellGetLine_num, + hunspell_destruct_filemgr_t* aHunspellDestructFilemgr) { + moz_glue_hunspell_create_filemgr = aHunspellCreateFilemgr; + moz_glue_hunspell_get_line = aHunspellGetLine; + moz_glue_hunspell_get_line_num = aHunspellGetLine_num; + moz_glue_hunspell_destruct_filemgr = aHunspellDestructFilemgr; +} diff --git a/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrSandbox.h b/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrSandbox.h new file mode 100644 index 0000000000..c0434c14ca --- /dev/null +++ b/extensions/spellcheck/hunspell/glue/mozHunspellFileMgrSandbox.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozHunspellFileMgrSandbox_h +#define mozHunspellFileMgrSandbox_h + +#include <string> +#include <stdint.h> + +#include "mozilla/Result.h" +#include "mozilla/ResultExtensions.h" +#include "nsIInputStream.h" +#include "nsReadLine.h" + +// Note: This class name and lack of namespacing terrible, but are necessary +// for Hunspell compatibility. +class FileMgr final { + public: + /** + * aFilename must be a local file/jar URI for the file to load. + * + * aKey is the decription key for encrypted Hunzip files, and is + * unsupported. The argument is there solely for compatibility. + */ + explicit FileMgr(const char* aFilename, const char* aKey = nullptr); + ~FileMgr(); + + // Note: The nonstandard naming conventions of these methods are necessary for + // Hunspell compatibility. + bool getline(std::string& aLine); + int getlinenum() const; + + private: + // opaque file descriptor got from the host application + uint32_t mFd; +}; + +#endif // mozHunspellFileMgrSandbox_h |