Bug 880864 - User dictionary (persdict.dat) read on main thread. r=mayhemer

This commit is contained in:
Roberto A. Vitillo 2014-05-07 06:35:00 -04:00
Родитель fe7b5cef61
Коммит 27a423ebef
3 изменённых файлов: 176 добавлений и 28 удалений

Просмотреть файл

@ -17,6 +17,8 @@
#include "nsNetUtil.h"
#include "nsStringEnumerator.h"
#include "nsUnicharInputStream.h"
#include "nsIRunnable.h"
#include "nsThreadUtils.h"
#define MOZ_PERSONAL_DICT_NAME "persdict.dat"
@ -47,8 +49,33 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION(mozPersonalDictionary, mEncoder)
class mozPersonalDictionaryLoader MOZ_FINAL : public nsRunnable
{
public:
mozPersonalDictionaryLoader(mozPersonalDictionary *dict) : mDict(dict)
{
}
NS_IMETHOD Run()
{
if (!NS_IsMainThread()) {
mDict->SyncLoad();
// Release refptr on the mainthread
NS_DispatchToMainThread(this);
}
return NS_OK;
}
private:
nsRefPtr<mozPersonalDictionary> mDict;
};
mozPersonalDictionary::mozPersonalDictionary()
: mDirty(false)
: mDirty(false),
mIsLoaded(false),
mMonitor("mozPersonalDictionary::mMonitor")
{
}
@ -64,43 +91,123 @@ nsresult mozPersonalDictionary::Init()
NS_ENSURE_STATE(svc);
// we want to reload the dictionary if the profile switches
nsresult rv = svc->AddObserver(this, "profile-do-change", true);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = svc->AddObserver(this, "profile-before-change", true);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
Load();
return NS_OK;
}
/* void Load (); */
NS_IMETHODIMP mozPersonalDictionary::Load()
void mozPersonalDictionary::WaitForLoad()
{
//FIXME Deinst -- get dictionary name from prefs;
nsresult res;
nsCOMPtr<nsIFile> theFile;
bool dictExists;
if (mIsLoaded) {
return;
}
res = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(theFile));
if(NS_FAILED(res)) return res;
if(!theFile)return NS_ERROR_FAILURE;
res = theFile->Append(NS_LITERAL_STRING(MOZ_PERSONAL_DICT_NAME));
if(NS_FAILED(res)) return res;
res = theFile->Exists(&dictExists);
if(NS_FAILED(res)) return res;
mozilla::MonitorAutoLock mon(mMonitor);
if (!dictExists) {
// Nothing is really wrong...
if (!mIsLoaded) {
mon.Wait();
}
}
nsresult mozPersonalDictionary::LoadInternal()
{
nsresult rv;
mozilla::MonitorAutoLock mon(mMonitor);
if (mIsLoaded) {
return NS_OK;
}
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!mFile) {
return NS_ERROR_FAILURE;
}
rv = mFile->Append(NS_LITERAL_STRING(MOZ_PERSONAL_DICT_NAME));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIRunnable> runnable = new mozPersonalDictionaryLoader(this);
rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
NS_IMETHODIMP mozPersonalDictionary::Load()
{
nsresult rv = LoadInternal();
if (NS_FAILED(rv)) {
mIsLoaded = true;
}
return rv;
}
void mozPersonalDictionary::SyncLoad()
{
MOZ_ASSERT(!NS_IsMainThread());
mozilla::MonitorAutoLock mon(mMonitor);
if (mIsLoaded) {
return;
}
SyncLoadInternal();
mIsLoaded = true;
mon.Notify();
}
void mozPersonalDictionary::SyncLoadInternal()
{
MOZ_ASSERT(!NS_IsMainThread());
//FIXME Deinst -- get dictionary name from prefs;
nsresult rv;
bool dictExists;
rv = mFile->Exists(&dictExists);
if (NS_FAILED(rv)) {
return;
}
if (!dictExists) {
// Nothing is really wrong...
return;
}
nsCOMPtr<nsIInputStream> inStream;
NS_NewLocalFileInputStream(getter_AddRefs(inStream), theFile);
NS_NewLocalFileInputStream(getter_AddRefs(inStream), mFile);
nsCOMPtr<nsIUnicharInputStream> convStream;
res = nsSimpleUnicharStreamFactory::GetInstance()->
rv = nsSimpleUnicharStreamFactory::GetInstance()->
CreateInstanceFromUTF8Stream(inStream, getter_AddRefs(convStream));
if(NS_FAILED(res)) return res;
if (NS_FAILED(rv)) {
return;
}
// we're rereading to get rid of the old data -- we shouldn't have any, but...
mDictionaryTable.Clear();
@ -123,8 +230,6 @@ NS_IMETHODIMP mozPersonalDictionary::Load()
}
} while(!done);
mDirty = false;
return res;
}
// A little helper function to add the key to the list.
@ -143,6 +248,7 @@ NS_IMETHODIMP mozPersonalDictionary::Save()
nsCOMPtr<nsIFile> theFile;
nsresult res;
WaitForLoad();
if(!mDirty) return NS_OK;
//FIXME Deinst -- get dictionary name from prefs;
@ -188,6 +294,8 @@ NS_IMETHODIMP mozPersonalDictionary::GetWordList(nsIStringEnumerator **aWords)
NS_ENSURE_ARG_POINTER(aWords);
*aWords = nullptr;
WaitForLoad();
nsTArray<nsString> *array = new nsTArray<nsString>(mDictionaryTable.Count());
if (!array)
return NS_ERROR_OUT_OF_MEMORY;
@ -205,6 +313,8 @@ NS_IMETHODIMP mozPersonalDictionary::Check(const char16_t *aWord, const char16_t
NS_ENSURE_ARG_POINTER(aWord);
NS_ENSURE_ARG_POINTER(aResult);
WaitForLoad();
*aResult = (mDictionaryTable.GetEntry(aWord) || mIgnoreTable.GetEntry(aWord));
return NS_OK;
}
@ -212,6 +322,8 @@ NS_IMETHODIMP mozPersonalDictionary::Check(const char16_t *aWord, const char16_t
/* void AddWord (in wstring word); */
NS_IMETHODIMP mozPersonalDictionary::AddWord(const char16_t *aWord, const char16_t *aLang)
{
WaitForLoad();
mDictionaryTable.PutEntry(aWord);
mDirty = true;
return NS_OK;
@ -220,6 +332,8 @@ NS_IMETHODIMP mozPersonalDictionary::AddWord(const char16_t *aWord, const char16
/* void RemoveWord (in wstring word); */
NS_IMETHODIMP mozPersonalDictionary::RemoveWord(const char16_t *aWord, const char16_t *aLang)
{
WaitForLoad();
mDictionaryTable.RemoveEntry(aWord);
mDirty = true;
return NS_OK;
@ -237,6 +351,8 @@ NS_IMETHODIMP mozPersonalDictionary::IgnoreWord(const char16_t *aWord)
/* void EndSession (); */
NS_IMETHODIMP mozPersonalDictionary::EndSession()
{
WaitForLoad();
Save(); // save any custom words at the end of a spell check session
mIgnoreTable.Clear();
return NS_OK;
@ -264,6 +380,10 @@ NS_IMETHODIMP mozPersonalDictionary::GetCorrection(const char16_t *word, char16_
NS_IMETHODIMP mozPersonalDictionary::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData)
{
if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
// The observer is registered in Init() which calls Load and in turn
// LoadInternal(); i.e. Observe() can't be called before Load().
WaitForLoad();
mIsLoaded = false;
Load(); // load automatically clears out the existing dictionary table
} else if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
Save();

Просмотреть файл

@ -16,6 +16,7 @@
#include "nsCRT.h"
#include "nsCycleCollectionParticipant.h"
#include "nsHashKeys.h"
#include <mozilla/Monitor.h>
#define MOZ_PERSONALDICTIONARY_CONTRACTID "@mozilla.org/spellchecker/personaldictionary;1"
#define MOZ_PERSONALDICTIONARY_CID \
@ -23,6 +24,8 @@
0X7EF52EAF, 0XB7E1, 0X462B, \
{ 0X87, 0XE2, 0X5D, 0X1D, 0XBA, 0XCA, 0X90, 0X48 } }
class mozPersonalDictionaryLoader;
class mozPersonalDictionary : public mozIPersonalDictionary,
public nsIObserver,
public nsSupportsWeakReference
@ -39,10 +42,35 @@ public:
nsresult Init();
protected:
bool mDirty; /* has the dictionary been modified */
/* has the dictionary been modified */
bool mDirty;
/* true if the dictionary has been loaded from disk */
bool mIsLoaded;
nsCOMPtr<nsIFile> mFile;
mozilla::Monitor mMonitor;
nsTHashtable<nsUnicharPtrHashKey> mDictionaryTable;
nsTHashtable<nsUnicharPtrHashKey> mIgnoreTable;
nsCOMPtr<nsIUnicodeEncoder> mEncoder; /*Encoder to use to compare with spellchecker word */
/*Encoder to use to compare with spellchecker word */
nsCOMPtr<nsIUnicodeEncoder> mEncoder;
private:
/* wait for the asynchronous load of the dictionary to be completed */
void WaitForLoad();
/* enter the monitor before starting a synchronous load off the main-thread */
void SyncLoad();
/* launch an asynchrounous load of the dictionary from the main-thread
* after successfully initializing mFile with the path of the dictionary */
nsresult LoadInternal();
/* perform a synchronous load of the dictionary from disk */
void SyncLoadInternal();
friend class mozPersonalDictionaryLoader;
};
#endif

Просмотреть файл

@ -1,4 +1,4 @@
content quitter chrome/quitter/content/
component {c235a986-5ac1-4f28-ad73-825dae9bad90} components/QuitterObserver.js
contract @mozilla.org/quitter-observer;1 {c235a986-5ac1-4f28-ad73-825dae9bad90}
category profile-after-change @mozilla.org/quitter-observer;1 @mozilla.org/quitter-observer;1
component {c235a986-5ac1-4f28-ad73-825dae9bad90} components/QuitterObserver.js
content quitter chrome/quitter/content/
contract @mozilla.org/quitter-observer;1 {c235a986-5ac1-4f28-ad73-825dae9bad90}