Bug 1177943 - Part 2. Add cmd_lookUpDictionary content command. r?masayuki
To get selected word on contnet process, I create new contnet command to look up dictionary. Then, call nsIWidget's method to show looking up dictionary.
About fetched length, see https://www.chromium.org/developers/design-documents/system-dictionary-pop-up-architecture for OSX 10.6's design
MozReview-Commit-ID: Ic2r6hboEEw
--- a/dom/base/nsGlobalWindowCommands.cpp
+++ b/dom/base/nsGlobalWindowCommands.cpp
@@ -24,18 +24,22 @@
#include "nsIDocShell.h"
#include "nsISelectionController.h"
#include "nsIWebNavigation.h"
#include "nsIContentViewerEdit.h"
#include "nsIContentViewer.h"
#include "nsFocusManager.h"
#include "nsCopySupport.h"
#include "nsIClipboard.h"
+#include "ContentEventHandler.h"
+#include "nsContentUtils.h"
+#include "nsIWordBreaker.h"
#include "mozilla/Attributes.h"
#include "mozilla/BasicEvents.h"
+#include "mozilla/TextEvents.h"
#include "mozilla/dom/Selection.h"
#include "nsIClipboardDragDropHooks.h"
#include "nsIClipboardDragDropHookList.h"
using namespace mozilla;
const char * const sSelectAllString = "cmd_selectAll";
@@ -974,16 +978,167 @@ NS_IMETHODIMP
nsClipboardDragDropHookCommand::GetCommandStateParams(const char *aCommandName,
nsICommandParams *aParams,
nsISupports *aCommandContext)
{
NS_ENSURE_ARG_POINTER(aParams);
return aParams->SetBooleanValue("state_enabled", true);
}
+class nsLookUpDictionaryCommand final : public nsIControllerCommand
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICONTROLLERCOMMAND
+
+private:
+ ~nsLookUpDictionaryCommand()
+ {
+ }
+};
+
+NS_IMPL_ISUPPORTS(nsLookUpDictionaryCommand, nsIControllerCommand)
+
+NS_IMETHODIMP
+nsLookUpDictionaryCommand::IsCommandEnabled(
+ const char* aCommandName,
+ nsISupports* aCommandContext,
+ bool* aRetval)
+{
+ *aRetval = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLookUpDictionaryCommand::GetCommandStateParams(const char* aCommandName,
+ nsICommandParams* aParams,
+ nsISupports* aCommandContext)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsLookUpDictionaryCommand::DoCommand(const char *aCommandName,
+ nsISupports *aCommandContext)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsLookUpDictionaryCommand::DoCommandParams(const char* aCommandName,
+ nsICommandParams* aParams,
+ nsISupports* aCommandContext)
+{
+ int32_t x;
+ int32_t y;
+
+ nsresult rv = aParams->GetLongValue("x", &x);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aParams->GetLongValue("y", &y);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ LayoutDeviceIntPoint point(x, y);
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(aCommandContext);
+ if (NS_WARN_IF(!window)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsIDocShell *docShell = window->GetDocShell();
+ if (NS_WARN_IF(!docShell)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
+ if (NS_WARN_IF(!presShell)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsPresContext* presContext = presShell->GetPresContext();
+ if (NS_WARN_IF(!presContext)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIWidget> widget = presContext->GetRootWidget();
+ if (NS_WARN_IF(!widget)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ WidgetQueryContentEvent charAt(true, eQueryCharacterAtPoint, widget);
+ charAt.refPoint.x = x;
+ charAt.refPoint.y = y;
+ ContentEventHandler handler(presContext);
+ handler.OnQueryCharacterAtPoint(&charAt);
+
+ if (!charAt.mSucceeded ||
+ charAt.mReply.mOffset == WidgetQueryContentEvent::NOT_FOUND) {
+ return NS_ERROR_FAILURE;
+ }
+
+ WidgetQueryContentEvent textContent(true, eQueryTextContent, widget);
+ // OSX 10.7 queries 50 characters before/after current point. So we fetch
+ // same length.
+ uint32_t offset = charAt.mReply.mOffset;
+ if (offset > 50) {
+ offset -= 50;
+ } else {
+ offset = 0;
+ }
+ textContent.InitForQueryTextContent(offset, 100);
+ handler.OnQueryTextContent(&textContent);
+ if (!textContent.mSucceeded || textContent.mReply.mString.IsEmpty()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // XXX nsIWordBreaker doesn't use contextual breaker.
+ // If OS provides it, widget should use it if contextual breaker is needed.
+ nsCOMPtr<nsIWordBreaker> wordBreaker = nsContentUtils::WordBreaker();
+ if (!wordBreaker) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsWordRange range =
+ wordBreaker->FindWord(textContent.mReply.mString.get(),
+ textContent.mReply.mString.Length(),
+ charAt.mReply.mOffset - offset);
+ if (range.mEnd == range.mBegin) {
+ return NS_ERROR_FAILURE;
+ }
+ range.mBegin += offset;
+ range.mEnd += offset;
+
+ WidgetQueryContentEvent lookUpContent(true, eQueryTextContent, widget);
+ lookUpContent.InitForQueryTextContent(range.mBegin,
+ range.mEnd - range.mBegin);
+ lookUpContent.RequestFontRanges();
+ handler.OnQueryTextContent(&lookUpContent);
+ if (!lookUpContent.mSucceeded || lookUpContent.mReply.mString.IsEmpty()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ WidgetQueryContentEvent charRect(true, eQueryTextRect, widget);
+ charRect.InitForQueryTextRect(range.mBegin, range.mEnd - range.mBegin);
+ handler.OnQueryTextRect(&charRect);
+ if (!charRect.mSucceeded) {
+ return NS_ERROR_FAILURE;
+ }
+
+ widget->LookUpDictionary(lookUpContent.mReply.mString,
+ lookUpContent.mReply.mFontRanges,
+ charRect.mReply.mWritingMode.IsVertical(),
+ charRect.mReply.mRect.TopLeft());
+
+ return NS_OK;
+}
+
/*---------------------------------------------------------------------------
RegisterWindowCommands
----------------------------------------------------------------------------*/
#define NS_REGISTER_ONE_COMMAND(_cmdClass, _cmdName) \
{ \
@@ -1085,10 +1240,12 @@ nsWindowCommandRegistration::RegisterWin
#if 0 // Remove unless needed again, bug 204777
NS_REGISTER_ONE_COMMAND(nsGoBackCommand, "cmd_browserBack");
NS_REGISTER_ONE_COMMAND(nsGoForwardCommand, "cmd_browserForward");
#endif
NS_REGISTER_ONE_COMMAND(nsClipboardDragDropHookCommand, "cmd_clipboardDragDropHook");
+ NS_REGISTER_ONE_COMMAND(nsLookUpDictionaryCommand, "cmd_lookUpDictionary");
+
return rv;
}
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -806,16 +806,17 @@ EventStateManager::PreHandleEvent(nsPres
break;
case eContentCommandCut:
case eContentCommandCopy:
case eContentCommandPaste:
case eContentCommandDelete:
case eContentCommandUndo:
case eContentCommandRedo:
case eContentCommandPasteTransferable:
+ case eContentCommandLookUpDictionary:
DoContentCommandEvent(aEvent->AsContentCommandEvent());
break;
case eContentCommandScroll:
DoContentCommandScrollEvent(aEvent->AsContentCommandEvent());
break;
case eCompositionStart:
if (aEvent->IsTrusted()) {
// If the event is trusted event, set the selected text to data of
@@ -5201,16 +5202,19 @@ EventStateManager::DoContentCommandEvent
cmd = "cmd_undo";
break;
case eContentCommandRedo:
cmd = "cmd_redo";
break;
case eContentCommandPasteTransferable:
cmd = "cmd_pasteTransferable";
break;
+ case eContentCommandLookUpDictionary:
+ cmd = "cmd_lookUpDictionary";
+ break;
default:
return NS_ERROR_NOT_IMPLEMENTED;
}
nsCOMPtr<nsIController> controller;
nsresult rv = root->GetControllerForCommand(cmd, getter_AddRefs(controller));
NS_ENSURE_SUCCESS(rv, rv);
if (!controller) {
// When GetControllerForCommand succeeded but there is no controller, the
@@ -5231,17 +5235,44 @@ EventStateManager::DoContentCommandEvent
NS_ENSURE_SUCCESS(rv, rv);
rv = params->SetISupportsValue("transferable", aEvent->mTransferable);
NS_ENSURE_SUCCESS(rv, rv);
rv = commandController->DoCommandWithParams(cmd, params);
break;
}
-
+
+ case eContentCommandLookUpDictionary: {
+ nsCOMPtr<nsICommandController> commandController =
+ do_QueryInterface(controller);
+ if (NS_WARN_IF(!commandController)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsICommandParams> params =
+ do_CreateInstance("@mozilla.org/embedcomp/command-params;1", &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = params->SetLongValue("x", aEvent->refPoint.x);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = params->SetLongValue("y", aEvent->refPoint.y);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = commandController->DoCommandWithParams(cmd, params);
+ break;
+ }
+
default:
rv = controller->DoCommand(cmd);
break;
}
NS_ENSURE_SUCCESS(rv, rv);
}
}
aEvent->mSucceeded = true;
--- a/widget/EventMessageList.h
+++ b/widget/EventMessageList.h
@@ -294,16 +294,17 @@ NS_EVENT_MESSAGE(eSetSelection)
// Events of commands for the contents
NS_EVENT_MESSAGE(eContentCommandCut)
NS_EVENT_MESSAGE(eContentCommandCopy)
NS_EVENT_MESSAGE(eContentCommandPaste)
NS_EVENT_MESSAGE(eContentCommandDelete)
NS_EVENT_MESSAGE(eContentCommandUndo)
NS_EVENT_MESSAGE(eContentCommandRedo)
NS_EVENT_MESSAGE(eContentCommandPasteTransferable)
+NS_EVENT_MESSAGE(eContentCommandLookUpDictionary)
// eContentCommandScroll scrolls the nearest scrollable element to the
// currently focused content or latest DOM selection. This would normally be
// the same element scrolled by keyboard scroll commands, except that this event
// will scroll an element scrollable in either direction. I.e., if the nearest
// scrollable ancestor element can only be scrolled vertically, and horizontal
// scrolling is requested using this event, no scrolling will occur.
NS_EVENT_MESSAGE(eContentCommandScroll)