mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1026099 - Rework the spellchecker context menu to not use CPOWs. r=ehsan/billm/felipe
This patch fixes a few nits and typos in the C++ spellchecking code as well as fixing the Finnish dictionary and multiple installed dictionaries in general (r=ehsan). It reworks the spellchecking context menu to use async message passing (in JS) and reduces, by a little, the CPOW use in the context menu code (r=felipe). Finally, the spellcheck IPDL no longer needs to be 'rpc' since we no longer nest spellchecking calls, so it can go back to being 'sync' (r=billm).
This commit is contained in:
parent
1401d14594
commit
8917309372
@ -8,6 +8,8 @@ let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource:///modules/ContentWebRTC.jsm");
|
||||
Cu.import("resource://gre/modules/InlineSpellChecker.jsm");
|
||||
Cu.import("resource://gre/modules/InlineSpellCheckerContent.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
|
||||
"resource:///modules/E10SUtils.jsm");
|
||||
@ -100,7 +102,15 @@ if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
||||
}
|
||||
|
||||
if (!defaultPrevented) {
|
||||
sendSyncMessage("contextmenu", {}, { event: event });
|
||||
let editFlags = SpellCheckHelper.isEditable(event.target, content);
|
||||
let spellInfo;
|
||||
if (editFlags &
|
||||
(SpellCheckHelper.EDITABLE | SpellCheckHelper.CONTENTEDITABLE)) {
|
||||
spellInfo =
|
||||
InlineSpellCheckerContent.initContextMenu(event, editFlags, this);
|
||||
}
|
||||
|
||||
sendSyncMessage("contextmenu", { editFlags, spellInfo }, { event });
|
||||
}
|
||||
}, false);
|
||||
} else {
|
||||
|
@ -1,8 +1,10 @@
|
||||
/* vim: set ts=2 sw=2 sts=2 et 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/.
|
||||
|
||||
Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
|
||||
|
||||
var gContextMenuContentData = null;
|
||||
|
||||
@ -518,11 +520,13 @@ nsContextMenu.prototype = {
|
||||
setTarget: function (aNode, aRangeParent, aRangeOffset) {
|
||||
// If gContextMenuContentData is not null, this event was forwarded from a
|
||||
// child process, so use that information instead.
|
||||
let editFlags;
|
||||
if (gContextMenuContentData) {
|
||||
this.isRemote = true;
|
||||
aNode = gContextMenuContentData.event.target;
|
||||
aRangeParent = gContextMenuContentData.event.rangeParent;
|
||||
aRangeOffset = gContextMenuContentData.event.rangeOffset;
|
||||
editFlags = gContextMenuContentData.editFlags;
|
||||
} else {
|
||||
this.isRemote = false;
|
||||
}
|
||||
@ -578,6 +582,7 @@ nsContextMenu.prototype = {
|
||||
if (this.isRemote) {
|
||||
this.browser = gContextMenuContentData.browser;
|
||||
} else {
|
||||
editFlags = SpellCheckHelper.isEditable(this.target, window);
|
||||
this.browser = this.target.ownerDocument.defaultView
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
@ -628,24 +633,19 @@ nsContextMenu.prototype = {
|
||||
this.onAudio = true;
|
||||
this.mediaURL = this.target.currentSrc || this.target.src;
|
||||
}
|
||||
else if (this.target instanceof HTMLInputElement ) {
|
||||
this.onTextInput = this.isTargetATextBox(this.target);
|
||||
// Allow spellchecking UI on all text and search inputs.
|
||||
if (this.onTextInput && ! this.target.readOnly &&
|
||||
(this.target.type == "text" || this.target.type == "search")) {
|
||||
this.onEditableArea = true;
|
||||
InlineSpellCheckerUI.init(this.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
|
||||
InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
|
||||
}
|
||||
this.onKeywordField = this.isTargetAKeywordField(this.target);
|
||||
}
|
||||
else if (this.target instanceof HTMLTextAreaElement) {
|
||||
this.onTextInput = true;
|
||||
if (!this.target.readOnly) {
|
||||
this.onEditableArea = true;
|
||||
InlineSpellCheckerUI.init(this.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
|
||||
InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
|
||||
else if (editFlags & (SpellCheckHelper.INPUT | SpellCheckHelper.TEXTAREA)) {
|
||||
this.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0;
|
||||
this.onEditableArea = (editFlags & SpellCheckHelper.EDITABLE) !== 0;
|
||||
if (this.onEditableArea) {
|
||||
if (gContextMenuContentData) {
|
||||
InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
|
||||
}
|
||||
else {
|
||||
InlineSpellCheckerUI.init(this.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
|
||||
InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
|
||||
}
|
||||
}
|
||||
this.onKeywordField = (editFlags & SpellCheckHelper.KEYWORD);
|
||||
}
|
||||
else if (this.target instanceof HTMLHtmlElement) {
|
||||
var bodyElt = this.target.ownerDocument.body;
|
||||
@ -748,41 +748,35 @@ nsContextMenu.prototype = {
|
||||
|
||||
// if the document is editable, show context menu like in text inputs
|
||||
if (!this.onEditableArea) {
|
||||
win = this.target.ownerDocument.defaultView;
|
||||
if (win) {
|
||||
var isEditable = false;
|
||||
try {
|
||||
var editingSession = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIEditingSession);
|
||||
if (editingSession.windowIsEditable(win) &&
|
||||
this.getComputedStyle(this.target, "-moz-user-modify") == "read-write") {
|
||||
isEditable = true;
|
||||
}
|
||||
if (editFlags & SpellCheckHelper.CONTENTEDITABLE) {
|
||||
// If this.onEditableArea is false but editFlags is CONTENTEDITABLE, then
|
||||
// the document itself must be editable.
|
||||
this.onTextInput = true;
|
||||
this.onKeywordField = false;
|
||||
this.onImage = false;
|
||||
this.onLoadedImage = false;
|
||||
this.onCompletedImage = false;
|
||||
this.onMathML = false;
|
||||
this.inFrame = false;
|
||||
this.inSrcdocFrame = false;
|
||||
this.hasBGImage = false;
|
||||
this.isDesignMode = true;
|
||||
this.onEditableArea = true;
|
||||
if (gContextMenuContentData) {
|
||||
InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
|
||||
}
|
||||
catch(ex) {
|
||||
// If someone built with composer disabled, we can't get an editing session.
|
||||
}
|
||||
|
||||
if (isEditable) {
|
||||
this.onTextInput = true;
|
||||
this.onKeywordField = false;
|
||||
this.onImage = false;
|
||||
this.onLoadedImage = false;
|
||||
this.onCompletedImage = false;
|
||||
this.onMathML = false;
|
||||
this.inFrame = false;
|
||||
this.inSrcdocFrame = false;
|
||||
this.hasBGImage = false;
|
||||
this.isDesignMode = true;
|
||||
this.onEditableArea = true;
|
||||
InlineSpellCheckerUI.init(editingSession.getEditorForWindow(win));
|
||||
var canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
|
||||
else {
|
||||
var targetWin = this.target.ownerDocument.defaultView;
|
||||
var editingSession = targetWin.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIEditingSession);
|
||||
InlineSpellCheckerUI.init(editingSession.getEditorForWindow(targetWin));
|
||||
InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
|
||||
this.showItem("spell-check-enabled", canSpell);
|
||||
this.showItem("spell-separator", canSpell);
|
||||
}
|
||||
var canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
|
||||
this.showItem("spell-check-enabled", canSpell);
|
||||
this.showItem("spell-separator", canSpell);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1502,30 +1496,6 @@ nsContextMenu.prototype = {
|
||||
return (node instanceof HTMLTextAreaElement);
|
||||
},
|
||||
|
||||
isTargetAKeywordField: function(aNode) {
|
||||
if (!(aNode instanceof HTMLInputElement))
|
||||
return false;
|
||||
|
||||
var form = aNode.form;
|
||||
if (!form || aNode.type == "password")
|
||||
return false;
|
||||
|
||||
var method = form.method.toUpperCase();
|
||||
|
||||
// These are the following types of forms we can create keywords for:
|
||||
//
|
||||
// method encoding type can create keyword
|
||||
// GET * YES
|
||||
// * YES
|
||||
// POST YES
|
||||
// POST application/x-www-form-urlencoded YES
|
||||
// POST text/plain NO (a little tricky to do)
|
||||
// POST multipart/form-data NO
|
||||
// POST everything else YES
|
||||
return (method == "GET" || method == "") ||
|
||||
(form.enctype != "text/plain") && (form.enctype != "multipart/form-data");
|
||||
},
|
||||
|
||||
// Determines whether or not the separator with the specified ID should be
|
||||
// shown or not by determining if there are any non-hidden items between it
|
||||
// and the previous separator.
|
||||
|
@ -3049,8 +3049,13 @@
|
||||
break;
|
||||
}
|
||||
case "contextmenu": {
|
||||
let spellInfo = aMessage.data.spellInfo;
|
||||
if (spellInfo)
|
||||
spellInfo.target = aMessage.target.messageManager;
|
||||
gContextMenuContentData = { event: aMessage.objects.event,
|
||||
browser: browser };
|
||||
browser: browser,
|
||||
editFlags: aMessage.data.editFlags,
|
||||
spellInfo: spellInfo };
|
||||
let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
|
||||
let event = gContextMenuContentData.event;
|
||||
let pos = browser.mapScreenCoordinatesFromContent(event.screenX, event.screenY);
|
||||
|
@ -53,6 +53,7 @@
|
||||
|
||||
#include "mozilla/unused.h"
|
||||
|
||||
#include "mozInlineSpellChecker.h"
|
||||
#include "nsIConsoleListener.h"
|
||||
#include "nsICycleCollectorListener.h"
|
||||
#include "nsIIPCBackgroundChildCreateCallback.h"
|
||||
@ -77,6 +78,7 @@
|
||||
#include "nsIJSRuntimeService.h"
|
||||
#include "nsThreadManager.h"
|
||||
#include "nsAnonymousTemporaryFile.h"
|
||||
#include "nsISpellChecker.h"
|
||||
|
||||
#include "IHistory.h"
|
||||
#include "nsNetUtil.h"
|
||||
@ -693,7 +695,7 @@ ContentChild::InitXPCOM()
|
||||
NS_WARNING("Couldn't register console listener for child process");
|
||||
|
||||
bool isOffline;
|
||||
SendGetXPCOMProcessAttributes(&isOffline);
|
||||
SendGetXPCOMProcessAttributes(&isOffline, &mAvailableDictionaries);
|
||||
RecvSetOffline(isOffline);
|
||||
|
||||
DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
|
||||
@ -1125,6 +1127,12 @@ ContentChild::RecvPBrowserConstructor(PBrowserChild* aActor,
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ContentChild::GetAvailableDictionaries(InfallibleTArray<nsString>& aDictionaries)
|
||||
{
|
||||
aDictionaries = mAvailableDictionaries;
|
||||
}
|
||||
|
||||
PFileDescriptorSetChild*
|
||||
ContentChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD)
|
||||
{
|
||||
@ -1153,7 +1161,7 @@ ContentChild::AllocPBlobChild(const BlobConstructorParams& aParams)
|
||||
mozilla::PRemoteSpellcheckEngineChild *
|
||||
ContentChild::AllocPRemoteSpellcheckEngineChild()
|
||||
{
|
||||
NS_NOTREACHED("Default Constructor for PRemoteSpellcheckEngineChilf should never be called");
|
||||
NS_NOTREACHED("Default Constructor for PRemoteSpellcheckEngineChild should never be called");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -1742,6 +1750,14 @@ ContentChild::RecvGeolocationUpdate(const GeoPosition& somewhere)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvUpdateDictionaryList(const InfallibleTArray<nsString>& aDictionaries)
|
||||
{
|
||||
mAvailableDictionaries = aDictionaries;
|
||||
mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvAddPermission(const IPC::Permission& permission)
|
||||
{
|
||||
|
@ -293,6 +293,8 @@ public:
|
||||
|
||||
virtual bool RecvGeolocationUpdate(const GeoPosition& somewhere) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvUpdateDictionaryList(const InfallibleTArray<nsString>& aDictionaries) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvAddPermission(const IPC::Permission& permission) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvScreenSizeChanged(const gfxIntSize &size) MOZ_OVERRIDE;
|
||||
@ -381,6 +383,8 @@ public:
|
||||
const bool& aIsForApp,
|
||||
const bool& aIsForBrowser) MOZ_OVERRIDE;
|
||||
|
||||
void GetAvailableDictionaries(InfallibleTArray<nsString>& aDictionaries);
|
||||
|
||||
private:
|
||||
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
|
||||
|
||||
@ -397,6 +401,8 @@ private:
|
||||
|
||||
nsTHashtable<nsPtrHashKey<nsIObserver>> mIdleObservers;
|
||||
|
||||
InfallibleTArray<nsString> mAvailableDictionaries;
|
||||
|
||||
/**
|
||||
* An ID unique to the process containing our corresponding
|
||||
* content parent.
|
||||
|
@ -101,6 +101,7 @@
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsISiteSecurityService.h"
|
||||
#include "nsISpellChecker.h"
|
||||
#include "nsIStyleSheet.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsIURIFixup.h"
|
||||
@ -2496,7 +2497,8 @@ ContentParent::RecvAddNewProcess(const uint32_t& aPid,
|
||||
|
||||
// Update offline settings.
|
||||
bool isOffline;
|
||||
RecvGetXPCOMProcessAttributes(&isOffline);
|
||||
InfallibleTArray<nsString> unusedDictionaries;
|
||||
RecvGetXPCOMProcessAttributes(&isOffline, &unusedDictionaries);
|
||||
content->SendSetOffline(isOffline);
|
||||
|
||||
PreallocatedProcessManager::PublishSpareProcess(content);
|
||||
@ -2744,12 +2746,18 @@ ContentParent::RecvGetProcessAttributes(uint64_t* aId,
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline)
|
||||
ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
|
||||
InfallibleTArray<nsString>* dictionaries)
|
||||
{
|
||||
nsCOMPtr<nsIIOService> io(do_GetIOService());
|
||||
NS_ASSERTION(io, "No IO service?");
|
||||
MOZ_ASSERT(io, "No IO service?");
|
||||
DebugOnly<nsresult> rv = io->GetOffline(aIsOffline);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed getting offline?");
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?");
|
||||
|
||||
nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
|
||||
MOZ_ASSERT(spellChecker, "No spell checker?");
|
||||
|
||||
spellChecker->GetDictionaryList(dictionaries);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -4053,6 +4061,23 @@ ContentParent::IgnoreIPCPrincipal()
|
||||
return sIgnoreIPCPrincipal;
|
||||
}
|
||||
|
||||
void
|
||||
ContentParent::NotifyUpdatedDictionaries()
|
||||
{
|
||||
nsAutoTArray<ContentParent*, 8> processes;
|
||||
GetAll(processes);
|
||||
|
||||
nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
|
||||
MOZ_ASSERT(spellChecker, "No spell checker?");
|
||||
|
||||
InfallibleTArray<nsString> dictionaries;
|
||||
spellChecker->GetDictionaryList(&dictionaries);
|
||||
|
||||
for (size_t i = 0; i < processes.Length(); ++i) {
|
||||
unused << processes[i]->SendUpdateDictionaryList(dictionaries);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -139,6 +139,8 @@ public:
|
||||
|
||||
static bool IgnoreIPCPrincipal();
|
||||
|
||||
static void NotifyUpdatedDictionaries();
|
||||
|
||||
virtual bool RecvCreateChildProcess(const IPCTabContext& aContext,
|
||||
const hal::ProcessPriority& aPriority,
|
||||
uint64_t* aId,
|
||||
@ -410,7 +412,9 @@ private:
|
||||
virtual bool RecvGetProcessAttributes(uint64_t* aId,
|
||||
bool* aIsForApp,
|
||||
bool* aIsForBrowser) MOZ_OVERRIDE;
|
||||
virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline) MOZ_OVERRIDE;
|
||||
virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline,
|
||||
InfallibleTArray<nsString>* dictionaries)
|
||||
MOZ_OVERRIDE;
|
||||
|
||||
virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) MOZ_OVERRIDE;
|
||||
|
||||
|
@ -433,6 +433,8 @@ child:
|
||||
|
||||
GeolocationUpdate(GeoPosition somewhere);
|
||||
|
||||
UpdateDictionaryList(nsString[] dictionaries);
|
||||
|
||||
// nsIPermissionManager messages
|
||||
AddPermission(Permission permission);
|
||||
|
||||
@ -442,7 +444,7 @@ child:
|
||||
|
||||
GarbageCollect();
|
||||
CycleCollect();
|
||||
|
||||
|
||||
/**
|
||||
* Start accessibility engine in content process.
|
||||
*/
|
||||
@ -504,7 +506,7 @@ parent:
|
||||
sync GetProcessAttributes()
|
||||
returns (uint64_t id, bool isForApp, bool isForBrowser);
|
||||
sync GetXPCOMProcessAttributes()
|
||||
returns (bool isOffline);
|
||||
returns (bool isOffline, nsString[] dictionaries);
|
||||
|
||||
sync CreateChildProcess(IPCTabContext context,
|
||||
ProcessPriority priority)
|
||||
|
@ -109,7 +109,9 @@ LOCAL_INCLUDES += [
|
||||
'/dom/geolocation',
|
||||
'/dom/mobilemessage/ipc',
|
||||
'/dom/storage',
|
||||
'/editor/libeditor',
|
||||
'/extensions/cookie',
|
||||
'/extensions/spellcheck/src',
|
||||
'/hal/sandbox',
|
||||
'/js/ipc',
|
||||
'/layout/base',
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 sts=2 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/. */
|
||||
@ -46,16 +47,16 @@
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
class UpdateDictionnaryHolder {
|
||||
class UpdateDictionaryHolder {
|
||||
private:
|
||||
nsEditorSpellCheck* mSpellCheck;
|
||||
public:
|
||||
explicit UpdateDictionnaryHolder(nsEditorSpellCheck* esc): mSpellCheck(esc) {
|
||||
explicit UpdateDictionaryHolder(nsEditorSpellCheck* esc): mSpellCheck(esc) {
|
||||
if (mSpellCheck) {
|
||||
mSpellCheck->BeginUpdateDictionary();
|
||||
}
|
||||
}
|
||||
~UpdateDictionnaryHolder() {
|
||||
~UpdateDictionaryHolder() {
|
||||
if (mSpellCheck) {
|
||||
mSpellCheck->EndUpdateDictionary();
|
||||
}
|
||||
@ -699,23 +700,15 @@ nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditorSpellCheckCallback* aCallba
|
||||
}
|
||||
NS_ENSURE_TRUE(rootContent, NS_ERROR_FAILURE);
|
||||
|
||||
DictionaryFetcher* fetcher = new DictionaryFetcher(this, aCallback,
|
||||
mDictionaryFetcherGroup);
|
||||
nsRefPtr<DictionaryFetcher> fetcher =
|
||||
new DictionaryFetcher(this, aCallback, mDictionaryFetcherGroup);
|
||||
rootContent->GetLang(fetcher->mRootContentLang);
|
||||
nsCOMPtr<nsIDocument> doc = rootContent->GetCurrentDoc();
|
||||
NS_ENSURE_STATE(doc);
|
||||
doc->GetContentLanguage(fetcher->mRootDocContentLang);
|
||||
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
// Content prefs don't work in E10S (Bug 1027898) pretend that we
|
||||
// didn't have any & trigger the asynchrous completion.
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethodWithArg<uint16_t>(fetcher, &DictionaryFetcher::HandleCompletion, 0);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
} else {
|
||||
rv = fetcher->Fetch(mEditor);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
rv = fetcher->Fetch(mEditor);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -723,14 +716,13 @@ nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditorSpellCheckCallback* aCallba
|
||||
nsresult
|
||||
nsEditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher)
|
||||
{
|
||||
MOZ_ASSERT(aFetcher);
|
||||
nsRefPtr<nsEditorSpellCheck> kungFuDeathGrip = this;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Important: declare the holder after the callback caller so that the former
|
||||
// is destructed first so that it's not active when the callback is called.
|
||||
CallbackCaller callbackCaller(aFetcher->mCallback);
|
||||
UpdateDictionnaryHolder holder(this);
|
||||
UpdateDictionaryHolder holder(this);
|
||||
|
||||
if (aFetcher->mGroup < mDictionaryFetcherGroup) {
|
||||
// SetCurrentDictionary was called after the fetch started. Don't overwrite
|
||||
@ -742,10 +734,9 @@ nsEditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher)
|
||||
|
||||
// If we successfully fetched a dictionary from content prefs, do not go
|
||||
// further. Use this exact dictionary.
|
||||
nsAutoString dictName;
|
||||
dictName.Assign(aFetcher->mDictionary);
|
||||
nsAutoString dictName(aFetcher->mDictionary);
|
||||
if (!dictName.IsEmpty()) {
|
||||
if (NS_FAILED(SetCurrentDictionary(dictName))) {
|
||||
if (NS_FAILED(SetCurrentDictionary(dictName))) {
|
||||
// may be dictionary was uninstalled ?
|
||||
ClearCurrentDictionary(mEditor);
|
||||
}
|
||||
@ -773,8 +764,8 @@ nsEditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher)
|
||||
dictName.Assign(preferedDict);
|
||||
}
|
||||
|
||||
if (dictName.IsEmpty())
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
if (dictName.IsEmpty()) {
|
||||
// Prefs didn't give us a dictionary name, so just get the current
|
||||
// locale and use that as the default dictionary name!
|
||||
|
||||
|
@ -6,17 +6,17 @@ include protocol PContent;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
rpc protocol PRemoteSpellcheckEngine {
|
||||
sync protocol PRemoteSpellcheckEngine {
|
||||
manager PContent;
|
||||
|
||||
parent:
|
||||
__delete__();
|
||||
|
||||
rpc Check(nsString aWord) returns (bool aIsMisspelled);
|
||||
sync Check(nsString aWord) returns (bool aIsMisspelled);
|
||||
|
||||
rpc CheckAndSuggest(nsString aWord) returns (bool aIsMisspelled, nsString[] aSuggestions);
|
||||
sync CheckAndSuggest(nsString aWord) returns (bool aIsMisspelled, nsString[] aSuggestions);
|
||||
|
||||
rpc SetDictionary(nsString aDictionary) returns (bool success);
|
||||
sync SetDictionary(nsString aDictionary) returns (bool success);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -5,8 +5,9 @@
|
||||
#include "RemoteSpellCheckEngineChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
RemoteSpellcheckEngineChild::RemoteSpellcheckEngineChild(mozSpellChecker *aOwner)
|
||||
:mOwner(aOwner)
|
||||
: mOwner(aOwner)
|
||||
{
|
||||
}
|
||||
|
||||
@ -15,7 +16,6 @@ RemoteSpellcheckEngineChild::~RemoteSpellcheckEngineChild()
|
||||
// null out the owner's SpellcheckEngineChild to prevent state corruption
|
||||
// during shutdown
|
||||
mOwner->DeleteRemoteEngine();
|
||||
|
||||
}
|
||||
|
||||
} //namespace mozilla
|
||||
|
@ -11,11 +11,12 @@
|
||||
class mozSpellChecker;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class RemoteSpellcheckEngineChild : public mozilla::PRemoteSpellcheckEngineChild
|
||||
{
|
||||
public:
|
||||
explicit RemoteSpellcheckEngineChild(mozSpellChecker *aOwner);
|
||||
~RemoteSpellcheckEngineChild();
|
||||
virtual ~RemoteSpellcheckEngineChild();
|
||||
|
||||
private:
|
||||
mozSpellChecker *mOwner;
|
||||
|
@ -1,18 +1,17 @@
|
||||
/* 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 "mozISpellCheckingEngine.h"
|
||||
#include "nsISpellChecker.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#define DEFAULT_SPELL_CHECKER "@mozilla.org/spellchecker/engine;1"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
RemoteSpellcheckEngineParent::RemoteSpellcheckEngineParent()
|
||||
{
|
||||
mEngine = do_GetService(DEFAULT_SPELL_CHECKER);
|
||||
mSpellChecker = do_CreateInstance(NS_SPELLCHECKER_CONTRACTID);
|
||||
}
|
||||
|
||||
RemoteSpellcheckEngineParent::~RemoteSpellcheckEngineParent()
|
||||
@ -20,43 +19,38 @@ RemoteSpellcheckEngineParent::~RemoteSpellcheckEngineParent()
|
||||
}
|
||||
|
||||
bool
|
||||
RemoteSpellcheckEngineParent::AnswerSetDictionary(
|
||||
RemoteSpellcheckEngineParent::RecvSetDictionary(
|
||||
const nsString& aDictionary,
|
||||
bool* success)
|
||||
{
|
||||
nsresult rv = mEngine->SetDictionary(aDictionary.get());
|
||||
nsresult rv = mSpellChecker->SetCurrentDictionary(aDictionary);
|
||||
*success = NS_SUCCEEDED(rv);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RemoteSpellcheckEngineParent::AnswerCheck(
|
||||
RemoteSpellcheckEngineParent::RecvCheck(
|
||||
const nsString& aWord,
|
||||
bool* aIsMisspelled)
|
||||
{
|
||||
bool isCorrect = true;
|
||||
mEngine->Check(aWord.get(), &isCorrect);
|
||||
*aIsMisspelled = !isCorrect;
|
||||
nsresult rv = mSpellChecker->CheckWord(aWord, aIsMisspelled, nullptr);
|
||||
|
||||
// If CheckWord failed, we can't tell whether the word is correctly spelled.
|
||||
if (NS_FAILED(rv))
|
||||
*aIsMisspelled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RemoteSpellcheckEngineParent::AnswerCheckAndSuggest(
|
||||
RemoteSpellcheckEngineParent::RecvCheckAndSuggest(
|
||||
const nsString& aWord,
|
||||
bool* aIsMisspelled,
|
||||
InfallibleTArray<nsString>* aSuggestions)
|
||||
{
|
||||
bool isCorrect = true;
|
||||
mEngine->Check(aWord.get(), &isCorrect);
|
||||
*aIsMisspelled = !isCorrect;
|
||||
if (!isCorrect) {
|
||||
char16_t **suggestions;
|
||||
uint32_t count = 0;
|
||||
mEngine->Suggest(aWord.get(), &suggestions, &count);
|
||||
|
||||
for (uint32_t i=0; i<count; i++) {
|
||||
aSuggestions->AppendElement(nsDependentString(suggestions[i]));
|
||||
}
|
||||
nsresult rv = mSpellChecker->CheckWord(aWord, aIsMisspelled, aSuggestions);
|
||||
if (NS_FAILED(rv)) {
|
||||
aSuggestions->Clear();
|
||||
*aIsMisspelled = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -67,4 +61,3 @@ RemoteSpellcheckEngineParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -4,34 +4,34 @@
|
||||
#ifndef RemoteSpellcheckEngineParent_h_
|
||||
#define RemoteSpellcheckEngineParent_h_
|
||||
|
||||
#include "mozISpellCheckingEngine.h"
|
||||
#include "mozilla/PRemoteSpellcheckEngineParent.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsISpellChecker;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class RemoteSpellcheckEngineParent : public mozilla::PRemoteSpellcheckEngineParent {
|
||||
|
||||
class RemoteSpellcheckEngineParent : public PRemoteSpellcheckEngineParent
|
||||
{
|
||||
public:
|
||||
RemoteSpellcheckEngineParent();
|
||||
|
||||
~RemoteSpellcheckEngineParent();
|
||||
virtual ~RemoteSpellcheckEngineParent();
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy);
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
|
||||
|
||||
bool AnswerSetDictionary(const nsString& aDictionary, bool* success);
|
||||
|
||||
bool AnswerCheck( const nsString& aWord, bool* aIsMisspelled);
|
||||
|
||||
bool AnswerCheckAndSuggest(
|
||||
const nsString& aWord,
|
||||
bool* aIsMisspelled,
|
||||
InfallibleTArray<nsString>* aSuggestions);
|
||||
virtual bool RecvSetDictionary(const nsString& aDictionary,
|
||||
bool* success) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvCheck(const nsString& aWord, bool* aIsMisspelled) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvCheckAndSuggest(const nsString& aWord,
|
||||
bool* aIsMisspelled,
|
||||
InfallibleTArray<nsString>* aSuggestions)
|
||||
MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
nsCOMPtr<mozISpellCheckingEngine> mEngine;
|
||||
nsCOMPtr<nsISpellChecker> mSpellChecker;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -76,7 +76,9 @@
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
|
||||
using mozilla::dom::ContentParent;
|
||||
using mozilla::dom::EncodingUtils;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(mozHunspell)
|
||||
@ -113,7 +115,7 @@ mozHunspell::mozHunspell()
|
||||
nsresult
|
||||
mozHunspell::Init()
|
||||
{
|
||||
LoadDictionaryList();
|
||||
LoadDictionaryList(false);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
@ -344,7 +346,7 @@ NS_IMETHODIMP mozHunspell::GetDictionaryList(char16_t ***aDictionaries,
|
||||
}
|
||||
|
||||
void
|
||||
mozHunspell::LoadDictionaryList()
|
||||
mozHunspell::LoadDictionaryList(bool aNotifyChildProcesses)
|
||||
{
|
||||
mDictionaries.Clear();
|
||||
|
||||
@ -424,6 +426,10 @@ mozHunspell::LoadDictionaryList()
|
||||
// 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()) {
|
||||
@ -589,7 +595,7 @@ mozHunspell::Observe(nsISupports* aSubj, const char *aTopic,
|
||||
|| !strcmp(aTopic, "profile-after-change"),
|
||||
"Unexpected observer topic");
|
||||
|
||||
LoadDictionaryList();
|
||||
LoadDictionaryList(false);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -598,7 +604,7 @@ mozHunspell::Observe(nsISupports* aSubj, const char *aTopic,
|
||||
NS_IMETHODIMP mozHunspell::AddDirectory(nsIFile *aDir)
|
||||
{
|
||||
mDynamicDirectories.AppendObject(aDir);
|
||||
LoadDictionaryList();
|
||||
LoadDictionaryList(true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -606,6 +612,6 @@ NS_IMETHODIMP mozHunspell::AddDirectory(nsIFile *aDir)
|
||||
NS_IMETHODIMP mozHunspell::RemoveDirectory(nsIFile *aDir)
|
||||
{
|
||||
mDynamicDirectories.RemoveObject(aDir);
|
||||
LoadDictionaryList();
|
||||
LoadDictionaryList(true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ public:
|
||||
|
||||
nsresult Init();
|
||||
|
||||
void LoadDictionaryList();
|
||||
void LoadDictionaryList(bool aNotifyChildProcesses);
|
||||
|
||||
// helper method for converting a word to the charset of the dictionary
|
||||
nsresult ConvertCharset(const char16_t* aStr, char ** aDst);
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* vim: set ts=2 sts=2 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/. */
|
||||
@ -34,38 +35,41 @@ NS_IMPL_CYCLE_COLLECTION(mozSpellChecker,
|
||||
mPersonalDictionary)
|
||||
|
||||
mozSpellChecker::mozSpellChecker()
|
||||
: mEngine(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
mozSpellChecker::~mozSpellChecker()
|
||||
{
|
||||
if(mPersonalDictionary){
|
||||
if (mPersonalDictionary) {
|
||||
// mPersonalDictionary->Save();
|
||||
mPersonalDictionary->EndSession();
|
||||
}
|
||||
mSpellCheckingEngine = nullptr;
|
||||
mPersonalDictionary = nullptr;
|
||||
|
||||
if(XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
if (mEngine) {
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
|
||||
mEngine->Send__delete__(mEngine);
|
||||
MOZ_ASSERT(!mEngine);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsresult
|
||||
mozSpellChecker::Init()
|
||||
{
|
||||
mPersonalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1");
|
||||
|
||||
mSpellCheckingEngine = nullptr;
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
mozilla::dom::ContentChild* contentChild = mozilla::dom::ContentChild::GetSingleton();
|
||||
MOZ_ASSERT(contentChild);
|
||||
mEngine = new RemoteSpellcheckEngineChild(this);
|
||||
contentChild->SendPRemoteSpellcheckEngineConstructor(mEngine);
|
||||
} else {
|
||||
mPersonalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
mozSpellChecker::SetDocument(nsITextServicesDocument *aDoc, bool aFromStartofDoc)
|
||||
@ -130,9 +134,9 @@ mozSpellChecker::CheckWord(const nsAString &aWord, bool *aIsMisspelled, nsTArray
|
||||
nsString wordwrapped = nsString(aWord);
|
||||
bool rv;
|
||||
if (aSuggestions) {
|
||||
rv = mEngine->CallCheckAndSuggest(wordwrapped, aIsMisspelled, aSuggestions);
|
||||
rv = mEngine->SendCheckAndSuggest(wordwrapped, aIsMisspelled, aSuggestions);
|
||||
} else {
|
||||
rv = mEngine->CallCheck(wordwrapped, aIsMisspelled);
|
||||
rv = mEngine->SendCheck(wordwrapped, aIsMisspelled);
|
||||
}
|
||||
return rv ? NS_OK : NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
@ -302,9 +306,15 @@ mozSpellChecker::GetPersonalDictionary(nsTArray<nsString> *aWordList)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NS_IMETHODIMP
|
||||
mozSpellChecker::GetDictionaryList(nsTArray<nsString> *aDictionaryList)
|
||||
{
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
ContentChild *child = ContentChild::GetSingleton();
|
||||
child->GetAvailableDictionaries(*aDictionaryList);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// For catching duplicates
|
||||
@ -344,9 +354,14 @@ mozSpellChecker::GetDictionaryList(nsTArray<nsString> *aDictionaryList)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NS_IMETHODIMP
|
||||
mozSpellChecker::GetCurrentDictionary(nsAString &aDictionary)
|
||||
{
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
aDictionary = mCurrentDictionary;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mSpellCheckingEngine) {
|
||||
aDictionary.Truncate();
|
||||
return NS_OK;
|
||||
@ -358,14 +373,20 @@ mozSpellChecker::GetCurrentDictionary(nsAString &aDictionary)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NS_IMETHODIMP
|
||||
mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary)
|
||||
{
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
nsString wrappedDict = nsString(aDictionary);
|
||||
bool isSuccess;
|
||||
mEngine->CallSetDictionary(wrappedDict, &isSuccess);
|
||||
return isSuccess ? NS_OK : NS_ERROR_NOT_AVAILABLE;
|
||||
mEngine->SendSetDictionary(wrappedDict, &isSuccess);
|
||||
if (!isSuccess) {
|
||||
mCurrentDictionary.Truncate();
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
mCurrentDictionary = wrappedDict;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Calls to mozISpellCheckingEngine::SetDictionary might destroy us
|
||||
@ -402,7 +423,7 @@ mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary)
|
||||
}
|
||||
|
||||
mSpellCheckingEngine = nullptr;
|
||||
|
||||
|
||||
// We could not find any engine with the requested dictionary
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
@ -515,6 +536,8 @@ mozSpellChecker::GetCurrentBlockIndex(nsITextServicesDocument *aDoc, int32_t *ou
|
||||
nsresult
|
||||
mozSpellChecker::GetEngineList(nsCOMArray<mozISpellCheckingEngine>* aSpellCheckingEngines)
|
||||
{
|
||||
MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Content);
|
||||
|
||||
nsresult rv;
|
||||
bool hasMoreEngines;
|
||||
|
||||
@ -564,7 +587,3 @@ mozSpellChecker::GetEngineList(nsCOMArray<mozISpellCheckingEngine>* aSpellChecki
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void mozSpellChecker::DeleteRemoteEngine() {
|
||||
mEngine = nullptr;
|
||||
}
|
||||
|
@ -49,7 +49,10 @@ public:
|
||||
NS_IMETHOD GetCurrentDictionary(nsAString &aDictionary);
|
||||
NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary);
|
||||
NS_IMETHOD CheckCurrentDictionary();
|
||||
void DeleteRemoteEngine();
|
||||
|
||||
void DeleteRemoteEngine() {
|
||||
mEngine = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~mozSpellChecker();
|
||||
@ -61,6 +64,8 @@ protected:
|
||||
nsCOMPtr<mozISpellCheckingEngine> mSpellCheckingEngine;
|
||||
bool mFromStart;
|
||||
|
||||
nsString mCurrentDictionary;
|
||||
|
||||
nsresult SetupDoc(int32_t *outBlockOffset);
|
||||
|
||||
nsresult GetCurrentBlockIndex(nsITextServicesDocument *aDoc, int32_t *outBlockIndex);
|
||||
|
@ -538,7 +538,7 @@
|
||||
<xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"/>
|
||||
<xul:menuseparator anonid="spell-check-separator"/>
|
||||
<xul:menuitem label="&spellCheckToggle.label;" type="checkbox" accesskey="&spellCheckToggle.accesskey;" anonid="spell-check-enabled"
|
||||
oncommand="this.parentNode.parentNode.spellCheckerUI.toggleEnabled(window);"/>
|
||||
oncommand="this.parentNode.parentNode.spellCheckerUI.toggleEnabled();"/>
|
||||
<xul:menu label="&spellDictionaries.label;" accesskey="&spellDictionaries.accesskey;" anonid="spell-dictionaries">
|
||||
<xul:menupopup anonid="spell-dictionaries-menu"
|
||||
onpopupshowing="event.stopPropagation();"
|
||||
|
@ -2,11 +2,16 @@
|
||||
* 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/. */
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "InlineSpellChecker" ];
|
||||
this.EXPORTED_SYMBOLS = [ "InlineSpellChecker",
|
||||
"SpellCheckHelper" ];
|
||||
var gLanguageBundle;
|
||||
var gRegionBundle;
|
||||
const MAX_UNDO_STACK_DEPTH = 1;
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
this.InlineSpellChecker = function InlineSpellChecker(aEditor) {
|
||||
this.init(aEditor);
|
||||
this.mAddedWordStack = []; // We init this here to preserve it between init/uninit calls
|
||||
@ -26,9 +31,27 @@ InlineSpellChecker.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
initFromRemote: function(aSpellInfo)
|
||||
{
|
||||
if (this.mRemote)
|
||||
throw new Error("Unexpected state");
|
||||
this.uninit();
|
||||
|
||||
if (!aSpellInfo)
|
||||
return;
|
||||
this.mInlineSpellChecker = this.mRemote = new RemoteSpellChecker(aSpellInfo);
|
||||
this.mOverMisspelling = aSpellInfo.overMisspelling;
|
||||
this.mMisspelling = aSpellInfo.misspelling;
|
||||
},
|
||||
|
||||
// call this to clear state
|
||||
uninit: function()
|
||||
{
|
||||
if (this.mRemote) {
|
||||
this.mRemote.uninit();
|
||||
this.mRemote = null;
|
||||
}
|
||||
|
||||
this.mEditor = null;
|
||||
this.mInlineSpellChecker = null;
|
||||
this.mOverMisspelling = false;
|
||||
@ -73,10 +96,15 @@ InlineSpellChecker.prototype = {
|
||||
{
|
||||
// inline spell checker objects will be created only if there are actual
|
||||
// dictionaries available
|
||||
return (this.mInlineSpellChecker != null);
|
||||
if (this.mRemote)
|
||||
return this.mRemote.canSpellCheck;
|
||||
return this.mInlineSpellChecker != null;
|
||||
},
|
||||
|
||||
get initialSpellCheckPending() {
|
||||
if (this.mRemote) {
|
||||
return this.mRemote.spellCheckPending;
|
||||
}
|
||||
return !!(this.mInlineSpellChecker &&
|
||||
!this.mInlineSpellChecker.spellChecker &&
|
||||
this.mInlineSpellChecker.spellCheckPending);
|
||||
@ -85,12 +113,16 @@ InlineSpellChecker.prototype = {
|
||||
// Whether spellchecking is enabled in the current box
|
||||
get enabled()
|
||||
{
|
||||
if (this.mRemote)
|
||||
return this.mRemote.enableRealTimeSpell;
|
||||
return (this.mInlineSpellChecker &&
|
||||
this.mInlineSpellChecker.enableRealTimeSpell);
|
||||
},
|
||||
set enabled(isEnabled)
|
||||
{
|
||||
if (this.mInlineSpellChecker)
|
||||
if (this.mRemote)
|
||||
this.mRemote.setSpellcheckUserOverride(isEnabled);
|
||||
else if (this.mInlineSpellChecker)
|
||||
this.mEditor.setSpellcheckUserOverride(isEnabled);
|
||||
},
|
||||
|
||||
@ -104,12 +136,12 @@ InlineSpellChecker.prototype = {
|
||||
// for the word under the cursor. Returns the number of suggestions inserted.
|
||||
addSuggestionsToMenu: function(menu, insertBefore, maxNumber)
|
||||
{
|
||||
if (! this.mInlineSpellChecker || ! this.mOverMisspelling)
|
||||
if (!this.mRemote && (!this.mInlineSpellChecker || !this.mOverMisspelling))
|
||||
return 0; // nothing to do
|
||||
|
||||
var spellchecker = this.mInlineSpellChecker.spellChecker;
|
||||
var spellchecker = this.mRemote || this.mInlineSpellChecker.spellChecker;
|
||||
try {
|
||||
if (! spellchecker.CheckCurrentWord(this.mMisspelling))
|
||||
if (!this.mRemote && !spellchecker.CheckCurrentWord(this.mMisspelling))
|
||||
return 0; // word seems not misspelled after all (?)
|
||||
} catch(e) {
|
||||
return 0;
|
||||
@ -148,31 +180,7 @@ InlineSpellChecker.prototype = {
|
||||
this.mSuggestionItems = [];
|
||||
},
|
||||
|
||||
// returns the number of dictionary languages. If insertBefore is NULL, this
|
||||
// does an append to the given menu
|
||||
addDictionaryListToMenu: function(menu, insertBefore)
|
||||
{
|
||||
this.mDictionaryMenu = menu;
|
||||
this.mDictionaryNames = [];
|
||||
this.mDictionaryItems = [];
|
||||
|
||||
if (! this.mInlineSpellChecker || ! this.enabled)
|
||||
return 0;
|
||||
var spellchecker = this.mInlineSpellChecker.spellChecker;
|
||||
|
||||
// Cannot access the dictionary list from another process so just return 0.
|
||||
if (Components.utils.isCrossProcessWrapper(spellchecker))
|
||||
return 0;
|
||||
|
||||
var o1 = {}, o2 = {};
|
||||
spellchecker.GetDictionaryList(o1, o2);
|
||||
var list = o1.value;
|
||||
var listcount = o2.value;
|
||||
var curlang = "";
|
||||
try {
|
||||
curlang = spellchecker.GetCurrentDictionary();
|
||||
} catch(e) {}
|
||||
|
||||
sortDictionaryList: function(list) {
|
||||
var sortedList = [];
|
||||
for (var i = 0; i < list.length; i ++) {
|
||||
sortedList.push({"id": list[i],
|
||||
@ -186,6 +194,39 @@ InlineSpellChecker.prototype = {
|
||||
return 0;
|
||||
});
|
||||
|
||||
return sortedList;
|
||||
},
|
||||
|
||||
// returns the number of dictionary languages. If insertBefore is NULL, this
|
||||
// does an append to the given menu
|
||||
addDictionaryListToMenu: function(menu, insertBefore)
|
||||
{
|
||||
this.mDictionaryMenu = menu;
|
||||
this.mDictionaryNames = [];
|
||||
this.mDictionaryItems = [];
|
||||
|
||||
if (!this.enabled)
|
||||
return 0;
|
||||
|
||||
var list;
|
||||
var curlang = "";
|
||||
if (this.mRemote) {
|
||||
list = this.mRemote.dictionaryList;
|
||||
curlang = this.mRemote.currentDictionary;
|
||||
}
|
||||
else if (this.mInlineSpellChecker) {
|
||||
var spellchecker = this.mInlineSpellChecker.spellChecker;
|
||||
var o1 = {}, o2 = {};
|
||||
spellchecker.GetDictionaryList(o1, o2);
|
||||
list = o1.value;
|
||||
var listcount = o2.value;
|
||||
try {
|
||||
curlang = spellchecker.GetCurrentDictionary();
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
var sortedList = this.sortDictionaryList(list);
|
||||
|
||||
for (var i = 0; i < sortedList.length; i ++) {
|
||||
this.mDictionaryNames.push(sortedList[i].id);
|
||||
var item = menu.ownerDocument.createElement("menuitem");
|
||||
@ -198,7 +239,7 @@ InlineSpellChecker.prototype = {
|
||||
} else {
|
||||
var callback = function(me, val) {
|
||||
return function(evt) {
|
||||
me.selectDictionary(val, menu.ownerDocument.defaultView);
|
||||
me.selectDictionary(val);
|
||||
}
|
||||
};
|
||||
item.addEventListener("command", callback(this, i), true);
|
||||
@ -281,18 +322,10 @@ InlineSpellChecker.prototype = {
|
||||
},
|
||||
|
||||
// callback for selecting a dictionary
|
||||
selectDictionary: function(index, aWindow)
|
||||
selectDictionary: function(index)
|
||||
{
|
||||
// Avoid a crash in multiprocess until Bug 1030451 lands
|
||||
// Remove aWindow parameter at that time
|
||||
const Ci = Components.interfaces;
|
||||
let chromeFlags = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIWebNavigation).
|
||||
QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
|
||||
QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIXULWindow).chromeFlags;
|
||||
let chromeRemoteWindow = Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW;
|
||||
if (chromeFlags & chromeRemoteWindow) {
|
||||
if (this.mRemote) {
|
||||
this.mRemote.selectDictionary(index);
|
||||
return;
|
||||
}
|
||||
if (! this.mInlineSpellChecker || index < 0 || index >= this.mDictionaryNames.length)
|
||||
@ -305,6 +338,10 @@ InlineSpellChecker.prototype = {
|
||||
// callback for selecting a suggesteed replacement
|
||||
replaceMisspelling: function(index)
|
||||
{
|
||||
if (this.mRemote) {
|
||||
this.mRemote.replaceMisspelling(index);
|
||||
return;
|
||||
}
|
||||
if (! this.mInlineSpellChecker || ! this.mOverMisspelling)
|
||||
return;
|
||||
if (index < 0 || index >= this.mSpellSuggestions.length)
|
||||
@ -314,21 +351,12 @@ InlineSpellChecker.prototype = {
|
||||
},
|
||||
|
||||
// callback for enabling or disabling spellchecking
|
||||
toggleEnabled: function(aWindow)
|
||||
toggleEnabled: function()
|
||||
{
|
||||
// Avoid a crash in multiprocess until Bug 1030451 lands
|
||||
// Remove aWindow parameter at that time
|
||||
const Ci = Components.interfaces;
|
||||
let chromeFlags = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIWebNavigation).
|
||||
QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
|
||||
QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIXULWindow).chromeFlags;
|
||||
let chromeRemoteWindow = Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW;
|
||||
if (chromeFlags & chromeRemoteWindow) {
|
||||
return;
|
||||
}
|
||||
this.mEditor.setSpellcheckUserOverride(!this.mInlineSpellChecker.enableRealTimeSpell);
|
||||
if (this.mRemote)
|
||||
this.mRemote.toggleEnabled();
|
||||
else
|
||||
this.mEditor.setSpellcheckUserOverride(!this.mInlineSpellChecker.enableRealTimeSpell);
|
||||
},
|
||||
|
||||
// callback for adding the current misspelling to the user-defined dictionary
|
||||
@ -339,7 +367,11 @@ InlineSpellChecker.prototype = {
|
||||
this.mAddedWordStack.shift();
|
||||
|
||||
this.mAddedWordStack.push(this.mMisspelling);
|
||||
this.mInlineSpellChecker.addWordToDictionary(this.mMisspelling);
|
||||
if (this.mRemote)
|
||||
this.mRemote.addToDictionary();
|
||||
else {
|
||||
this.mInlineSpellChecker.addWordToDictionary(this.mMisspelling);
|
||||
}
|
||||
},
|
||||
// callback for removing the last added word to the dictionary LIFO fashion
|
||||
undoAddToDictionary: function()
|
||||
@ -347,7 +379,10 @@ InlineSpellChecker.prototype = {
|
||||
if (this.mAddedWordStack.length > 0)
|
||||
{
|
||||
var word = this.mAddedWordStack.pop();
|
||||
this.mInlineSpellChecker.removeWordFromDictionary(word);
|
||||
if (this.mRemote)
|
||||
this.mRemote.undoAddToDictionary(word);
|
||||
else
|
||||
this.mInlineSpellChecker.removeWordFromDictionary(word);
|
||||
}
|
||||
},
|
||||
canUndo : function()
|
||||
@ -357,6 +392,182 @@ InlineSpellChecker.prototype = {
|
||||
},
|
||||
ignoreWord: function()
|
||||
{
|
||||
this.mInlineSpellChecker.ignoreWord(this.mMisspelling);
|
||||
if (this.mRemote)
|
||||
this.mRemote.ignoreWord();
|
||||
else
|
||||
this.mInlineSpellChecker.ignoreWord(this.mMisspelling);
|
||||
}
|
||||
};
|
||||
|
||||
var SpellCheckHelper = {
|
||||
// Set when over a non-read-only <textarea> or editable <input>.
|
||||
EDITABLE: 0x1,
|
||||
|
||||
// Set when over an <input> element of any type.
|
||||
INPUT: 0x2,
|
||||
|
||||
// Set when over any <textarea>.
|
||||
TEXTAREA: 0x4,
|
||||
|
||||
// Set when over any text-entry <input>.
|
||||
TEXTINPUT: 0x8,
|
||||
|
||||
// Set when over an <input> that can be used as a keyword field.
|
||||
KEYWORD: 0x10,
|
||||
|
||||
// Set when over an element that otherwise would not be considered
|
||||
// "editable" but is because content editable is enabled for the document.
|
||||
CONTENTEDITABLE: 0x20,
|
||||
|
||||
isTargetAKeywordField(aNode, window) {
|
||||
if (!(aNode instanceof window.HTMLInputElement))
|
||||
return false;
|
||||
|
||||
var form = aNode.form;
|
||||
if (!form || aNode.type == "password")
|
||||
return false;
|
||||
|
||||
var method = form.method.toUpperCase();
|
||||
|
||||
// These are the following types of forms we can create keywords for:
|
||||
//
|
||||
// method encoding type can create keyword
|
||||
// GET * YES
|
||||
// * YES
|
||||
// POST YES
|
||||
// POST application/x-www-form-urlencoded YES
|
||||
// POST text/plain NO (a little tricky to do)
|
||||
// POST multipart/form-data NO
|
||||
// POST everything else YES
|
||||
return (method == "GET" || method == "") ||
|
||||
(form.enctype != "text/plain") && (form.enctype != "multipart/form-data");
|
||||
},
|
||||
|
||||
// Returns the computed style attribute for the given element.
|
||||
getComputedStyle(aElem, aProp) {
|
||||
return aElem.ownerDocument
|
||||
.defaultView
|
||||
.getComputedStyle(aElem, "").getPropertyValue(aProp);
|
||||
},
|
||||
|
||||
isEditable(element, window) {
|
||||
var flags = 0;
|
||||
if (element instanceof window.HTMLInputElement) {
|
||||
flags |= this.INPUT;
|
||||
if (element.mozIsTextField(false)) {
|
||||
flags |= this.TEXTINPUT;
|
||||
|
||||
// Allow spellchecking UI on all text and search inputs.
|
||||
if (!element.readOnly &&
|
||||
(element.type == "text" || element.type == "search")) {
|
||||
flags |= this.EDITABLE;
|
||||
}
|
||||
if (this.isTargetAKeywordField(element, window))
|
||||
flags |= this.KEYWORD;
|
||||
}
|
||||
} else if (element instanceof window.HTMLTextAreaElement) {
|
||||
flags |= this.TEXTINPUT | this.TEXTAREA;
|
||||
if (!element.readOnly) {
|
||||
flags |= this.EDITABLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & this.EDITABLE)) {
|
||||
var win = element.ownerDocument.defaultView;
|
||||
if (win) {
|
||||
var isEditable = false;
|
||||
try {
|
||||
var editingSession = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIEditingSession);
|
||||
if (editingSession.windowIsEditable(win) &&
|
||||
this.getComputedStyle(element, "-moz-user-modify") == "read-write") {
|
||||
isEditable = true;
|
||||
}
|
||||
}
|
||||
catch(ex) {
|
||||
// If someone built with composer disabled, we can't get an editing session.
|
||||
}
|
||||
|
||||
if (isEditable)
|
||||
flags |= this.CONTENTEDITABLE;
|
||||
}
|
||||
}
|
||||
|
||||
return flags;
|
||||
},
|
||||
};
|
||||
|
||||
function RemoteSpellChecker(aSpellInfo) {
|
||||
this._spellInfo = aSpellInfo;
|
||||
this._suggestionGenerator = null;
|
||||
}
|
||||
|
||||
RemoteSpellChecker.prototype = {
|
||||
get canSpellCheck() { return this._spellInfo.canSpellCheck; },
|
||||
get spellCheckPending() { return this._spellInfo.initialSpellCheckPending; },
|
||||
get overMisspelling() { return this._spellInfo.overMisspelling; },
|
||||
get enableRealTimeSpell() { return this._spellInfo.enableRealTimeSpell; },
|
||||
|
||||
GetSuggestedWord() {
|
||||
if (!this._suggestionGenerator) {
|
||||
this._suggestionGenerator = (function*(spellInfo) {
|
||||
for (let i of spellInfo.spellSuggestions)
|
||||
yield i;
|
||||
})(this._spellInfo);
|
||||
}
|
||||
|
||||
let next = this._suggestionGenerator.next();
|
||||
if (next.done) {
|
||||
this._suggestionGenerator = null;
|
||||
return "";
|
||||
}
|
||||
return next.value;
|
||||
},
|
||||
|
||||
get currentDictionary() { return this._spellInfo.currentDictionary },
|
||||
get dictionaryList() { return this._spellInfo.dictionaryList.slice(); },
|
||||
|
||||
selectDictionary(index) {
|
||||
this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:selectDictionary",
|
||||
{ index });
|
||||
},
|
||||
|
||||
replaceMisspelling(index) {
|
||||
this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:replaceMisspelling",
|
||||
{ index });
|
||||
},
|
||||
|
||||
toggleEnabled() { this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:toggleEnabled", {}); },
|
||||
addToDictionary() {
|
||||
// This is really ugly. There is an nsISpellChecker somewhere in the
|
||||
// parent that corresponds to our current element's spell checker in the
|
||||
// child, but it's hard to access it. However, we know that
|
||||
// addToDictionary adds the word to the singleton personal dictionary, so
|
||||
// we just do that here.
|
||||
// NB: We also rely on the fact that we only ever pass an empty string in
|
||||
// as the "lang".
|
||||
|
||||
let dictionary = Cc["@mozilla.org/spellchecker/personaldictionary;1"]
|
||||
.getService(Ci.mozIPersonalDictionary);
|
||||
dictionary.addWord(this._spellInfo.misspelling, "");
|
||||
|
||||
this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:recheck", {});
|
||||
},
|
||||
undoAddToDictionary(word) {
|
||||
let dictionary = Cc["@mozilla.org/spellchecker/personaldictionary;1"]
|
||||
.getService(Ci.mozIPersonalDictionary);
|
||||
dictionary.removeWord(word, "");
|
||||
|
||||
this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:recheck", {});
|
||||
},
|
||||
ignoreWord() {
|
||||
let dictionary = Cc["@mozilla.org/spellchecker/personaldictionary;1"]
|
||||
.getService(Ci.mozIPersonalDictionary);
|
||||
dictionary.ignoreWord(this._spellInfo.misspelling);
|
||||
|
||||
this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:recheck", {});
|
||||
},
|
||||
uninit() { this._spellInfo.target.sendAsyncMessage("InlineSpellChecker:uninit", {}); }
|
||||
};
|
||||
|
141
toolkit/modules/InlineSpellCheckerContent.jsm
Normal file
141
toolkit/modules/InlineSpellCheckerContent.jsm
Normal file
@ -0,0 +1,141 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
let { SpellCheckHelper } = Cu.import("resource://gre/modules/InlineSpellChecker.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "InlineSpellCheckerContent" ]
|
||||
|
||||
var InlineSpellCheckerContent = {
|
||||
_spellChecker: null,
|
||||
_manager: null,
|
||||
|
||||
initContextMenu(event, editFlags, messageManager) {
|
||||
this._manager = messageManager;
|
||||
|
||||
let spellChecker;
|
||||
if (!(editFlags & (SpellCheckHelper.TEXTAREA | SpellCheckHelper.INPUT))) {
|
||||
// Get the editor off the window.
|
||||
let win = event.target.ownerDocument.defaultView;
|
||||
let editingSession = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIEditingSession);
|
||||
spellChecker = this._spellChecker =
|
||||
new InlineSpellChecker(editingSession.getEditorForWindow(win));
|
||||
} else {
|
||||
// Use the element's editor.
|
||||
spellChecker = this._spellChecker =
|
||||
new InlineSpellChecker(event.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
|
||||
}
|
||||
|
||||
this._spellChecker.initFromEvent(event.rangeParent, event.rangeOffset)
|
||||
|
||||
this._addMessageListeners();
|
||||
|
||||
if (!spellChecker.canSpellCheck) {
|
||||
return { canSpellCheck: false,
|
||||
initialSpellCheckPending: true,
|
||||
enableRealTimeSpell: false };
|
||||
}
|
||||
|
||||
if (!spellChecker.mInlineSpellChecker.enableRealTimeSpell) {
|
||||
return { canSpellCheck: true,
|
||||
initialSpellCheckPending: spellChecker.initialSpellCheckPending,
|
||||
enableRealTimeSpell: false };
|
||||
}
|
||||
|
||||
let dictionaryList = {};
|
||||
let realSpellChecker = spellChecker.mInlineSpellChecker.spellChecker;
|
||||
realSpellChecker.GetDictionaryList(dictionaryList, {});
|
||||
|
||||
// The original list we get is in random order. We need our list to be
|
||||
// sorted by display names.
|
||||
dictionaryList = spellChecker.sortDictionaryList(dictionaryList.value).map((obj) => {
|
||||
return obj.id;
|
||||
});
|
||||
spellChecker.mDictionaryNames = dictionaryList;
|
||||
|
||||
return { canSpellCheck: spellChecker.canSpellCheck,
|
||||
initialSpellCheckPending: spellChecker.initialSpellCheckPending,
|
||||
enableRealTimeSpell: spellChecker.enabled,
|
||||
overMisspelling: spellChecker.overMisspelling,
|
||||
misspelling: spellChecker.mMisspelling,
|
||||
spellSuggestions: this._generateSpellSuggestions(),
|
||||
currentDictionary: spellChecker.mInlineSpellChecker.spellChecker.GetCurrentDictionary(),
|
||||
dictionaryList: dictionaryList };
|
||||
},
|
||||
|
||||
uninitContextMenu() {
|
||||
for (let i of this._messages)
|
||||
this._manager.removeMessageListener(i, this);
|
||||
|
||||
this._manager = null;
|
||||
this._spellChecker = null;
|
||||
},
|
||||
|
||||
_generateSpellSuggestions() {
|
||||
let spellChecker = this._spellChecker.mInlineSpellChecker.spellChecker;
|
||||
try {
|
||||
spellChecker.CheckCurrentWord(this._spellChecker.mMisspelling);
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let suggestions = new Array(5);
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
suggestions[i] = spellChecker.GetSuggestedWord();
|
||||
if (suggestions[i].length === 0) {
|
||||
suggestions.length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this._spellChecker.mSpellSuggestions = suggestions;
|
||||
return suggestions;
|
||||
},
|
||||
|
||||
_messages: [
|
||||
"InlineSpellChecker:selectDictionary",
|
||||
"InlineSpellChecker:replaceMisspelling",
|
||||
"InlineSpellChecker:toggleEnabled",
|
||||
|
||||
"InlineSpellChecker:recheck",
|
||||
|
||||
"InlineSpellChecker:uninit"
|
||||
],
|
||||
|
||||
_addMessageListeners() {
|
||||
for (let i of this._messages)
|
||||
this._manager.addMessageListener(i, this);
|
||||
},
|
||||
|
||||
receiveMessage(msg) {
|
||||
switch (msg.name) {
|
||||
case "InlineSpellChecker:selectDictionary":
|
||||
this._spellChecker.selectDictionary(msg.data.index);
|
||||
break;
|
||||
|
||||
case "InlineSpellChecker:replaceMisspelling":
|
||||
this._spellChecker.replaceMisspelling(msg.data.index);
|
||||
break;
|
||||
|
||||
case "InlineSpellChecker:toggleEnabled":
|
||||
this._spellChecker.toggleEnabled();
|
||||
break;
|
||||
|
||||
case "InlineSpellChecker:recheck":
|
||||
this._spellChecker.mInlineSpellChecker.enableRealTimeSpell = true;
|
||||
break;
|
||||
|
||||
case "InlineSpellChecker:uninit":
|
||||
this.uninitContextMenu();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
@ -25,6 +25,7 @@ EXTRA_JS_MODULES += [
|
||||
'Geometry.jsm',
|
||||
'Http.jsm',
|
||||
'InlineSpellChecker.jsm',
|
||||
'InlineSpellCheckerContent.jsm',
|
||||
'LoadContextInfo.jsm',
|
||||
'Log.jsm',
|
||||
'NewTabUtils.jsm',
|
||||
|
Loading…
Reference in New Issue
Block a user