Bug 569334 part 1 - Support getting font info in content query. r=masayuki,jfkthame,smaug
authorXidorn Quan <quanxunzhen@gmail.com>
Sat, 31 Jan 2015 18:17:12 +1100
changeset 226881 bae702058e42f9c4dbc74bba2dff935175c795e9
parent 226880 e93e124f3e91c4ebc580dce3b7b33efe19f77211
child 226882 9758962dd013139dd8d52c9d2ab61843103245cb
push id28207
push userphilringnalda@gmail.com
push dateSat, 31 Jan 2015 16:54:13 +0000
treeherdermozilla-central@6f9b69780bf4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki, jfkthame, smaug
bugs569334
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 569334 part 1 - Support getting font info in content query. r=masayuki,jfkthame,smaug
dom/events/ContentEventHandler.cpp
dom/events/ContentEventHandler.h
ipc/glue/IPCMessageUtils.h
widget/EventForwards.h
widget/FontRange.h
widget/TextEvents.h
widget/moz.build
widget/nsGUIEventIPC.h
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -309,16 +309,27 @@ ContentEventHandler::GetNativeTextLength
 
 /* static */ uint32_t
 ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
                                          uint32_t aMaxLength)
 {
   return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength);
 }
 
+static inline uint32_t
+GetBRLength(LineBreakType aLineBreakType)
+{
+#if defined(XP_WIN)
+  // Length of \r\n
+  return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1;
+#else
+  return 1;
+#endif
+}
+
 /* static */ uint32_t
 ContentEventHandler::GetTextLength(nsIContent* aContent,
                                    LineBreakType aLineBreakType,
                                    uint32_t aMaxLength)
 {
   if (aContent->IsNodeOfType(nsINode::eTEXT)) {
     uint32_t textLengthDifference =
 #if defined(XP_MACOSX)
@@ -339,22 +350,17 @@ ContentEventHandler::GetTextLength(nsICo
 
     const nsTextFragment* text = aContent->GetText();
     if (!text) {
       return 0;
     }
     uint32_t length = std::min(text->GetLength(), aMaxLength);
     return length + textLengthDifference;
   } else if (IsContentBR(aContent)) {
-#if defined(XP_WIN)
-    // Length of \r\n
-    return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1;
-#else
-    return 1;
-#endif
+    return GetBRLength(aLineBreakType);
   }
   return 0;
 }
 
 static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset)
 {
 #if defined(XP_MACOSX)
   // On Mac, the length of a native newline ("\r") is equal to the length of
@@ -388,17 +394,16 @@ static nsresult GenerateFlatTextContent(
   if (startNode == endNode && startNode->IsNodeOfType(nsINode::eTEXT)) {
     nsIContent* content = static_cast<nsIContent*>(startNode);
     AppendSubString(aString, content, aRange->StartOffset(),
                     aRange->EndOffset() - aRange->StartOffset());
     ConvertToNativeNewlines(aString);
     return NS_OK;
   }
 
-  nsAutoString tmpStr;
   for (; !iter->IsDone(); iter->Next()) {
     nsINode* node = iter->GetCurrentNode();
     if (!node) {
       break;
     }
     if (!node->IsNodeOfType(nsINode::eCONTENT)) {
       continue;
     }
@@ -418,16 +423,181 @@ static nsresult GenerateFlatTextContent(
     }
   }
   if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
     ConvertToNativeNewlines(aString);
   }
   return NS_OK;
 }
 
+static FontRange*
+AppendFontRange(nsTArray<FontRange>& aFontRanges, uint32_t aBaseOffset)
+{
+  FontRange* fontRange = aFontRanges.AppendElement();
+  fontRange->mStartOffset = aBaseOffset;
+  return fontRange;
+}
+
+/* static */ uint32_t
+ContentEventHandler::GetTextLengthInRange(nsIContent* aContent,
+                                          uint32_t aXPStartOffset,
+                                          uint32_t aXPEndOffset,
+                                          LineBreakType aLineBreakType)
+{
+  return aLineBreakType == LINE_BREAK_TYPE_NATIVE ?
+    GetNativeTextLength(aContent, aXPStartOffset, aXPEndOffset) :
+    aXPEndOffset - aXPStartOffset;
+}
+
+/* static */ void
+ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
+                                      nsIContent* aContent,
+                                      int32_t aBaseOffset,
+                                      int32_t aXPStartOffset,
+                                      int32_t aXPEndOffset,
+                                      LineBreakType aLineBreakType)
+{
+  nsIFrame* frame = aContent->GetPrimaryFrame();
+  if (!frame) {
+    // It is a non-rendered content, create an empty range for it.
+    AppendFontRange(aFontRanges, aBaseOffset);
+    return;
+  }
+
+  int32_t baseOffset = aBaseOffset;
+  nsTextFrame* curr = do_QueryFrame(frame);
+  MOZ_ASSERT(curr, "Not a text frame");
+  while (curr) {
+    int32_t frameXPStart = std::max(curr->GetContentOffset(), aXPStartOffset);
+    int32_t frameXPEnd = std::min(curr->GetContentEnd(), aXPEndOffset);
+    if (frameXPStart >= frameXPEnd) {
+      curr = static_cast<nsTextFrame*>(curr->GetNextContinuation());
+      continue;
+    }
+
+    gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated);
+    gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated);
+
+    nsTextFrame* next = nullptr;
+    if (frameXPEnd < aXPEndOffset) {
+      next = static_cast<nsTextFrame*>(curr->GetNextContinuation());
+      while (next && next->GetTextRun(nsTextFrame::eInflated) == textRun) {
+        frameXPEnd = std::min(next->GetContentEnd(), aXPEndOffset);
+        next = frameXPEnd < aXPEndOffset ?
+          static_cast<nsTextFrame*>(next->GetNextContinuation()) : nullptr;
+      }
+    }
+
+    uint32_t skipStart = iter.ConvertOriginalToSkipped(frameXPStart);
+    uint32_t skipEnd = iter.ConvertOriginalToSkipped(frameXPEnd);
+    gfxTextRun::GlyphRunIterator runIter(
+      textRun, skipStart, skipEnd - skipStart);
+    int32_t lastXPEndOffset = frameXPStart;
+    while (runIter.NextRun()) {
+      gfxFont* font = runIter.GetGlyphRun()->mFont.get();
+      int32_t startXPOffset =
+        iter.ConvertSkippedToOriginal(runIter.GetStringStart());
+      // It is possible that the first glyph run has exceeded the frame,
+      // because the whole frame is filled by skipped chars.
+      if (startXPOffset >= frameXPEnd) {
+        break;
+      }
+
+      if (startXPOffset > lastXPEndOffset) {
+        // Create range for skipped leading chars.
+        AppendFontRange(aFontRanges, baseOffset);
+        baseOffset += GetTextLengthInRange(
+          aContent, lastXPEndOffset, startXPOffset, aLineBreakType);
+        lastXPEndOffset = startXPOffset;
+      }
+
+      FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
+      fontRange->mFontName = font->GetName();
+      fontRange->mFontSize = font->GetAdjustedSize();
+
+      // The converted original offset may exceed the range,
+      // hence we need to clamp it.
+      int32_t endXPOffset =
+        iter.ConvertSkippedToOriginal(runIter.GetStringEnd());
+      endXPOffset = std::min(frameXPEnd, endXPOffset);
+      baseOffset += GetTextLengthInRange(aContent, startXPOffset, endXPOffset,
+                                         aLineBreakType);
+      lastXPEndOffset = endXPOffset;
+    }
+    if (lastXPEndOffset < frameXPEnd) {
+      // Create range for skipped trailing chars. It also handles case
+      // that the whole frame contains only skipped chars.
+      AppendFontRange(aFontRanges, baseOffset);
+      baseOffset += GetTextLengthInRange(
+        aContent, lastXPEndOffset, frameXPEnd, aLineBreakType);
+    }
+
+    curr = next;
+  }
+}
+
+/* static */ nsresult
+ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
+                                            FontRangeArray& aFontRanges,
+                                            uint32_t& aLength,
+                                            LineBreakType aLineBreakType)
+{
+  MOZ_ASSERT(aFontRanges.IsEmpty(), "aRanges must be empty array");
+
+  nsINode* startNode = aRange->GetStartParent();
+  nsINode* endNode = aRange->GetEndParent();
+  if (NS_WARN_IF(!startNode || !endNode)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // baseOffset is the flattened offset of each content node.
+  int32_t baseOffset = 0;
+  nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
+  for (iter->Init(aRange); !iter->IsDone(); iter->Next()) {
+    nsINode* node = iter->GetCurrentNode();
+    if (NS_WARN_IF(!node)) {
+      break;
+    }
+    if (!node->IsContent()) {
+      continue;
+    }
+    nsIContent* content = node->AsContent();
+
+    if (content->IsNodeOfType(nsINode::eTEXT)) {
+      int32_t startOffset = content != startNode ? 0 : aRange->StartOffset();
+      int32_t endOffset = content != endNode ?
+        content->TextLength() : aRange->EndOffset();
+      AppendFontRanges(aFontRanges, content, baseOffset,
+                       startOffset, endOffset, aLineBreakType);
+      baseOffset += GetTextLengthInRange(content, startOffset, endOffset,
+                                         aLineBreakType);
+    } else if (IsContentBR(content)) {
+      if (aFontRanges.IsEmpty()) {
+        MOZ_ASSERT(baseOffset == 0);
+        FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
+        nsIFrame* frame = content->GetPrimaryFrame();
+        if (frame) {
+          const nsFont& font = frame->GetParent()->StyleFont()->mFont;
+          const FontFamilyList& fontList = font.fontlist;
+          const FontFamilyName& fontName = fontList.IsEmpty() ?
+            FontFamilyName(fontList.GetDefaultFontType()) :
+            fontList.GetFontlist()[0];
+          fontName.AppendToString(fontRange->mFontName, false);
+          fontRange->mFontSize =
+            frame->PresContext()->AppUnitsToDevPixels(font.size);
+        }
+      }
+      baseOffset += GetBRLength(aLineBreakType);
+    }
+  }
+
+  aLength = baseOffset;
+  return NS_OK;
+}
+
 nsresult
 ContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
                                              bool aForward,
                                              uint32_t* aXPOffset)
 {
   // XXX This method assumes that the frame boundaries must be cluster
   // boundaries. It's false, but no problem now, maybe.
   if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
@@ -692,16 +862,28 @@ ContentEventHandler::OnQueryTextContent(
   rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
                                   aEvent->mInput.mLength, lineBreakType, false,
                                   &aEvent->mReply.mOffset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (aEvent->mWithFontRanges) {
+    uint32_t fontRangeLength;
+    rv = GenerateFlatFontRanges(range, aEvent->mReply.mFontRanges,
+                                fontRangeLength, lineBreakType);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    MOZ_ASSERT(fontRangeLength == aEvent->mReply.mString.Length(),
+               "Font ranges doesn't match the string");
+  }
+
   aEvent->mSucceeded = true;
 
   return NS_OK;
 }
 
 // Adjust to use a child node if possible
 // to make the returned rect more accurate
 static nsINode* AdjustTextRectNode(nsINode* aNode,
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -89,16 +89,22 @@ public:
   // aContent.  Currently, this method supports only text node or br element
   // for aContent.
   static uint32_t GetNativeTextLength(nsIContent* aContent,
                                       uint32_t aStartOffset,
                                       uint32_t aEndOffset);
   // Get the native text length of a content node excluding any children
   static uint32_t GetNativeTextLength(nsIContent* aContent,
                                       uint32_t aMaxLength = UINT32_MAX);
+  // Get the text length of a given range of a content node in
+  // the given line break type.
+  static uint32_t GetTextLengthInRange(nsIContent* aContent,
+                                       uint32_t aXPStartOffset,
+                                       uint32_t aXPEndOffset,
+                                       LineBreakType aLineBreakType);
 protected:
   static uint32_t GetTextLength(nsIContent* aContent,
                                 LineBreakType aLineBreakType,
                                 uint32_t aMaxLength = UINT32_MAX);
   static LineBreakType GetLineBreakType(WidgetQueryContentEvent* aEvent);
   static LineBreakType GetLineBreakType(WidgetSelectionEvent* aEvent);
   static LineBreakType GetLineBreakType(bool aUseNativeLineBreak);
   // Returns focused content (including its descendant documents).
@@ -124,13 +130,25 @@ protected:
                                   int32_t* aOffsetInFrame);
   // Convert the frame relative offset to the root view relative offset.
   nsresult ConvertToRootViewRelativeOffset(nsIFrame* aFrame,
                                            nsRect& aRect);
   // Expand aXPOffset to the nearest offset in cluster boundary. aForward is
   // true, it is expanded to forward.
   nsresult ExpandToClusterBoundary(nsIContent* aContent, bool aForward,
                                    uint32_t* aXPOffset);
+
+  typedef nsTArray<mozilla::FontRange> FontRangeArray;
+  static void AppendFontRanges(FontRangeArray& aFontRanges,
+                               nsIContent* aContent,
+                               int32_t aBaseOffset,
+                               int32_t aXPStartOffset,
+                               int32_t aXPEndOffset,
+                               LineBreakType aLineBreakType);
+  static nsresult GenerateFlatFontRanges(nsRange* aRange,
+                                         FontRangeArray& aFontRanges,
+                                         uint32_t& aLength,
+                                         LineBreakType aLineBreakType);
 };
 
 } // namespace mozilla
 
 #endif // mozilla_ContentEventHandler_h_
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -539,16 +539,22 @@ struct ParamTraits<InfallibleTArray<E> >
   }
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
     LogParam(static_cast<const FallibleTArray<E>&>(aParam), aLog);
   }
 };
 
+template<typename E, size_t N>
+struct ParamTraits<nsAutoTArray<E, N>> : ParamTraits<nsTArray<E>>
+{
+  typedef nsAutoTArray<E, N> paramType;
+};
+
 template<>
 struct ParamTraits<float>
 {
   typedef float paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     aMsg->WriteBytes(&aParam, sizeof(paramType));
--- a/widget/EventForwards.h
+++ b/widget/EventForwards.h
@@ -107,11 +107,14 @@ struct EventFlags;
 struct AlternativeCharCode;
 
 // TextRange.h
 struct TextRangeStyle;
 struct TextRange;
 
 class TextRangeArray;
 
+// FontRange.h
+struct FontRange;
+
 } // namespace mozilla
 
 #endif // mozilla_EventForwards_h__
new file mode 100644
--- /dev/null
+++ b/widget/FontRange.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 2; 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 mozilla_FontRange_h_
+#define mozilla_FontRange_h_
+
+namespace mozilla {
+
+struct FontRange
+{
+  FontRange()
+    : mStartOffset(0)
+    , mFontSize(0)
+  {
+  }
+
+  int32_t mStartOffset;
+  nsString mFontName;
+  gfxFloat mFontSize; // in device pixels
+};
+
+}
+
+#endif // mozilla_FontRange_h_
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -7,16 +7,17 @@
 #define mozilla_TextEvents_h__
 
 #include <stdint.h>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/EventForwards.h" // for KeyNameIndex, temporarily
 #include "mozilla/TextRange.h"
+#include "mozilla/FontRange.h"
 #include "nsCOMPtr.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsITransferable.h"
 #include "nsRect.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 #include "WritingModes.h"
 
@@ -394,16 +395,17 @@ public:
   }
 
   WidgetQueryContentEvent(bool aIsTrusted, uint32_t aMessage,
                           nsIWidget* aWidget)
     : WidgetGUIEvent(aIsTrusted, aMessage, aWidget, eQueryContentEventClass)
     , mSucceeded(false)
     , mWasAsync(false)
     , mUseNativeLineBreak(true)
+    , mWithFontRanges(false)
   {
   }
 
   virtual WidgetEvent* Duplicate() const MOZ_OVERRIDE
   {
     // This event isn't an internal event of any DOM event.
     NS_ASSERTION(!IsAllowedToDispatchDOMEvent(),
       "WidgetQueryContentEvent needs to support Duplicate()");
@@ -442,16 +444,23 @@ public:
 
   void InitForQueryDOMWidgetHittest(const mozilla::LayoutDeviceIntPoint& aPoint)
   {
     NS_ASSERTION(message == NS_QUERY_DOM_WIDGET_HITTEST,
                  "wrong initializer is called");
     refPoint = aPoint;
   }
 
+  void RequestFontRanges()
+  {
+    NS_ASSERTION(message == NS_QUERY_TEXT_CONTENT,
+                 "not querying text content");
+    mWithFontRanges = true;
+  }
+
   uint32_t GetSelectionStart(void) const
   {
     NS_ASSERTION(message == NS_QUERY_SELECTED_TEXT,
                  "not querying selection");
     return mReply.mOffset + (mReply.mReversed ? mReply.mString.Length() : 0);
   }
 
   uint32_t GetSelectionEnd(void) const
@@ -466,16 +475,17 @@ public:
     NS_ASSERTION(message == NS_QUERY_SELECTED_TEXT,
                  "not querying selection");
     return mReply.mWritingMode;
   }
 
   bool mSucceeded;
   bool mWasAsync;
   bool mUseNativeLineBreak;
+  bool mWithFontRanges;
   struct
   {
     uint32_t mOffset;
     uint32_t mLength;
   } mInput;
   struct
   {
     void* mContentsRoot;
@@ -490,16 +500,18 @@ public:
     // true if the selection exists
     bool mHasSelection;
     // true if DOM element under mouse belongs to widget
     bool mWidgetIsHit;
     // mozilla::WritingMode value at the end (focus) of the selection
     mozilla::WritingMode mWritingMode;
     // used by NS_QUERY_SELECTION_AS_TRANSFERABLE
     nsCOMPtr<nsITransferable> mTransferable;
+    // used by NS_QUERY_TEXT_CONTENT with font ranges requested
+    nsAutoTArray<mozilla::FontRange, 1> mFontRanges;
   } mReply;
 
   enum
   {
     NOT_FOUND = UINT32_MAX
   };
 
   // values of mComputedScrollAction
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -112,16 +112,17 @@ EXPORTS += [
 ]
 
 EXPORTS.mozilla += [
     'BasicEvents.h',
     'CommandList.h',
     'ContentEvents.h',
     'EventClassList.h',
     'EventForwards.h',
+    'FontRange.h',
     'LookAndFeel.h',
     'MiscEvents.h',
     'MouseEvents.h',
     'TextEventDispatcher.h',
     'TextEventDispatcherListener.h',
     'TextEvents.h',
     'TextRange.h',
     'TouchEvents.h',
--- a/widget/nsGUIEventIPC.h
+++ b/widget/nsGUIEventIPC.h
@@ -521,50 +521,74 @@ struct ParamTraits<mozilla::WidgetCompos
         return false;
       }
     }
     return true;
   }
 };
 
 template<>
+struct ParamTraits<mozilla::FontRange>
+{
+  typedef mozilla::FontRange paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mStartOffset);
+    WriteParam(aMsg, aParam.mFontName);
+    WriteParam(aMsg, aParam.mFontSize);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->mStartOffset) &&
+           ReadParam(aMsg, aIter, &aResult->mFontName) &&
+           ReadParam(aMsg, aIter, &aResult->mFontSize);
+  }
+};
+
+template<>
 struct ParamTraits<mozilla::WidgetQueryContentEvent>
 {
   typedef mozilla::WidgetQueryContentEvent paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, static_cast<mozilla::WidgetGUIEvent>(aParam));
     WriteParam(aMsg, aParam.mSucceeded);
     WriteParam(aMsg, aParam.mUseNativeLineBreak);
+    WriteParam(aMsg, aParam.mWithFontRanges);
     WriteParam(aMsg, aParam.mInput.mOffset);
     WriteParam(aMsg, aParam.mInput.mLength);
     WriteParam(aMsg, aParam.mReply.mOffset);
     WriteParam(aMsg, aParam.mReply.mString);
     WriteParam(aMsg, aParam.mReply.mRect);
     WriteParam(aMsg, aParam.mReply.mReversed);
     WriteParam(aMsg, aParam.mReply.mHasSelection);
     WriteParam(aMsg, aParam.mReply.mWidgetIsHit);
+    WriteParam(aMsg, aParam.mReply.mFontRanges);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     aResult->mWasAsync = true;
     return ReadParam(aMsg, aIter,
                      static_cast<mozilla::WidgetGUIEvent*>(aResult)) &&
            ReadParam(aMsg, aIter, &aResult->mSucceeded) &&
            ReadParam(aMsg, aIter, &aResult->mUseNativeLineBreak) &&
+           ReadParam(aMsg, aIter, &aResult->mWithFontRanges) &&
            ReadParam(aMsg, aIter, &aResult->mInput.mOffset) &&
            ReadParam(aMsg, aIter, &aResult->mInput.mLength) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mOffset) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mString) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mRect) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mReversed) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mHasSelection) &&
-           ReadParam(aMsg, aIter, &aResult->mReply.mWidgetIsHit);
+           ReadParam(aMsg, aIter, &aResult->mReply.mWidgetIsHit) &&
+           ReadParam(aMsg, aIter, &aResult->mReply.mFontRanges);
   }
 };
 
 template<>
 struct ParamTraits<mozilla::WidgetSelectionEvent>
 {
   typedef mozilla::WidgetSelectionEvent paramType;