Bug 1177943 - Part 2. Add cmd_lookUpDictionary content command. r=masayuki draft
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Mon, 02 May 2016 11:41:20 +0900
changeset 368216 3976d86ec5dbc005399f7171eafdd1c15d0619f6
parent 368215 0b92c9a7159a793ba8eb65545ac4970bf44cfcf6
child 368217 a20d80a8a367fa2f6c4294fb5a1a2eb3a0d18e88
push id18471
push userm_kato@ga2.so-net.ne.jp
push dateWed, 18 May 2016 11:49:22 +0000
reviewersmasayuki
bugs1177943
milestone49.0a1
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
dom/base/nsGlobalWindowCommands.cpp
dom/events/EventStateManager.cpp
widget/EventMessageList.h
--- 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";
@@ -981,16 +985,173 @@ 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:
+  virtual ~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)
+{
+  if (NS_WARN_IF(!nsContentUtils::IsSafeToRunScript())) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  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.mRefPoint.x = x;
+  charAt.mRefPoint.y = y;
+  ContentEventHandler handler(presContext);
+  handler.OnQueryCharacterAtPoint(&charAt);
+
+  if (NS_WARN_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 (NS_WARN_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 (NS_WARN_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 (NS_WARN_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 (NS_WARN_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)                \
   {                                                                 \
@@ -1092,10 +1253,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
@@ -811,16 +811,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
@@ -5211,16 +5212,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
@@ -5241,17 +5245,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->mRefPoint.x);
+          if (NS_WARN_IF(NS_FAILED(rv))) {
+            return rv;
+          }
+
+          rv = params->SetLongValue("y", aEvent->mRefPoint.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
@@ -307,16 +307,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)