bug 467669 - pt 1 - get list of fonts actually used to render a document range. r=roc sr=bzbarsky
authorJonathan Kew <jfkthame@gmail.com>
Thu, 16 Jun 2011 07:31:36 +0100
changeset 71472 08a6a9f91d6e508c88be9ad2455b62e39cefb91e
parent 71471 2d24929f1d6596b88c242fbe4caa1c36f293bf64
child 71473 14eb4f38aff843badd02ed09929a99d630c34245
push id209
push userbzbarsky@mozilla.com
push dateTue, 05 Jul 2011 17:42:16 +0000
treeherdermozilla-aurora@cc6e30cce8af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, bzbarsky
bugs467669
milestone7.0a1
bug 467669 - pt 1 - get list of fonts actually used to render a document range. r=roc sr=bzbarsky
content/base/public/nsIRange.h
content/base/src/nsRange.cpp
content/base/src/nsRange.h
dom/interfaces/base/domstubs.idl
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/inspector/public/Makefile.in
layout/inspector/public/inIDOMUtils.idl
layout/inspector/public/nsIDOMFontFace.idl
layout/inspector/public/nsIDOMFontFaceList.idl
layout/inspector/src/Makefile.in
layout/inspector/src/inDOMUtils.cpp
layout/inspector/src/inDOMUtils.h
layout/inspector/src/nsFontFace.cpp
layout/inspector/src/nsFontFace.h
layout/inspector/src/nsFontFaceList.cpp
layout/inspector/src/nsFontFaceList.h
--- a/content/base/public/nsIRange.h
+++ b/content/base/public/nsIRange.h
@@ -43,16 +43,18 @@
 #include "nsINode.h"
 #include "nsIDOMRange.h"
 
 // IID for the nsIRange interface
 #define NS_IRANGE_IID \
 { 0x09dec26b, 0x1ab7, 0x4ff0, \
  { 0xa1, 0x67, 0x7f, 0x22, 0x9c, 0xaa, 0xc3, 0x04 } }
 
+class nsIDOMFontFaceList;
+
 class nsIRange : public nsIDOMRange {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IRANGE_IID)
 
   nsIRange()
     : mRoot(nsnull),
       mStartOffset(0),
       mEndOffset(0),
@@ -118,16 +120,19 @@ public:
   virtual nsresult SetEnd(nsINode* aParent, PRInt32 aOffset) = 0;
   virtual nsresult CloneRange(nsIRange** aNewRange) const = 0;
 
   // Work around hiding warnings
   NS_IMETHOD SetStart(nsIDOMNode* aParent, PRInt32 aOffset) = 0;
   NS_IMETHOD SetEnd(nsIDOMNode* aParent, PRInt32 aOffset) = 0;
   NS_IMETHOD CloneRange(nsIDOMRange** aNewRange) = 0;
 
+  // To support the font inspector API
+  NS_IMETHOD GetUsedFontFaces(nsIDOMFontFaceList** aResult) = 0;
+
 protected:
   nsCOMPtr<nsINode> mRoot;
   nsCOMPtr<nsINode> mStartParent;
   nsCOMPtr<nsINode> mEndParent;
   PRInt32 mStartOffset;
   PRInt32 mEndOffset;
 
   PRPackedBool mIsPositioned;
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -54,16 +54,17 @@
 #include "nsIContentIterator.h"
 #include "nsIDOMNodeList.h"
 #include "nsGkAtoms.h"
 #include "nsContentUtils.h"
 #include "nsGenericDOMDataNode.h"
 #include "nsClientRect.h"
 #include "nsLayoutUtils.h"
 #include "nsTextFrame.h"
+#include "nsFontFaceList.h"
 
 nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
 nsresult NS_NewContentSubtreeIterator(nsIContentIterator** aInstancePtrResult);
 
 /******************************************************
  * stack based utilty class for managing monitor
  ******************************************************/
 
@@ -2231,8 +2232,67 @@ nsRange::GetClientRects(nsIDOMClientRect
     mEndParent, mEndOffset);
 
   if (NS_FAILED(builder.mRV))
     return builder.mRV;
   rectList.forget(aResult);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsRange::GetUsedFontFaces(nsIDOMFontFaceList** aResult)
+{
+  *aResult = nsnull;
+
+  NS_ENSURE_TRUE(mStartParent, NS_ERROR_UNEXPECTED);
+
+  nsCOMPtr<nsIDOMNode> startContainer = do_QueryInterface(mStartParent);
+  nsCOMPtr<nsIDOMNode> endContainer = do_QueryInterface(mEndParent);
+
+  // Flush out layout so our frames are up to date.
+  nsIDocument* doc = mStartParent->GetOwnerDoc();
+  NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
+  doc->FlushPendingNotifications(Flush_Frames);
+
+  // Recheck whether we're still in the document
+  NS_ENSURE_TRUE(mStartParent->IsInDoc(), NS_ERROR_UNEXPECTED);
+
+  nsRefPtr<nsFontFaceList> fontFaceList = new nsFontFaceList();
+
+  RangeSubtreeIterator iter;
+  nsresult rv = iter.Init(this);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  while (!iter.IsDone()) {
+    // only collect anything if the range is not collapsed
+    nsCOMPtr<nsIDOMNode> node(iter.GetCurrentNode());
+    iter.Next();
+
+    nsCOMPtr<nsIContent> content = do_QueryInterface(node);
+    if (!content) {
+      continue;
+    }
+    nsIFrame* frame = content->GetPrimaryFrame();
+    if (!frame) {
+      continue;
+    }
+
+    if (content->IsNodeOfType(nsINode::eTEXT)) {
+       if (node == startContainer) {
+         PRInt32 offset = startContainer == endContainer ? 
+           mEndOffset : content->GetText()->GetLength();
+         nsLayoutUtils::GetFontFacesForText(frame, mStartOffset, offset,
+                                            PR_TRUE, fontFaceList);
+         continue;
+       }
+       if (node == endContainer) {
+         nsLayoutUtils::GetFontFacesForText(frame, 0, mEndOffset,
+                                            PR_TRUE, fontFaceList);
+         continue;
+       }
+    }
+
+    nsLayoutUtils::GetFontFacesForFrames(frame, fontFaceList);
+  }
+
+  fontFaceList.forget(aResult);
+  return NS_OK;
+}
--- a/content/base/src/nsRange.h
+++ b/content/base/src/nsRange.h
@@ -93,16 +93,18 @@ public:
   
   // nsIRange interface
   virtual nsINode* GetCommonAncestor() const;
   virtual void Reset();
   virtual nsresult SetStart(nsINode* aParent, PRInt32 aOffset);
   virtual nsresult SetEnd(nsINode* aParent, PRInt32 aOffset);
   virtual nsresult CloneRange(nsIRange** aNewRange) const;
 
+  NS_IMETHOD GetUsedFontFaces(nsIDOMFontFaceList** aResult);
+
   // nsIMutationObserver methods
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
   NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
 
 private:
   // no copy's or assigns
--- a/dom/interfaces/base/domstubs.idl
+++ b/dom/interfaces/base/domstubs.idl
@@ -117,8 +117,12 @@ interface nsIDOMHTMLTableSectionElement;
 
 // Range
 interface nsIDOMRange;
 
 // Crypto
 interface nsIDOMCRMFObject;
 interface nsIDOMCrypto;
 interface nsIDOMPkcs11;
+
+// Used font face (for inspector)
+interface nsIDOMFontFace;
+interface nsIDOMFontFaceList;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -93,16 +93,18 @@
 #include "nsListControlFrame.h"
 #include "ImageLayers.h"
 #include "mozilla/arm.h"
 #include "mozilla/dom/Element.h"
 #include "nsCanvasFrame.h"
 #include "gfxDrawable.h"
 #include "gfxUtils.h"
 #include "nsDataHashtable.h"
+#include "nsTextFrame.h"
+#include "nsFontFaceList.h"
 
 #include "nsSVGUtils.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGForeignObjectFrame.h"
 #include "nsSVGOuterSVGFrame.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
@@ -4051,16 +4053,86 @@ nsLayoutUtils::AssertTreeOnlyEmptyNextIn
       nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(child);
     }
     childList = aSubtreeRoot->GetAdditionalChildListName(listIndex++);
   } while (childList);
 }
 #endif
 
 /* static */
+nsresult
+nsLayoutUtils::GetFontFacesForFrames(nsIFrame* aFrame,
+                                     nsFontFaceList* aFontFaceList)
+{
+  NS_PRECONDITION(aFrame, "NULL frame pointer");
+
+  if (aFrame->GetType() == nsGkAtoms::textFrame) {
+    return GetFontFacesForText(aFrame, 0, PR_INT32_MAX, PR_FALSE,
+                               aFontFaceList);
+  }
+
+  while (aFrame) {
+    nsIAtom* childLists[] = { nsnull, nsGkAtoms::popupList };
+    for (int i = 0; i < NS_ARRAY_LENGTH(childLists); ++i) {
+      nsFrameList children(aFrame->GetChildList(childLists[i]));
+      for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
+        nsIFrame* child = e.get();
+        if (child->GetPrevContinuation()) {
+          continue;
+        }
+        child = nsPlaceholderFrame::GetRealFrameFor(child);
+        nsresult rv = GetFontFacesForFrames(child, aFontFaceList);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    }
+    aFrame = GetNextContinuationOrSpecialSibling(aFrame);
+  }
+
+  return NS_OK;
+}
+
+/* static */
+nsresult
+nsLayoutUtils::GetFontFacesForText(nsIFrame* aFrame,
+                                   PRInt32 aStartOffset, PRInt32 aEndOffset,
+                                   PRBool aFollowContinuations,
+                                   nsFontFaceList* aFontFaceList)
+{
+  NS_PRECONDITION(aFrame, "NULL frame pointer");
+
+  if (aFrame->GetType() != nsGkAtoms::textFrame) {
+    return NS_OK;
+  }
+
+  nsTextFrame* curr = static_cast<nsTextFrame*>(aFrame);
+  do {
+    PRInt32 offset = curr->GetContentOffset();
+    PRInt32 fstart = NS_MAX(offset, aStartOffset);
+    PRInt32 fend = NS_MIN(curr->GetContentEnd(), aEndOffset);
+    if (fstart >= fend) {
+      continue;
+    }
+
+    // overlapping with the offset we want
+    curr->EnsureTextRun();
+    gfxTextRun* textRun = curr->GetTextRun();
+    NS_ENSURE_TRUE(textRun, NS_ERROR_OUT_OF_MEMORY);
+
+    gfxSkipCharsIterator iter(textRun->GetSkipChars());
+    PRUint32 skipStart = iter.ConvertOriginalToSkipped(fstart - offset);
+    PRUint32 skipEnd = iter.ConvertOriginalToSkipped(fend - offset);
+    aFontFaceList->AddFontsFromTextRun(textRun,
+                                       skipStart, skipEnd - skipStart);
+  } while (aFollowContinuations &&
+           (curr = static_cast<nsTextFrame*>(curr->GetNextContinuation())));
+
+  return NS_OK;
+}
+
+/* static */
 void
 nsLayoutUtils::Shutdown()
 {
   if (sContentMap) {
     delete sContentMap;
     sContentMap = NULL;
   }
 }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -47,16 +47,17 @@ class nsIContent;
 class nsIAtom;
 class nsIScrollableFrame;
 class nsIDOMEvent;
 class nsRegion;
 class nsDisplayListBuilder;
 class nsDisplayItem;
 class nsFontMetrics;
 class nsClientRectList;
+class nsFontFaceList;
 
 #include "prtypes.h"
 #include "nsStyleContext.h"
 #include "nsAutoPtr.h"
 #include "nsStyleSet.h"
 #include "nsIView.h"
 #include "nsIFrame.h"
 #include "nsThreadUtils.h"
@@ -1339,16 +1340,35 @@ public:
    * that goes behind the page of a print preview presentation.
    */
   static PRBool NeedsPrintPreviewBackground(nsPresContext* aPresContext) {
     return aPresContext->IsRootPaginatedDocument() &&
       (aPresContext->Type() == nsPresContext::eContext_PrintPreview ||
        aPresContext->Type() == nsPresContext::eContext_PageLayout);
   }
 
+  /**
+   * Adds all font faces used in the frame tree starting from aFrame
+   * to the list aFontFaceList.
+   */
+  static nsresult GetFontFacesForFrames(nsIFrame* aFrame,
+                                        nsFontFaceList* aFontFaceList);
+
+  /**
+   * Adds all font faces used within the specified range of text in aFrame,
+   * and optionally its continuations, to the list in aFontFaceList.
+   * Pass 0 and PR_INT32_MAX for aStartOffset and aEndOffset to specify the
+   * entire text is to be considered.
+   */
+  static nsresult GetFontFacesForText(nsIFrame* aFrame,
+                                      PRInt32 aStartOffset,
+                                      PRInt32 aEndOffset,
+                                      PRBool aFollowContinuations,
+                                      nsFontFaceList* aFontFaceList);
+
   static void Shutdown();
 
 #ifdef DEBUG
   /**
    * Assert that there are no duplicate continuations of the same frame
    * within aFrameList.  Optimize the tests by assuming that all frames
    * in aFrameList have parent aContainer.
    */
--- a/layout/inspector/public/Makefile.in
+++ b/layout/inspector/public/Makefile.in
@@ -49,12 +49,14 @@ XPIDL_MODULE=inspector
 
 XPIDLSRCS = inIDOMView.idl \
             inIDeepTreeWalker.idl \
             inIFlasher.idl \
             inISearchProcess.idl \
             inISearchObserver.idl \
             inICSSValueSearch.idl \
             inIDOMUtils.idl \
+            nsIDOMFontFace.idl \
+            nsIDOMFontFaceList.idl \
 	    $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
--- a/layout/inspector/public/inIDOMUtils.idl
+++ b/layout/inspector/public/inIDOMUtils.idl
@@ -41,18 +41,20 @@
 interface nsIArray;
 interface nsISupportsArray;
 interface nsIDOMCharacterData;
 interface nsIDOMElement;
 interface nsIDOMDocument;
 interface nsIDOMCSSStyleRule;
 interface nsIDOMNode;
 interface nsIDOMNodeList;
+interface nsIDOMFontFaceList;
+interface nsIDOMRange;
 
-[scriptable, uuid(bb8f76f4-888e-11e0-9e35-5f8b6c85da46)]
+[scriptable, uuid(70205D9E-EFD7-4658-8E9E-690400B57FD0)]
 interface inIDOMUtils : nsISupports
 {
   // CSS utilities
   nsISupportsArray getCSSStyleRules(in nsIDOMElement aElement, [optional] in DOMString aPseudo);
   unsigned long getRuleLine(in nsIDOMCSSStyleRule aRule);
 
   // DOM Node utilities
   boolean isIgnorableWhitespace(in nsIDOMCharacterData aDataNode);
@@ -65,9 +67,11 @@ interface inIDOMUtils : nsISupports
                                     in boolean aShowingAnonymousContent);
   
   // XBL utilities
   nsIArray getBindingURLs(in nsIDOMElement aElement);
 
   // content state utilities
   unsigned long long getContentState(in nsIDOMElement aElement);
   void setContentState(in nsIDOMElement aElement, in unsigned long long aState);
+
+  nsIDOMFontFaceList getUsedFontFaces(in nsIDOMRange aRange);
 };
new file mode 100644
--- /dev/null
+++ b/layout/inspector/public/nsIDOMFontFace.idl
@@ -0,0 +1,28 @@
+#include "nsISupports.idl"
+
+interface nsIDOMCSSFontFaceRule;
+interface nsIDOMCSSStyleDeclaration;
+
+[scriptable, uuid(9a3b1272-6585-4f41-b08f-fdc5da444cd0)]
+interface nsIDOMFontFace : nsISupports
+{
+  // An indication of how we found this font during font-matching.
+  // Note that the same physical font may have been found in multiple ways within a range.
+  readonly attribute boolean fromFontGroup;
+  readonly attribute boolean fromLanguagePrefs;
+  readonly attribute boolean fromSystemFallback;
+
+  // available for all fonts
+  readonly attribute DOMString name; // full font name as obtained from the font resource
+  readonly attribute DOMString CSSFamilyName; // a family name that could be used in CSS font-family
+                                              // (not necessarily the actual name that was used,
+                                              // due to aliases, generics, localized names, etc)
+
+  // meaningful only when the font is a user font defined using @font-face
+  readonly attribute nsIDOMCSSFontFaceRule rule; // null if no associated @font-face rule
+  readonly attribute long srcIndex; // index in the rule's src list, -1 if no @font-face rule
+  readonly attribute DOMString URI; // null if not a downloaded font, i.e. local
+  readonly attribute DOMString localName; // null if not a src:local(...) rule
+  readonly attribute DOMString format; // as per http://www.w3.org/TR/css3-webfonts/#referencing
+  readonly attribute DOMString metadata; // XML metadata from WOFF file (if any)
+};
new file mode 100644
--- /dev/null
+++ b/layout/inspector/public/nsIDOMFontFaceList.idl
@@ -0,0 +1,10 @@
+#include "nsISupports.idl"
+
+interface nsIDOMFontFace;
+
+[scriptable, uuid(2538579c-9472-4fd9-8dc1-d44ce4c1b7ba)]
+interface nsIDOMFontFaceList : nsISupports
+{
+  nsIDOMFontFace                    item(in unsigned long index);
+  readonly attribute unsigned long  length;
+};
--- a/layout/inspector/src/Makefile.in
+++ b/layout/inspector/src/Makefile.in
@@ -41,25 +41,30 @@ srcdir=@srcdir@
 VPATH=@srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = inspector
 LIBRARY_NAME = inspector_s
 LIBXUL_LIBRARY = 1
 
-
+EXPORTS = \
+  nsFontFace.h \
+  nsFontFaceList.h \
+  $(NULL)
 
 CPPSRCS= \
   inDeepTreeWalker.cpp \
   inFlasher.cpp \
   inSearchLoop.cpp \
   inCSSValueSearch.cpp \
   inDOMUtils.cpp \
   inLayoutUtils.cpp \
+  nsFontFace.cpp \
+  nsFontFaceList.cpp \
 	$(NULL)
 
 ifdef MOZ_XUL
 CPPSRCS += \
   inDOMView.cpp \
   $(NULL)
 endif
 
--- a/layout/inspector/src/inDOMUtils.cpp
+++ b/layout/inspector/src/inDOMUtils.cpp
@@ -53,16 +53,17 @@
 #include "nsIDOMWindowInternal.h"
 #include "nsXBLBinding.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsIMutableArray.h"
 #include "nsBindingManager.h"
 #include "nsComputedDOMStyle.h"
 #include "nsEventStateManager.h"
 #include "nsIAtom.h"
+#include "nsIRange.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 
 inDOMUtils::inDOMUtils()
 {
 }
 
 inDOMUtils::~inDOMUtils()
@@ -326,8 +327,18 @@ inDOMUtils::GetRuleNodeForContent(nsICon
   nsRefPtr<nsStyleContext> sContext =
     nsComputedDOMStyle::GetStyleContextForElement(aContent->AsElement(), aPseudo, presShell);
   if (sContext) {
     *aRuleNode = sContext->GetRuleNode();
     sContext.forget(aStyleContext);
   }
   return NS_OK;
 }
+
+NS_IMETHODIMP
+inDOMUtils::GetUsedFontFaces(nsIDOMRange* aRange,
+                             nsIDOMFontFaceList** aFontFaceList)
+{
+  nsCOMPtr<nsIRange> range = do_QueryInterface(aRange);
+  NS_ENSURE_TRUE(range, NS_ERROR_UNEXPECTED);
+
+  return range->GetUsedFontFaces(aFontFaceList);
+}
--- a/layout/inspector/src/inDOMUtils.h
+++ b/layout/inspector/src/inDOMUtils.h
@@ -59,13 +59,13 @@ public:
 private:
   // aStyleContext must be released by the caller once he's done with aRuleNode.
   static nsresult GetRuleNodeForContent(nsIContent* aContent,
                                         nsIAtom* aPseudo,
                                         nsStyleContext** aStyleContext,
                                         nsRuleNode** aRuleNode);
 };
 
-// {40B22006-5DD5-42f2-BFE7-7DBF0757AB8B}
+// {0a499822-a287-4089-ad3f-9ffcd4f40263}
 #define IN_DOMUTILS_CID \
-{ 0x40b22006, 0x5dd5, 0x42f2, { 0xbf, 0xe7, 0x7d, 0xbf, 0x7, 0x57, 0xab, 0x8b } }
+  {0x0a499822, 0xa287, 0x4089, {0xad, 0x3f, 0x9f, 0xfc, 0xd4, 0xf4, 0x02, 0x63}}
 
 #endif // __inDOMUtils_h__
new file mode 100644
--- /dev/null
+++ b/layout/inspector/src/nsFontFace.cpp
@@ -0,0 +1,133 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsFontFace.h"
+
+nsFontFace::nsFontFace(gfxFontEntry* aFontEntry)
+  : mFontEntry(aFontEntry)
+{
+}
+
+nsFontFace::~nsFontFace()
+{
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsISupports
+
+NS_IMPL_ISUPPORTS1(nsFontFace, nsIDOMFontFace)
+
+////////////////////////////////////////////////////////////////////////
+// nsIDOMFontFace
+
+/* readonly attribute boolean fromFontGroup; */
+NS_IMETHODIMP
+nsFontFace::GetFromFontGroup(PRBool * aFromFontGroup)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* readonly attribute boolean fromLanguagePrefs; */
+NS_IMETHODIMP
+nsFontFace::GetFromLanguagePrefs(PRBool * aFromLanguagePrefs)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* readonly attribute boolean fromSystemFallback; */
+NS_IMETHODIMP
+nsFontFace::GetFromSystemFallback(PRBool * aFromSystemFallback)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* readonly attribute DOMString name; */
+NS_IMETHODIMP
+nsFontFace::GetName(nsAString & aName)
+{
+  aName = mFontEntry->Name();
+  return NS_OK;
+}
+
+/* readonly attribute DOMString CSSFamilyName; */
+NS_IMETHODIMP
+nsFontFace::GetCSSFamilyName(nsAString & aCSSFamilyName)
+{
+  aCSSFamilyName = mFontEntry->FamilyName();
+  return NS_OK;
+}
+
+/* readonly attribute nsIDOMCSSFontFaceRule rule; */
+NS_IMETHODIMP
+nsFontFace::GetRule(nsIDOMCSSFontFaceRule **aRule)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* readonly attribute long srcIndex; */
+NS_IMETHODIMP
+nsFontFace::GetSrcIndex(PRInt32 * aSrcIndex)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* readonly attribute DOMString URI; */
+NS_IMETHODIMP
+nsFontFace::GetURI(nsAString & aURI)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* readonly attribute DOMString localName; */
+NS_IMETHODIMP
+nsFontFace::GetLocalName(nsAString & aLocalName)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* readonly attribute DOMString format; */
+NS_IMETHODIMP
+nsFontFace::GetFormat(nsAString & aFormat)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* readonly attribute DOMString metadata; */
+NS_IMETHODIMP
+nsFontFace::GetMetadata(nsAString & aMetadata)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
new file mode 100644
--- /dev/null
+++ b/layout/inspector/src/nsFontFace.h
@@ -0,0 +1,59 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __nsFontFace_h__
+#define __nsFontFace_h__
+
+#include "nsIDOMFontFace.h"
+
+#include "gfxFont.h"
+
+class nsFontFace : public nsIDOMFontFace
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMFONTFACE
+
+  nsFontFace(gfxFontEntry* aFontEntry);
+  virtual ~nsFontFace();
+
+  gfxFontEntry* GetFontEntry() const { return mFontEntry.get(); }
+
+protected:
+  nsRefPtr<gfxFontEntry> mFontEntry;
+};
+
+#endif // __nsFontFace_h__
new file mode 100644
--- /dev/null
+++ b/layout/inspector/src/nsFontFaceList.cpp
@@ -0,0 +1,118 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsFontFaceList.h"
+#include "nsFontFace.h"
+#include "gfxFont.h"
+
+nsFontFaceList::nsFontFaceList()
+{
+  mFontFaces.Init();
+}
+
+nsFontFaceList::~nsFontFaceList()
+{
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsISupports
+
+NS_IMPL_ISUPPORTS1(nsFontFaceList, nsIDOMFontFaceList)
+
+////////////////////////////////////////////////////////////////////////
+// nsIDOMFontFaceList
+
+/* nsIDOMFontFace item (in unsigned long index); */
+struct FindByIndexData {
+  PRUint32 mTarget;
+  PRUint32 mCurrent;
+  nsIDOMFontFace* mResult;
+};
+
+static PLDHashOperator
+FindByIndex(gfxFontEntry* aKey, nsIDOMFontFace* aData, void* aUserData)
+{
+  FindByIndexData* data = static_cast<FindByIndexData*>(aUserData);
+  if (data->mCurrent == data->mTarget) {
+    data->mResult = aData;
+    return PL_DHASH_STOP;
+  }
+  data->mCurrent++;
+  return PL_DHASH_NEXT;
+}
+
+NS_IMETHODIMP
+nsFontFaceList::Item(PRUint32 index, nsIDOMFontFace **_retval NS_OUTPARAM)
+{
+  NS_ENSURE_TRUE(index < mFontFaces.Count(), NS_ERROR_INVALID_ARG);
+  FindByIndexData userData;
+  userData.mTarget = index;
+  userData.mCurrent = 0;
+  userData.mResult = nsnull;
+  mFontFaces.EnumerateRead(FindByIndex, &userData);
+  NS_ASSERTION(userData.mResult != nsnull, "null entry in nsFontFaceList?");
+  NS_IF_ADDREF(*_retval = userData.mResult);
+  return NS_OK;
+}
+
+/* readonly attribute unsigned long length; */
+NS_IMETHODIMP
+nsFontFaceList::GetLength(PRUint32 *aLength)
+{
+  *aLength = mFontFaces.Count();
+  return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsFontFaceList
+
+nsresult
+nsFontFaceList::AddFontsFromTextRun(gfxTextRun* aTextRun,
+                                    PRUint32 aOffset, PRUint32 aLength)
+{
+  gfxTextRun::GlyphRunIterator iter(aTextRun, aOffset, aLength);
+  while (iter.NextRun()) {
+    gfxFontEntry *fe = iter.GetGlyphRun()->mFont->GetFontEntry();
+    if (!mFontFaces.GetWeak(fe)) {
+      nsCOMPtr<nsFontFace> ff = new nsFontFace(fe);
+      if (!mFontFaces.Put(fe, ff)) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+    }
+  }
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/layout/inspector/src/nsFontFaceList.h
@@ -0,0 +1,65 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __nsFontFaceList_h__
+#define __nsFontFaceList_h__
+
+#include "nsIDOMFontFaceList.h"
+#include "nsIDOMFontFace.h"
+#include "nsCOMPtr.h"
+#include "nsInterfaceHashtable.h"
+#include "nsHashKeys.h"
+#include "gfxFont.h"
+
+class gfxTextRun;
+
+class nsFontFaceList : public nsIDOMFontFaceList
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMFONTFACELIST
+
+  nsFontFaceList();
+  virtual ~nsFontFaceList();
+
+  nsresult AddFontsFromTextRun(gfxTextRun* aTextRun,
+                               PRUint32 aOffset, PRUint32 aLength);
+
+protected:
+  nsInterfaceHashtable<nsPtrHashKey<gfxFontEntry>,nsIDOMFontFace> mFontFaces;
+};
+
+#endif // __nsFontFaceList_h__