Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Fri, 06 Jan 2012 17:03:12 -0800
changeset 105570 266b7ffc925d7b04bd7ecca549dde55047758676
parent 105568 2f73f3274b267adb5fd6447ddb640b31fc930189 (current diff)
parent 83970 5a446202be5fa2633e4cc1c6113a6ebded512882 (diff)
child 105571 df3fab333dbc7011bc12816f1cf3d521107465bf
push id14706
push usereakhgari@mozilla.com
push dateTue, 11 Sep 2012 20:39:52 +0000
treeherdermozilla-inbound@d50bf1edaabe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone12.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
Merge from mozilla-central.
browser/app/profile/firefox.js
browser/base/content/browser.js
browser/base/content/browser.xul
browser/base/content/tabbrowser.xml
browser/components/nsBrowserGlue.js
build/mobile/devicemanagerADB.py
content/base/src/nsDocument.cpp
content/base/src/nsImageLoadingContent.cpp
content/base/src/nsStubImageDecoderObserver.cpp
content/base/src/nsWebSocket.cpp
content/canvas/src/nsCanvasRenderingContext2D.cpp
content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
content/html/content/src/nsHTMLMediaElement.cpp
content/html/content/src/nsTextEditorState.cpp
content/media/nsBuiltinDecoder.cpp
content/media/nsBuiltinDecoderStateMachine.cpp
content/media/nsBuiltinDecoderStateMachine.h
content/svg/content/src/nsSVGFilters.cpp
content/svg/content/src/nsSVGFilters.h
dom/base/nsDOMWindowUtils.cpp
dom/base/nsGlobalWindowCommands.cpp
dom/indexedDB/IndexedDatabaseManager.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
dom/src/storage/nsDOMStoragePersistentDB.cpp
dom/workers/RuntimeService.cpp
editor/libeditor/text/nsTextEditRules.cpp
editor/libeditor/text/tests/Makefile.in
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/d3d10/LayerManagerD3D10.cpp
gfx/layers/d3d9/LayerManagerD3D9.cpp
gfx/layers/opengl/LayerManagerOGL.cpp
gfx/src/nsFontMetrics.cpp
gfx/tests/gfxFontSelectionTest.cpp
gfx/thebes/gfxASurface.h
gfx/thebes/gfxCoreTextShaper.cpp
gfx/thebes/gfxCoreTextShaper.h
gfx/thebes/gfxDWriteShaper.cpp
gfx/thebes/gfxDWriteShaper.h
gfx/thebes/gfxFT2Fonts.cpp
gfx/thebes/gfxFT2Fonts.h
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxGDIFont.cpp
gfx/thebes/gfxGDIFont.h
gfx/thebes/gfxGDIShaper.cpp
gfx/thebes/gfxGDIShaper.h
gfx/thebes/gfxHarfBuzzShaper.cpp
gfx/thebes/gfxHarfBuzzShaper.h
gfx/thebes/gfxMacFont.cpp
gfx/thebes/gfxMacFont.h
gfx/thebes/gfxPangoFonts.cpp
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
gfx/thebes/gfxPlatformFontList.cpp
gfx/thebes/gfxTextRunCache.cpp
gfx/thebes/gfxTextRunCache.h
gfx/thebes/gfxTextRunWordCache.cpp
gfx/thebes/gfxTextRunWordCache.h
gfx/thebes/gfxUniscribeShaper.cpp
gfx/thebes/gfxUniscribeShaper.h
gfx/thebes/gfxWindowsPlatform.cpp
gfx/thebes/gfxWindowsPlatform.h
image/src/RasterImage.cpp
image/src/VectorImage.cpp
image/src/imgRequest.cpp
image/src/imgRequestProxy.cpp
ipc/chromium/src/chrome/common/ipc_message_utils.h
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/Parser.cpp
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsinterpinlines.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsopcode.cpp
js/src/jsopcode.h
js/src/jsopcode.tbl
js/src/jsprvtd.h
js/src/jsscope.cpp
js/src/jsstr.cpp
js/src/jsxdrapi.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FastOps.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MonoIC.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/StubCalls.cpp
js/src/methodjit/StubCalls.h
js/src/shell/js.cpp
layout/base/nsImageLoader.cpp
layout/base/nsImageLoader.h
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
layout/generic/nsBulletFrame.cpp
layout/generic/nsBulletFrame.h
layout/generic/nsImageFrame.cpp
layout/generic/nsImageFrame.h
layout/generic/nsSelection.cpp
layout/generic/nsTextFrameThebes.cpp
layout/generic/nsTextFrameUtils.h
layout/generic/nsTextRunTransformations.cpp
layout/generic/nsTextRunTransformations.h
layout/svg/base/src/nsSVGGlyphFrame.cpp
layout/svg/base/src/nsSVGImageFrame.cpp
layout/xul/base/src/nsImageBoxFrame.cpp
layout/xul/base/src/nsImageBoxFrame.h
layout/xul/base/src/tree/src/nsTreeImageListener.cpp
layout/xul/base/src/tree/src/nsTreeImageListener.h
mobile/android/base/GeckoApp.java
mobile/android/base/Makefile.in
netwerk/cache/nsCacheService.cpp
netwerk/cache/nsCacheService.h
netwerk/cache/nsDiskCacheDevice.cpp
netwerk/cache/nsDiskCacheDevice.h
netwerk/cookie/nsCookieService.cpp
netwerk/protocol/http/nsHttpPipeline.cpp
storage/public/mozStorageHelper.h
storage/src/VacuumManager.cpp
storage/src/mozStorageConnection.cpp
testing/mochitest/runtests.py
testing/mochitest/runtestsremote.py
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
toolkit/content/widgets/videocontrols.xml
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js
toolkit/system/gnome/nsAlertsIconListener.cpp
toolkit/themes/winstripe/mozapps/extensions/extensions.css
toolkit/themes/winstripe/mozapps/extensions/selectAddons.css
widget/android/nsWindow.cpp
widget/cocoa/nsMenuItemIconX.mm
xpcom/base/nsMemoryReporterManager.cpp
--- a/.gitignore
+++ b/.gitignore
@@ -18,17 +18,17 @@ ID
 /configure
 /config.cache
 /config.log
 
 # Empty marker file that's generated when we check out NSS
 security/manager/.nss.checkout
 
 # Build directories
-obj/*
+obj*/
 
 # Build directories for js shell
 */_DBG.OBJ/
 */_OPT.OBJ/
 
 # SpiderMonkey configury
 js/src/configure
 js/src/autom4te.cache
--- a/accessible/tests/mochitest/events/test_scroll.xul
+++ b/accessible/tests/mochitest/events/test_scroll.xul
@@ -27,17 +27,17 @@
 
   <script type="application/javascript"
           src="chrome://mochikit/content/chrome-harness.js"/>
 
   <script type="application/javascript">
   <![CDATA[
 
     ////////////////////////////////////////////////////////////////////////////
-    // Hack to make xul:tabbrowser work
+    // Hacks to make xul:tabbrowser work
 
     const Ci = Components.interfaces;
     const CC = Components.classes;
 
     Components.utils.import("resource://gre/modules/Services.jsm");
 
     var handleDroppedLink = null;
 
@@ -48,16 +48,18 @@
     };
 
     var gURLBar = {
       focused: false
     };
 
     var gFindBarInitialized = false;
 
+    function goSetCommandEnabled() {}
+
     ////////////////////////////////////////////////////////////////////////////
     // Tests
 
     function getTabDocument()
     {
       return getNode("tabBrowser").selectedBrowser.contentDocument;
     }
 
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -1,8 +1,10 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* ***** 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/
  *
@@ -55,18 +57,18 @@ XPCOMUtils.defineLazyGetter(Services, 's
 });
 XPCOMUtils.defineLazyGetter(Services, 'fm', function() {
   return Cc['@mozilla.org/focus-manager;1']
            .getService(Ci.nsIFocusManager);
 });
 
 // In order to use http:// scheme instead of file:// scheme
 // (that is much more restricted) the following code kick-off
-// a local http server listening on http://127.0.0.1:8888 and
-// http://localhost:8888.
+// a local http server listening on http://127.0.0.1:7777 and
+// http://localhost:7777.
 function startupHttpd(baseDir, port) {
   const httpdURL = 'chrome://browser/content/httpd.js';
   let httpd = {};
   Services.scriptloader.loadSubScript(httpdURL, httpd);
   let server = new httpd.nsHttpServer();
   server.registerDirectory('/', new LocalFile(baseDir));
   server.registerContentType('appcache', 'text/cache-manifest');
   server.start(port);
@@ -86,16 +88,19 @@ function addPermissions(urls) {
     permissions.forEach(function(permission) {
       Services.perms.add(uri, permission, allow);
     });
   });
 }
 
 
 var shell = {
+  // FIXME/bug 678695: this should be a system setting
+  preferredScreenBrightness: 1.0,
+
   get home() {
     delete this.home;
     return this.home = document.getElementById('homescreen');
   },
 
   get homeURL() {
     try {
       let homeSrc = Services.env.get('B2G_HOMESCREEN');
@@ -134,17 +139,17 @@ var shell = {
       let fileScheme = 'file://';
       if (homeURL.substring(0, fileScheme.length) == fileScheme) {
         homeURL = homeURL.replace(fileScheme, '');
 
         let baseDir = homeURL.split('/');
         baseDir.pop();
         baseDir = baseDir.join('/');
 
-        const SERVER_PORT = 8888;
+        const SERVER_PORT = 6666;
         startupHttpd(baseDir, SERVER_PORT);
 
         let baseHost = 'http://localhost';
         homeURL = homeURL.replace(baseDir, baseHost + ':' + SERVER_PORT);
       }
       addPermissions([homeURL]);
     } catch (e) {
       let msg = 'Fatal error during startup: [' + e + '[' + homeURL + ']';
@@ -190,27 +195,28 @@ var shell = {
   handleEvent: function shell_handleEvent(evt) {
     switch (evt.type) {
       case 'keypress':
         switch (evt.keyCode) {
           case evt.DOM_VK_HOME:
             this.sendEvent(this.home.contentWindow, 'home');
             break;
           case evt.DOM_VK_SLEEP:
-            screen.mozEnabled = !screen.mozEnabled;
+            this.toggleScreen();
             break;
           case evt.DOM_VK_ESCAPE:
             if (evt.defaultPrevented)
               return;
             this.doCommand('cmd_close');
             break;
         }
         break;
       case 'load':
         this.home.removeEventListener('load', this, true);
+        this.turnScreenOn();
         this.sendEvent(window, 'ContentStart');
         break;
       case 'MozApplicationManifest':
         try {
           if (!Services.prefs.getBoolPref('browser.cache.offline.enable'))
             return;
 
           let contentWindow = evt.originalTarget.defaultView;
@@ -243,17 +249,31 @@ var shell = {
         }
         break;
     }
   },
   sendEvent: function shell_sendEvent(content, type, details) {
     let event = content.document.createEvent('CustomEvent');
     event.initCustomEvent(type, true, true, details ? details : {});
     content.dispatchEvent(event);
-  }
+  },
+  toggleScreen: function shell_toggleScreen() {
+    if (screen.mozEnabled)
+      this.turnScreenOff();
+    else
+      this.turnScreenOn();
+  },
+  turnScreenOff: function shell_turnScreenOff() {
+    screen.mozEnabled = false;
+    screen.mozBrightness = 0.0;
+  },
+  turnScreenOn: function shell_turnScreenOn() {
+    screen.mozEnabled = true;
+    screen.mozBrightness = this.preferredScreenBrightness;
+  },
 };
 
 (function VirtualKeyboardManager() {
   let activeElement = null;
   let isKeyboardOpened = false;
 
   let constructor = {
     handleEvent: function vkm_handleEvent(evt) {
--- a/b2g/chrome/content/shell.xul
+++ b/b2g/chrome/content/shell.xul
@@ -35,16 +35,17 @@
    - 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 ***** -->
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         id="shell"
         width="480" height="800"
+        windowtype="navigator:browser"
 #ifdef ANDROID
         sizemode="fullscreen"
 #endif
         style="background: black; overflow: hidden;"
         onload="shell.start();"
         onunload="shell.stop();">
 
   <script type="application/javascript" src="chrome://browser/content/commandUtil.js"/>
--- a/browser/branding/nightly/pref/firefox-branding.js
+++ b/browser/branding/nightly/pref/firefox-branding.js
@@ -1,17 +1,17 @@
 pref("startup.homepage_override_url","http://www.mozilla.org/projects/%APP%/%VERSION%/whatsnew/");
 pref("startup.homepage_welcome_url","http://www.mozilla.org/projects/%APP%/%VERSION%/firstrun/");
 // The time interval between checks for a new version (in seconds)
-pref("app.update.interval", 7200); // 2 hours
+pref("app.update.interval", 3600); // 1 hour
 // The time interval between the downloading of mar file chunks in the
 // background (in seconds)
 pref("app.update.download.backgroundInterval", 60);
-// Give the user x seconds to react before showing the big UI. default=12 hours
-pref("app.update.promptWaitTime", 43200);
+// Give the user x seconds to react before showing the big UI. default=1 hour
+pref("app.update.promptWaitTime", 3600);
 // URL user can browse to manually if for some reason all update installation
 // attempts fail.
 pref("app.update.url.manual", "http://nightly.mozilla.org/");
 // A default value for the "More information about this update" link
 // supplied in the "An update is available" page of the update wizard. 
 pref("app.update.url.details", "http://www.mozilla.org/projects/%APP%/");
 
 // Release notes and vendor URLs
--- a/browser/components/privatebrowsing/test/unit/test_privatebrowsingwrapper_removeDataFromDomain.js
+++ b/browser/components/privatebrowsing/test/unit/test_privatebrowsingwrapper_removeDataFromDomain.js
@@ -37,13 +37,18 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /**
  * Test added with bug 460086 to test the behavior of the new API that was added
  * to remove all traces of visiting a site.
  */
 
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 function run_test() {
   PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing-wrapper;1";
   load("do_test_removeDataFromDomain.js");
   do_test();
+
+  // Shutdown the download manager.
+  Services.obs.notifyObservers(null, "quit-application", null);
 }
--- a/browser/components/privatebrowsing/test/unit/test_privatebrowsingwrapper_removeDataFromDomain_activeDownloads.js
+++ b/browser/components/privatebrowsing/test/unit/test_privatebrowsingwrapper_removeDataFromDomain_activeDownloads.js
@@ -37,13 +37,18 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /**
  * Test added with bug 460086 to test the behavior of the new API that was added
  * to remove all traces of visiting a site.
  */
 
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 function run_test() {
   PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing;1";
   load("do_test_removeDataFromDomain_activeDownloads.js");
   do_test();
+
+  // Shutdown the download manager.
+  Services.obs.notifyObservers(null, "quit-application", null);
 }
--- a/browser/components/privatebrowsing/test/unit/test_removeDataFromDomain.js
+++ b/browser/components/privatebrowsing/test/unit/test_removeDataFromDomain.js
@@ -37,13 +37,18 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /**
  * Test added with bug 460086 to test the behavior of the new API that was added
  * to remove all traces of visiting a site.
  */
 
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 function run_test() {
   PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing;1";
   load("do_test_removeDataFromDomain.js");
   do_test();
+
+  // Shutdown the download manager.
+  Services.obs.notifyObservers(null, "quit-application", null);
 }
--- a/browser/components/privatebrowsing/test/unit/test_removeDataFromDomain_activeDownloads.js
+++ b/browser/components/privatebrowsing/test/unit/test_removeDataFromDomain_activeDownloads.js
@@ -37,13 +37,18 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /**
  * Test added with bug 460086 to test the behavior of the new API that was added
  * to remove all traces of visiting a site.
  */
 
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 function run_test() {
   PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing;1";
   load("do_test_removeDataFromDomain_activeDownloads.js");
   do_test();
+
+  // Shutdown the download manager.
+  Services.obs.notifyObservers(null, "quit-application", null);
 }
--- a/browser/themes/winstripe/browser-aero.css
+++ b/browser/themes/winstripe/browser-aero.css
@@ -32,17 +32,17 @@
     -moz-margin-start: 1px;
   }
 
   .panel-promo-message {
     font-style: italic;
   }
 }
 
-@media all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme) {
   #navigator-toolbox > toolbar:not(:-moz-lwtheme),
   #browser-bottombox:not(:-moz-lwtheme) {
     background-color: @customToolbarColor@;
   }
 
   .tabbrowser-tab:not(:-moz-lwtheme),
   .tabs-newtab-button:not(:-moz-lwtheme) {
     background-image: @toolbarShadowOnTab@, @bgTabTexture@,
@@ -77,17 +77,17 @@
   }
 
   .menu-accel,
   .menu-iconic-accel {
     color: graytext;
   }
 }
 
-@media all and (-moz-windows-compositor) {
+@media (-moz-windows-compositor) {
   /* These should be hidden w/ glass enabled. Windows draws its own buttons. */
   .titlebar-button {
     display: none;
   }
 
   #main-window[sizemode="maximized"] #titlebar-buttonbox {
     -moz-margin-end: 3px;
   }
@@ -343,17 +343,17 @@
 
 #minimize-button:-moz-locale-dir(rtl),
 #restore-button:-moz-locale-dir(rtl),
 #close-button:-moz-locale-dir(rtl) {
   -moz-transform: scaleX(-1);
 }
 
 /* ::::: splitmenu highlight style that imitates Windows 7 start menu ::::: */
-@media all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme) {
   .splitmenu-menuitem,
   .splitmenu-menu {
     -moz-appearance: none;
     padding-top: 2px;
     padding-bottom: 2px;
     border: 1px solid transparent;
   }
   .splitmenu-menuitem {
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -160,24 +160,24 @@
   color: white;
   text-shadow: 0 0 1px rgba(0,0,0,.7),
                0 1px 1.5px rgba(0,0,0,.5);
   font-weight: bold;
   padding: 0 1.5em .05em;
   margin: 0 0 2px;
 }
 
-@media all and (-moz-windows-classic) {
+@media (-moz-windows-classic) {
   #appmenu-button {
     margin-bottom: 1px;
   }
 }
 
 %ifndef WINSTRIPE_AERO
-@media all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme) {
   #main-window[sizemode="normal"] #appmenu-button {
     margin-bottom: 5px;
   }
 }
 %endif
 
 #appmenu-button:hover:active,
 #appmenu-button[open] {
@@ -310,17 +310,17 @@
 .appmenu-edit-button[disabled="true"] {
   opacity: .3;
 }
 
 #appmenuPrimaryPane {
   -moz-border-end: 1px solid ThreeDShadow;
 }
 
-@media all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme) {
   #appmenu-popup {
     -moz-appearance: none;
     background: white;
     border: 1px solid ThreeDShadow;
   }
   #appmenuPrimaryPane {
     background-color: rgba(255,255,255,0.5);
     padding: 2px;
@@ -478,17 +478,17 @@
 #main-window[sizemode="normal"] > #titlebar {
   -moz-appearance: -moz-window-titlebar;
 }
 
 #main-window[sizemode="maximized"] > #titlebar {
   -moz-appearance: -moz-window-titlebar-maximized;
 }
 
-@media all and (-moz-windows-classic) {
+@media (-moz-windows-classic) {
   #main-window[sizemode="normal"] > #titlebar > #titlebar-content > #appmenu-button-container {
     margin-top: 4px;
   }
 }
 
 #titlebar-buttonbox {
   -moz-appearance: -moz-window-button-box;
 }
@@ -1209,17 +1209,17 @@ toolbar[mode="full"] .toolbarbutton-1 > 
 }
 
 #urlbar {
   width: 7em;
   min-width: 7em;
   -moz-padding-end: 2px;
 }
 
-@media all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme) {
   #urlbar,
   .searchbar-textbox {
     @navbarTextboxCustomBorder@
   }
 }
 
 #urlbar:-moz-lwtheme,
 .searchbar-textbox:-moz-lwtheme {
@@ -1707,17 +1707,17 @@ richlistitem[type~="action"][actiontype=
 #editBMPanel_folderTree {
   min-width: 27em;
 }
 
 .panel-promo-box {
   margin: 16px 0 -2px;
 }
 
-@media all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme) {
   .panel-promo-box {
     margin: 8px -16px -16px;
     padding: 8px 16px;
 %ifndef WINSTRIPE_AERO
     border-bottom-left-radius: 6px;
     border-bottom-right-radius: 6px;
 %endif
     background-color: #f1f5fb;
@@ -1790,17 +1790,17 @@ richlistitem[type~="action"][actiontype=
 
 #TabsToolbar:not(:-moz-lwtheme),
 #TabsToolbar[tabsontop=false] {
   background-image:
     -moz-linear-gradient(bottom, @toolbarShadowColor@ 1px, rgba(0,0,0,.05) 1px, transparent 50%);
 }
 
 %ifndef WINSTRIPE_AERO
-@media all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme) {
   #main-window[sizemode=normal] #TabsToolbar {
     padding-left: 2px;
     padding-right: 2px;
   }
 }
 %endif
 
 .tabbrowser-tab,
@@ -1822,17 +1822,17 @@ richlistitem[type~="action"][actiontype=
 
 .tabbrowser-tab:hover,
 .tabs-newtab-button:hover {
   background-image: @toolbarShadowOnTab@, @bgTabTextureHover@,
                     -moz-linear-gradient(-moz-dialog, -moz-dialog);
 }
 
 %ifndef WINSTRIPE_AERO
-@media all and (-moz-windows-theme: luna-blue) {
+@media (-moz-windows-theme: luna-blue) {
   .tabbrowser-tab,
   .tabs-newtab-button {
     background-image: @toolbarShadowOnTab@,
                       -moz-linear-gradient(hsla(51,34%,89%,.9), hsla(51,15%,79%,.9) 1px, hsla(51,9%,68%,.9));
   }
 
   .tabbrowser-tab:hover,
   .tabs-newtab-button:hover {
@@ -1951,17 +1951,17 @@ richlistitem[type~="action"][actiontype=
 }
 
 .tab-close-button[selected="true"] {
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
 /* Tab scrollbox arrow, tabstrip new tab and all-tabs buttons */
 
-@media all and (-moz-touch-enabled) {
+@media (-moz-touch-enabled) {
   .tabbrowser-arrowscrollbox > .scrollbutton-up,
   .tabbrowser-arrowscrollbox > .scrollbutton-down,
   #TabsToolbar .toolbarbutton-1 {
     min-width: 8.1mozmm;
   }
 
   .tabs-newtab-button {
     min-width: 10mozmm;
--- a/browser/themes/winstripe/places/organizer-aero.css
+++ b/browser/themes/winstripe/places/organizer-aero.css
@@ -16,27 +16,27 @@
 
   #placesToolbar {
     -moz-appearance: none;
     background-color: -moz-Dialog;
     color: -moz-dialogText;
   }
 }
 
-@media all and (-moz-windows-compositor) {
+@media (-moz-windows-compositor) {
   #placesToolbox {
     border-top: none;
   }
 
   #placesToolbar {
     background-image: -moz-linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
   }
 }
 
-@media all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme) {
   #placesView,
   #searchModifiers,
   #infoPane,
   #placesList,
   #placeContent {
     background-color: #EEF3FA;
   }
 
--- a/build/mobile/devicemanagerADB.py
+++ b/build/mobile/devicemanagerADB.py
@@ -107,27 +107,31 @@ class DeviceManagerADB(DeviceManager):
   #  success: remoteDir
   #  failure: None
   def pushDir(self, localDir, remoteDir):
     # adb "push" accepts a directory as an argument, but if the directory
     # contains symbolic links, the links are pushed, rather than the linked
     # files; we either zip/unzip or push file-by-file to get around this 
     # limitation
     try:
+      if (not self.dirExists(remoteDir)):
+        self.mkDirs(remoteDir+"/x")
       if (self.useZip):
         localZip = tempfile.mktemp()+".zip"
         remoteZip = remoteDir + "/adbdmtmp.zip"
         subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir)
         self.pushFile(localZip, remoteZip)
         os.remove(localZip)
-        self.checkCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir])
+        data = self.runCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir]).stdout.read()
         self.checkCmdAs(["shell", "rm", remoteZip])
+        if (re.search("unzip: exiting", data) or re.search("Operation not permitted", data)):
+          print "zip/unzip failure: falling back to normal push"
+          self.useZip = False
+          self.pushDir(localDir, remoteDir)
       else:
-        if (not self.dirExists(remoteDir)):
-          self.mkDirs(remoteDir+"/x")
         for root, dirs, files in os.walk(localDir, followlinks='true'):
           relRoot = os.path.relpath(root, localDir)
           for file in files:
             localFile = os.path.join(root, file)
             remoteFile = remoteDir + "/"
             if (relRoot!="."):
               remoteFile = remoteFile + relRoot + "/"
             remoteFile = remoteFile + file
@@ -604,17 +608,17 @@ class DeviceManagerADB(DeviceManager):
       if (self.fileExists(devroot + "/sanity/tmpfile")):
         print "will execute commands via run-as " + packageName
         self.packageName = packageName
         self.useRunAs = True
       self.checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"])
       self.checkCmd(["shell", "run-as", packageName, "rm", "-r", devroot + "/sanity"])
       
   def isUnzipAvailable(self):
-    data = self.runCmd(["shell", "unzip"]).stdout.read()
+    data = self.runCmdAs(["shell", "unzip"]).stdout.read()
     if (re.search('Usage', data)):
       return True
     else:
       return False
 
   def isLocalZipAvailable(self):
     try:
       subprocess.check_call(["zip", "-?"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
--- a/build/mobile/robocop/Actions.java.in
+++ b/build/mobile/robocop/Actions.java.in
@@ -39,22 +39,31 @@
 
 package @ANDROID_PACKAGE_NAME@;
 import java.util.List;
 
 public interface Actions {
   public enum SpecialKey {
     DOWN, UP, LEFT, RIGHT, ENTER
   }
+
+  public interface EventExpecter {
+    /** Blocks until the event has been received. Subsequent calls will return immediately. */
+    public void blockForEvent();
+    /** Polls to see if the event has been received. Once this returns true, subsequent calls will also return true. */
+    public boolean eventReceived();
+  }
+
   /**
-   * Waits for a gecko event to be sent from the Gecko instance.
+   * Listens for a gecko event to be sent from the Gecko instance.
+   * The returned object can be used to test if the event has been
+   * received. Note that only one event is listened for.
    * 
    * @param geckoEvent The geckoEvent JSONObject's type
    */
-
-  void waitForGeckoEvent(String geckoEvent);
+  EventExpecter expectGeckoEvent(String geckoEvent);
   // Send the string kewsToSend to the application 
   void sendKeys(String keysToSend);
   //Send any of the above keys to the element
   void sendSpecialKey(SpecialKey button);
 
   void drag(int startingX, int endingX, int startingY, int endingY);
 }
--- a/build/mobile/robocop/Driver.java.in
+++ b/build/mobile/robocop/Driver.java.in
@@ -33,27 +33,30 @@
  * 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 ***** */
 
 package @ANDROID_PACKAGE_NAME@;
+
 import java.util.List;
+import android.app.Activity;
 
 public interface Driver {
   /**
    * Find the first Element using the given method.
    * 
+   * @param activity The activity the element belongs to
    * @param name The name of the element
    * @return The first matching element on the current context
    * @throws RoboCopException If no matching elements are found
    */
-  Element findElement(String name);
+  Element findElement(Activity activity, String name);
 
   /**
    * Sets up scroll handling so that data is received from the extension.
    */
   void setupScrollHandling();
 
   int getPageHeight();
   int getScrollHeight();
--- a/build/mobile/robocop/FennecNativeActions.java.in
+++ b/build/mobile/robocop/FennecNativeActions.java.in
@@ -79,19 +79,16 @@ public class FennecNativeActions impleme
   private Class gel;
   private Class ge;
   private Class gas;
   private Method registerGEL;
   private Method unregisterGEL;
   private Method sendGE;
 
 
-  // If waiting for an event.
-  private SynchronousQueue waitqueue = new SynchronousQueue<Boolean>();
-
   public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation){
     this.solo = robocop;
     this.instr = instrumentation;
     // Set up reflexive access of java classes and methods.
     try {
       classLoader = activity.getClassLoader();
       gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
       ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent");
@@ -111,61 +108,106 @@ public class FennecNativeActions impleme
      } catch (NoSuchMethodException e) {
        e.printStackTrace();
      } catch (IllegalArgumentException e) {
        e.printStackTrace();
      }
   }
 
   class wakeInvocationHandler implements InvocationHandler {
-    public wakeInvocationHandler(){};
+    private final GeckoEventExpecter mEventExpecter;
+
+    public wakeInvocationHandler(GeckoEventExpecter expecter) {
+      mEventExpecter = expecter;
+    }
+
     public Object invoke(Object proxy, Method method, Object[] args) {
       String methodName = method.getName();
       //Depending on the method, return a completely different type.
       if(methodName.equals("toString")) {
         return "wakeInvocationHandler";
       }
       if(methodName.equals("equals")) {
         return this == args[0];
       }
       if(methodName.equals("clone")) {
         return this;
       }
       if(methodName.equals("hashCode")) {
         return 314;
       }
       Log.i("Robocop", "Waking up on "+methodName);
-      waitqueue.offer(new Boolean(true));
+      mEventExpecter.notifyOfEvent();
       return null;
     }
   }
+
+  class GeckoEventExpecter implements EventExpecter {
+    private final String mGeckoEvent;
+    private final Object[] mRegistrationParams;
+    private boolean mEventReceived;
+
+    GeckoEventExpecter(String geckoEvent, Object[] registrationParams) {
+      mGeckoEvent = geckoEvent;
+      mRegistrationParams = registrationParams;
+    }
+
+    public synchronized void blockForEvent() {
+      while (! mEventReceived) {
+        try {
+          this.wait();
+        } catch (InterruptedException ie) {
+          ie.printStackTrace();
+          break;
+        }
+      }
+      Log.i("Robocop", "unblocked on expecter for " + mGeckoEvent);
+    }
+
+    public synchronized boolean eventReceived() {
+      return mEventReceived;
+    }
+
+    void notifyOfEvent() {
+      try {
+        unregisterGEL.invoke(null, mRegistrationParams);
+      } catch (IllegalAccessException e) {
+        e.printStackTrace();
+      } catch (InvocationTargetException e) {
+        e.printStackTrace();
+      }
+      Log.i("Robocop", "received event " + mGeckoEvent);
+      synchronized (this) {
+        mEventReceived = true;
+        this.notifyAll();
+      }
+    }
+  }
   
-  public void waitForGeckoEvent(String geckoEvent) {
+  public EventExpecter expectGeckoEvent(String geckoEvent) {
     Log.i("Robocop", "waiting for "+geckoEvent);
     try {
       Class [] interfaces = new Class[1];
       interfaces[0] = gel;
       Object[] finalParams = new Object[2];
       finalParams[0] = geckoEvent;
      
-      wakeInvocationHandler wIH = new wakeInvocationHandler();
+      GeckoEventExpecter expecter = new GeckoEventExpecter(geckoEvent, finalParams);
+      wakeInvocationHandler wIH = new wakeInvocationHandler(expecter);
       Object proxy = Proxy.newProxyInstance(classLoader, interfaces, wIH);
       finalParams[1] = proxy;
       registerGEL.invoke(null, finalParams);
       
-      waitqueue.take();
-      unregisterGEL.invoke(null, finalParams);
+      return expecter;
     } catch (IllegalAccessException e) {
       e.printStackTrace();
     } catch (InvocationTargetException e) {
       e.printStackTrace();
-    } catch (InterruptedException e) {
-      e.printStackTrace();
     }
-    Log.i("Robocop", "wait ends for: "+geckoEvent);
+    return null;
   }
 
   public void sendSpecialKey(SpecialKey button) {
     switch( button) {
       case DOWN:
         instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN);
         break;
       case UP:
--- a/build/mobile/robocop/FennecNativeDriver.java.in
+++ b/build/mobile/robocop/FennecNativeDriver.java.in
@@ -162,18 +162,17 @@ public class FennecNativeDriver implemen
   }
   public int getGeckoWidth() {
     if(!geckoInfo) {
       getGeckoInfo();
     }
     return geckoWidth;
   }
 
-  @Override
-  public Element findElement(String name) {
+  public Element findElement(Activity activity, String name) {
     if (name == null)
       throw new IllegalArgumentException("Can not findElements when passed a null");
     if (locators.containsKey(name)){
       return new FennecNativeElement(Integer.decode((String)locators.get(name)), activity, solo);
     }
     throw new RoboCopException("Element does not exist in the list");
   }
 
--- a/build/mobile/robocop/FennecNativeElement.java.in
+++ b/build/mobile/robocop/FennecNativeElement.java.in
@@ -51,64 +51,59 @@ import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.TextSwitcher;
 import android.app.Instrumentation;
 import android.util.Log;
 import com.jayway.android.robotium.solo.Solo;
 import java.util.concurrent.SynchronousQueue;
 
 public class FennecNativeElement implements Element {
+  private final Activity mActivity;
   private Integer id;
-  private Activity currentActivity;
   private Solo robocop;
 
   public FennecNativeElement(Integer id, Activity activity, Solo solo){
     this.id = id;
+    mActivity = activity;
     robocop = solo;
-    currentActivity = activity;
   }
 
   public Integer getId() {
     return id;
   }
 
-  @Override
   public void click() {
     final SynchronousQueue syncQueue = new SynchronousQueue();
-    currentActivity = robocop.getCurrentActivity();
-    currentActivity.runOnUiThread(
+    mActivity.runOnUiThread(
         new Runnable() {
           public void run() {
-            View view = (View)currentActivity.findViewById(id);
+            View view = (View)mActivity.findViewById(id);
             if(view != null) {
               view.performClick();
             } else {
               throw new RoboCopException("click: unable to find view "+id); 
             }
             syncQueue.offer(new Object());
           }
         });
     try {
       syncQueue.take();
     } catch (InterruptedException e) {
       e.printStackTrace();
     }
   }
 
   private Object text;
-  private Activity elementActivity;
 
-  @Override
   public String getText() {
-    elementActivity = robocop.getCurrentActivity();
     final SynchronousQueue syncQueue = new SynchronousQueue();
-    elementActivity.runOnUiThread(
+    mActivity.runOnUiThread(
         new Runnable() {
           public void run() {
-            View v = elementActivity.findViewById(id);
+            View v = mActivity.findViewById(id);
             if(v instanceof EditText) {
               EditText et = (EditText)v;
               text = et.getEditableText();
             }else if(v instanceof TextSwitcher) {
               TextSwitcher ts = (TextSwitcher)v;
               ts.getNextView();
               text = ((TextView)ts.getCurrentView()).getText();
             }else if(v instanceof ViewGroup) {
@@ -136,14 +131,32 @@ public class FennecNativeElement impleme
       e.printStackTrace();
     }
     if(text == null) {
       throw new RoboCopException("getText: Text is null for view "+id);
     }
     return text.toString();
   }
 
-  @Override
+  private boolean displayed;
+
   public boolean isDisplayed() {
-    // TODO Auto-generated method stub
-    return false;
+    final SynchronousQueue syncQueue = new SynchronousQueue();
+    displayed = false;
+    mActivity.runOnUiThread(
+        new Runnable() {
+          public void run() {
+            View view = (View)mActivity.findViewById(id);
+            if(view != null) {
+              displayed = true;
+            }
+            syncQueue.offer(new Object());
+          }
+        });
+    try {
+      syncQueue.take();
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
+    return displayed;
   }
+
 }
--- a/content/base/public/nsISelectionController.idl
+++ b/content/base/public/nsISelectionController.idl
@@ -46,17 +46,17 @@
 typedef short SelectionType;
 typedef short SelectionRegion;
 %}
 
 interface nsIDOMNode;
 interface nsISelection;
 interface nsISelectionDisplay;
 
-[scriptable, uuid(e0dd9365-470b-4ee8-b644-54add1c4c73f)]
+[scriptable, uuid(cf30315f-b65d-44c3-8c57-557e36d18fd2)]
 interface nsISelectionController : nsISelectionDisplay
 {
    const short SELECTION_NONE=0;
    const short SELECTION_NORMAL=1;
    const short SELECTION_SPELLCHECK=2;
    const short SELECTION_IME_RAWINPUT=4;
    const short SELECTION_IME_SELECTEDRAWTEXT=8;
    const short SELECTION_IME_CONVERTEDTEXT=16;
@@ -253,20 +253,21 @@ interface nsISelectionController : nsISe
    */
     void scrollPage(in boolean forward);
 
   /** ScrolLine will scroll line up or down dependent on the boolean
    *  @param aForward scroll forward or backwards in selection
    */
 	  void scrollLine(in boolean forward);
 
-  /** ScrolHorizontal will scroll left or right dependent on the boolean
-   *  @param aLeft if true will scroll left. if not will scroll right.
+  /** ScrollCharacter will scroll right or left dependent on the boolean
+   *  @param aRight if true will scroll right. if not will scroll left.
    */
-	  void scrollHorizontal(in boolean left);
+    void scrollCharacter(in boolean right);
+
   /** SelectAll will select the whole page
    */
     void selectAll();
 
   /** CheckVisibility will return true if textnode and offsets are actually rendered 
    *  in the current precontext.
    *  @param aNode textNode to test
    *  @param aStartOffset  offset in dom to first char of textnode to test
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -8902,35 +8902,37 @@ nsDocument::RequestFullScreen(Element* a
   // document, as required by the spec.
   for (PRUint32 i = 0; i < changed.Length(); ++i) {
     DispatchFullScreenChange(changed[changed.Length() - i - 1]);
   }
 
   // Remember this is the requesting full-screen document.
   sFullScreenDoc = do_GetWeakReference(static_cast<nsIDocument*>(this));
 
-  // Make the window full-screen. Note we must make the state changes above
-  // before making the window full-screen, as then the document reports as
-  // being in full-screen mode when the chrome "fullscreen" event fires,
-  // enabling chrome to distinguish between browser and dom full-screen
-  // modes. Also note that nsGlobalWindow::SetFullScreen() (which
-  // SetWindowFullScreen() calls) proxies to the root window in its hierarchy,
-  // and does not operate on the a per-nsIDOMWindow basis.
-  SetWindowFullScreen(this, true);
-
 #ifdef DEBUG
+  // Note assertions must run before SetWindowFullScreen() as that does
+  // synchronous event dispatch which can run script which exits full-screen!
   NS_ASSERTION(GetFullScreenElement() == aElement,
                "Full-screen element should be the requested element!");
   NS_ASSERTION(IsFullScreenDoc(), "Should be full-screen doc");
   nsCOMPtr<nsIDOMHTMLElement> fse;
   GetMozFullScreenElement(getter_AddRefs(fse));
   nsCOMPtr<nsIContent> c(do_QueryInterface(fse));
   NS_ASSERTION(c->AsElement() == aElement,
     "GetMozFullScreenElement should match GetFullScreenElement()");
 #endif
+
+  // Make the window full-screen. Note we must make the state changes above
+  // before making the window full-screen, as then the document reports as
+  // being in full-screen mode when the chrome "fullscreen" event fires,
+  // enabling chrome to distinguish between browser and dom full-screen
+  // modes. Also note that nsGlobalWindow::SetFullScreen() (which
+  // SetWindowFullScreen() calls) proxies to the root window in its hierarchy,
+  // and does not operate on the a per-nsIDOMWindow basis.
+  SetWindowFullScreen(this, true);
 }
 
 NS_IMETHODIMP
 nsDocument::GetMozFullScreenElement(nsIDOMHTMLElement **aFullScreenElement)
 {
   NS_ENSURE_ARG_POINTER(aFullScreenElement);
   *aFullScreenElement = nsnull;
   if (IsFullScreenDoc()) {
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -154,20 +154,21 @@ nsImageLoadingContent::~nsImageLoadingCo
     }                                                                    \
   PR_END_MACRO
 
 
 /*
  * imgIContainerObserver impl
  */
 NS_IMETHODIMP
-nsImageLoadingContent::FrameChanged(imgIContainer* aContainer,
+nsImageLoadingContent::FrameChanged(imgIRequest* aRequest,
+                                    imgIContainer* aContainer,
                                     const nsIntRect* aDirtyRect)
 {
-  LOOP_OVER_OBSERVERS(FrameChanged(aContainer, aDirtyRect));
+  LOOP_OVER_OBSERVERS(FrameChanged(aRequest, aContainer, aDirtyRect));
   return NS_OK;
 }
             
 /*
  * imgIDecoderObserver impl
  */
 NS_IMETHODIMP
 nsImageLoadingContent::OnStartRequest(imgIRequest* aRequest)
--- a/content/base/src/nsStubImageDecoderObserver.cpp
+++ b/content/base/src/nsStubImageDecoderObserver.cpp
@@ -108,13 +108,14 @@ nsStubImageDecoderObserver::OnDiscard(im
 
 NS_IMETHODIMP
 nsStubImageDecoderObserver::OnImageIsAnimated(imgIRequest *aRequest)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsStubImageDecoderObserver::FrameChanged(imgIContainer *aContainer,
+nsStubImageDecoderObserver::FrameChanged(imgIRequest* aRequest,
+                                         imgIContainer *aContainer,
                                          const nsIntRect *aDirtyRect)
 {
     return NS_OK;
 }
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -1563,16 +1563,17 @@ NS_IMETHODIMP
 nsWebSocket::Cancel(nsresult aStatus)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
   if (mDisconnected)
     return NS_OK;
 
   ConsoleError();
+  mClientReasonCode = nsIWebSocketChannel::CLOSE_GOING_AWAY;
   return CloseConnection();
 }
 
 NS_IMETHODIMP
 nsWebSocket::Suspend()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -92,17 +92,16 @@
 
 #include "imgIEncoder.h"
 
 #include "gfxContext.h"
 #include "gfxASurface.h"
 #include "gfxImageSurface.h"
 #include "gfxPlatform.h"
 #include "gfxFont.h"
-#include "gfxTextRunCache.h"
 #include "gfxBlur.h"
 #include "gfxUtils.h"
 
 #include "nsFrameManager.h"
 #include "nsFrameLoader.h"
 #include "nsBidi.h"
 #include "nsBidiPresUtils.h"
 #include "Layers.h"
@@ -2732,22 +2731,21 @@ nsCanvasRenderingContext2D::MeasureText(
 
 /**
  * Used for nsBidiPresUtils::ProcessText
  */
 struct NS_STACK_CLASS nsCanvasBidiProcessor : public nsBidiPresUtils::BidiProcessor
 {
     virtual void SetText(const PRUnichar* text, PRInt32 length, nsBidiDirection direction)
     {
-        mTextRun = gfxTextRunCache::MakeTextRun(text,
-                                                length,
-                                                mFontgrp,
-                                                mThebes,
-                                                mAppUnitsPerDevPixel,
-                                                direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
+        mTextRun = mFontgrp->MakeTextRun(text,
+                                         length,
+                                         mThebes,
+                                         mAppUnitsPerDevPixel,
+                                         direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
     }
 
     virtual nscoord GetWidth()
     {
         gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(0,
                                                                    mTextRun->GetLength(),
                                                                    mDoMeasureBoundingBox ?
                                                                        gfxFont::TIGHT_INK_EXTENTS :
@@ -2806,17 +2804,17 @@ struct NS_STACK_CLASS nsCanvasBidiProces
                            point,
                            0,
                            mTextRun->GetLength(),
                            nsnull,
                            nsnull);
     }
 
     // current text run
-    gfxTextRunCache::AutoTextRun mTextRun;
+    nsAutoPtr<gfxTextRun> mTextRun;
 
     // pointer to the context, may not be the canvas's context
     // if an intermediate surface is being used
     gfxContext* mThebes;
 
     // position of the left side of the string, alphabetic baseline
     gfxPoint mPt;
 
@@ -3150,18 +3148,18 @@ gfxTextRun*
 nsCanvasRenderingContext2D::MakeTextRun(const PRUnichar* aText,
                                         PRUint32         aLength,
                                         PRUint32         aAppUnitsPerDevUnit,
                                         PRUint32         aFlags)
 {
     gfxFontGroup* currentFontStyle = GetCurrentFontStyle();
     if (!currentFontStyle)
         return nsnull;
-    return gfxTextRunCache::MakeTextRun(aText, aLength, currentFontStyle,
-                                        mThebes, aAppUnitsPerDevUnit, aFlags);
+    return currentFontStyle->MakeTextRun(aText, aLength,
+                                         mThebes, aAppUnitsPerDevUnit, aFlags);
 }
 
 
 //
 // line caps/joins
 //
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetLineWidth(float width)
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -89,17 +89,16 @@
 
 #include "imgIEncoder.h"
 
 #include "gfxContext.h"
 #include "gfxASurface.h"
 #include "gfxImageSurface.h"
 #include "gfxPlatform.h"
 #include "gfxFont.h"
-#include "gfxTextRunCache.h"
 #include "gfxBlur.h"
 #include "gfxUtils.h"
 
 #include "nsFrameManager.h"
 #include "nsFrameLoader.h"
 #include "nsBidi.h"
 #include "nsBidiPresUtils.h"
 #include "Layers.h"
@@ -2937,22 +2936,21 @@ nsCanvasRenderingContext2DAzure::Measure
  * Used for nsBidiPresUtils::ProcessText
  */
 struct NS_STACK_CLASS nsCanvasBidiProcessorAzure : public nsBidiPresUtils::BidiProcessor
 {
   typedef nsCanvasRenderingContext2DAzure::ContextState ContextState;
 
   virtual void SetText(const PRUnichar* text, PRInt32 length, nsBidiDirection direction)
   {
-    mTextRun = gfxTextRunCache::MakeTextRun(text,
-                                            length,
-                                            mFontgrp,
-                                            mThebes,
-                                            mAppUnitsPerDevPixel,
-                                            direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
+    mTextRun = mFontgrp->MakeTextRun(text,
+                                     length,
+                                     mThebes,
+                                     mAppUnitsPerDevPixel,
+                                     direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
   }
 
   virtual nscoord GetWidth()
   {
     gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(0,
                                                                mTextRun->GetLength(),
                                                                mDoMeasureBoundingBox ?
                                                                  gfxFont::TIGHT_INK_EXTENTS :
@@ -3091,17 +3089,17 @@ struct NS_STACK_CLASS nsCanvasBidiProces
                                 state.dashOffset),
                   DrawOptions(state.globalAlpha, mCtx->UsedOperation()));
 
       }
     }
   }
 
   // current text run
-  gfxTextRunCache::AutoTextRun mTextRun;
+  nsAutoPtr<gfxTextRun> mTextRun;
 
   // pointer to a screen reference context used to measure text and such
   nsRefPtr<gfxContext> mThebes;
 
   // Pointer to the draw target we should fill our text to
   nsCanvasRenderingContext2DAzure *mCtx;
 
   // position of the left side of the string, alphabetic baseline
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -297,16 +297,21 @@ void nsHTMLMediaElement::ReportLoadError
                                   aParamCount);
 }
 
 
 NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
 {
   nsContentUtils::UnregisterShutdownObserver(this);
 
+  if (!mElement) {
+    // We've been notified by the shutdown observer, and are shutting down.
+    return NS_BINDING_ABORTED;
+  }
+
   // The element is only needed until we've had a chance to call
   // InitializeDecoderForChannel. So make sure mElement is cleared here.
   nsRefPtr<nsHTMLMediaElement> element;
   element.swap(mElement);
 
   if (mLoadID != element->GetCurrentLoadID()) {
     // The channel has been cancelled before we had a chance to create
     // a decoder. Abort, don't dispatch an "error" event, as the new load
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -209,17 +209,17 @@ public:
   NS_IMETHOD WordExtendForDelete(bool aForward);
   NS_IMETHOD LineMove(bool aForward, bool aExtend);
   NS_IMETHOD IntraLineMove(bool aForward, bool aExtend);
   NS_IMETHOD PageMove(bool aForward, bool aExtend);
   NS_IMETHOD CompleteScroll(bool aForward);
   NS_IMETHOD CompleteMove(bool aForward, bool aExtend);
   NS_IMETHOD ScrollPage(bool aForward);
   NS_IMETHOD ScrollLine(bool aForward);
-  NS_IMETHOD ScrollHorizontal(bool aLeft);
+  NS_IMETHOD ScrollCharacter(bool aRight);
   NS_IMETHOD SelectAll(void);
   NS_IMETHOD CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, bool *_retval);
 
 private:
   nsRefPtr<nsFrameSelection> mFrameSelection;
   nsCOMPtr<nsIContent>       mLimiter;
   nsIScrollableFrame        *mScrollFrame;
   nsWeakPtr mPresShellWeak;
@@ -568,22 +568,22 @@ nsTextInputSelectionImpl::ScrollLine(boo
 
   mScrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
                          nsIScrollableFrame::LINES,
                          nsIScrollableFrame::SMOOTH);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsTextInputSelectionImpl::ScrollHorizontal(bool aLeft)
+nsTextInputSelectionImpl::ScrollCharacter(bool aRight)
 {
   if (!mScrollFrame)
     return NS_ERROR_NOT_INITIALIZED;
 
-  mScrollFrame->ScrollBy(nsIntPoint(aLeft ? -1 : 1, 0),
+  mScrollFrame->ScrollBy(nsIntPoint(aRight ? 1 : -1, 0),
                          nsIScrollableFrame::LINES,
                          nsIScrollableFrame::SMOOTH);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsTextInputSelectionImpl::SelectAll()
 {
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -268,17 +268,17 @@ nsresult nsBuiltinDecoder::Play()
   ChangeState(PLAY_STATE_PLAYING);
   return NS_OK;
 }
 
 /**
  * Returns true if aValue is inside a range of aRanges, and put the range
  * index in aIntervalIndex if it is not null.
  * If aValue is not inside a range, false is returned, and aIntervalIndex, if
- * not null, is set to the index of the range which ends immediatly before aValue
+ * not null, is set to the index of the range which ends immediately before aValue
  * (and can be -1 if aValue is before aRanges.Start(0)).
  */
 static bool IsInRanges(nsTimeRanges& aRanges, double aValue, PRInt32& aIntervalIndex) {
   PRUint32 length;
   aRanges.GetLength(&length);
   for (PRUint32 i = 0; i < length; i++) {
     double start, end;
     aRanges.Start(i, &start);
@@ -310,40 +310,42 @@ nsresult nsBuiltinDecoder::Seek(double a
   NS_ENSURE_SUCCESS(res, NS_OK);
 
   seekable.GetLength(&length);
   if (!length) {
     return NS_OK;
   }
 
   // If the position we want to seek to is not in a seekable range, we seek
-  // to the closest position in the seekable ranges instead . If two positions
-  // are equaly close, we seek to the closest position from the currentTime.
+  // to the closest position in the seekable ranges instead. If two positions
+  // are equally close, we seek to the closest position from the currentTime.
   // See seeking spec, point 7 :
-  // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#seeking
+  // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#seeking
   PRInt32 range = 0;
   if (!IsInRanges(seekable, aTime, range)) {
     if (range != -1) {
-      double leftBound, rightBound;
-      res = seekable.End(range, &leftBound);
-      NS_ENSURE_SUCCESS(res, NS_OK);
-      double distanceLeft = NS_ABS(leftBound - aTime);
-
-      double distanceRight = -1;
       if (range + 1 < length) {
-        res = seekable.Start(range+1, &rightBound);
+        double leftBound, rightBound;
+        res = seekable.End(range, &leftBound);
+        NS_ENSURE_SUCCESS(res, NS_OK);
+        res = seekable.Start(range + 1, &rightBound);
         NS_ENSURE_SUCCESS(res, NS_OK);
-        distanceRight = NS_ABS(rightBound - aTime);
+        double distanceLeft = NS_ABS(leftBound - aTime);
+        double distanceRight = NS_ABS(rightBound - aTime);
+        if (distanceLeft == distanceRight) {
+          distanceLeft = NS_ABS(leftBound - mCurrentTime);
+          distanceRight = NS_ABS(rightBound - mCurrentTime);
+        } 
+        aTime = (distanceLeft < distanceRight) ? leftBound : rightBound;
+      } else {
+        // Seek target is after the end last range in seekable data.
+        // Clamp the seek target to the end of the last seekable range.
+        res = seekable.End(length - 1, &aTime);
+        NS_ENSURE_SUCCESS(res, NS_OK);
       }
-
-      if (distanceLeft == distanceRight) {
-        distanceLeft = NS_ABS(leftBound - mCurrentTime);
-        distanceRight = NS_ABS(rightBound - mCurrentTime);
-      } 
-      aTime = (distanceLeft < distanceRight) ? leftBound : rightBound;
     } else {
       // aTime is before the first range in |seekable|, the closest point we can
       // seek to is the start of the first range.
       seekable.Start(0, &aTime);
     }
   }
 
   mRequestedSeekTime = aTime;
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -1119,16 +1119,43 @@ void nsBuiltinDecoderStateMachine::Reset
 {
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
   mVideoFrameEndTime = -1;
   mAudioStartTime = -1;
   mAudioEndTime = -1;
   mAudioCompleted = false;
 }
 
+void nsBuiltinDecoderStateMachine::NotifyDataArrived(const char* aBuffer,
+                                                     PRUint32 aLength,
+                                                     PRUint32 aOffset)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+  mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
+
+  // While playing an unseekable stream of unknown duration, mEndTime is
+  // updated (in AdvanceFrame()) as we play. But if data is being downloaded
+  // faster than played, mEndTime won't reflect the end of playable data
+  // since we haven't played the frame at the end of buffered data. So update
+  // mEndTime here as new data is downloaded to prevent such a lag.
+  nsTimeRanges buffered;
+  if (mDecoder->IsInfinite() &&
+      NS_SUCCEEDED(mDecoder->GetBuffered(&buffered)))
+  {
+    PRUint32 length = 0;
+    buffered.GetLength(&length);
+    if (length) {
+      double end = 0;
+      buffered.End(length - 1, &end);
+      ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+      mEndTime = NS_MAX<PRInt64>(mEndTime, end * USECS_PER_S);
+    }
+  }
+}
+
 void nsBuiltinDecoderStateMachine::Seek(double aTime)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   // nsBuiltinDecoder::mPlayState should be SEEKING while we seek, and
   // in that case nsBuiltinDecoder shouldn't be calling us.
   NS_ASSERTION(mState != DECODER_STATE_SEEKING,
                "We shouldn't already be seeking");
@@ -1500,17 +1527,21 @@ void nsBuiltinDecoderStateMachine::Decod
   LOG(PR_LOG_DEBUG, ("%p Seek completed, mCurrentFrameTime=%lld\n",
       mDecoder.get(), mCurrentFrameTime));
 
   // Change state to DECODING or COMPLETED now. SeekingStopped will
   // call nsBuiltinDecoderStateMachine::Seek to reset our state to SEEKING
   // if we need to seek again.
 
   nsCOMPtr<nsIRunnable> stopEvent;
-  if (GetMediaTime() == mEndTime) {
+  bool isLiveStream = mDecoder->GetStream()->GetLength() == -1;
+  if (GetMediaTime() == mEndTime && !isLiveStream) {
+    // Seeked to end of media, move to COMPLETED state. Note we don't do
+    // this if we're playing a live stream, since the end of media will advance
+    // once we download more data!
     LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to COMPLETED",
                         mDecoder.get(), seekTime));
     stopEvent = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::SeekingStoppedAtEnd);
     mState = DECODER_STATE_COMPLETED;
   } else {
     LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to DECODING",
                         mDecoder.get(), seekTime));
     stopEvent = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::SeekingStopped);
--- a/content/media/nsBuiltinDecoderStateMachine.h
+++ b/content/media/nsBuiltinDecoderStateMachine.h
@@ -218,20 +218,17 @@ public:
 
   PRInt64 AudioQueueMemoryInUse() {
     if (mReader) {
       return mReader->AudioQueueMemoryInUse();
     }
     return 0;
   }
 
-  void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset) {
-    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-    mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
-  }
+  void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset);
 
   PRInt64 GetEndMediaTime() const {
     mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
     return mEndTime;
   }
 
   bool IsSeekable() {
     mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -5737,21 +5737,22 @@ nsSVGFEImageElement::OnStopDecode(imgIRe
 {
   nsresult rv =
     nsImageLoadingContent::OnStopDecode(aRequest, status, statusArg);
   Invalidate();
   return rv;
 }
 
 NS_IMETHODIMP
-nsSVGFEImageElement::FrameChanged(imgIContainer *aContainer,
+nsSVGFEImageElement::FrameChanged(imgIRequest* aRequest,
+                                  imgIContainer *aContainer,
                                   const nsIntRect *aDirtyRect)
 {
   nsresult rv =
-    nsImageLoadingContent::FrameChanged(aContainer, aDirtyRect);
+    nsImageLoadingContent::FrameChanged(aRequest, aContainer, aDirtyRect);
   Invalidate();
   return rv;
 }
 
 NS_IMETHODIMP
 nsSVGFEImageElement::OnStartContainer(imgIRequest *aRequest,
                                       imgIContainer *aContainer)
 {
--- a/content/svg/content/src/nsSVGFilters.h
+++ b/content/svg/content/src/nsSVGFilters.h
@@ -293,17 +293,18 @@ public:
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers);
   virtual nsEventStates IntrinsicState() const;
 
   // imgIDecoderObserver
   NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult status,
                           const PRUnichar *statusArg);
   // imgIContainerObserver
-  NS_IMETHOD FrameChanged(imgIContainer *aContainer,
+  NS_IMETHOD FrameChanged(imgIRequest* aRequest,
+                          imgIContainer *aContainer,
                           const nsIntRect *aDirtyRect);
   // imgIContainerObserver
   NS_IMETHOD OnStartContainer(imgIRequest *aRequest,
                               imgIContainer *aContainer);
 
   void MaybeLoadSVGImage();
 
   virtual nsXPCClassInfo* GetClassInfo();
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1614,16 +1614,65 @@ nsDOMWindowUtils::GetLayerManagerType(ns
   if (!mgr)
     return NS_ERROR_FAILURE;
 
   mgr->GetBackendName(aType);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDOMWindowUtils::StartFrameTimeRecording()
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget)
+    return NS_ERROR_FAILURE;
+
+  LayerManager *mgr = widget->GetLayerManager();
+  if (!mgr)
+    return NS_ERROR_FAILURE;
+
+  mgr->StartFrameTimeRecording();
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMWindowUtils::StopFrameTimeRecording(PRUint32 *frameCount NS_OUTPARAM, float **frames NS_OUTPARAM)
+{
+  NS_ENSURE_ARG_POINTER(frameCount);
+  NS_ENSURE_ARG_POINTER(frames);
+
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget)
+    return NS_ERROR_FAILURE;
+
+  LayerManager *mgr = widget->GetLayerManager();
+  if (!mgr)
+    return NS_ERROR_FAILURE;
+
+  nsTArray<float> frameTimes = mgr->StopFrameTimeRecording();
+
+  *frames = nsnull;
+  *frameCount = frameTimes.Length();
+
+  if (*frameCount != 0) {
+    *frames = (float*)nsMemory::Alloc(*frameCount * sizeof(float*));
+    if (!*frames)
+      return NS_ERROR_OUT_OF_MEMORY;
+
+    /* copy over the frame times into the array we just allocated */
+    for (PRUint32 i = 0; i < *frameCount; i++) {
+      (*frames)[i] = frameTimes[i];
+    }
+  }
+
+  return NS_OK;
+}
+
 static bool
 ComputeAnimationValue(nsCSSProperty aProperty,
                       Element* aElement,
                       const nsAString& aInput,
                       nsStyleAnimation::Value& aOutput)
 {
 
   if (!nsStyleAnimation::ComputeValue(aProperty, aElement, aInput,
--- a/dom/base/nsGlobalWindowCommands.cpp
+++ b/dom/base/nsGlobalWindowCommands.cpp
@@ -40,16 +40,17 @@
 #include "nsGlobalWindowCommands.h"
 
 #include "nsIComponentManager.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsCRT.h"
 #include "nsString.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/Util.h"
 
 #include "nsIControllerCommandTable.h"
 #include "nsICommandParams.h"
 
 #include "nsPIDOMWindow.h"
 #include "nsIPresShell.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
@@ -71,16 +72,20 @@ const char * const sSelectNoneString = "
 const char * const sCopyImageLocationString = "cmd_copyImageLocation";
 const char * const sCopyImageContentsString = "cmd_copyImageContents";
 const char * const sCopyImageString = "cmd_copyImage";
 
 const char * const sScrollTopString = "cmd_scrollTop";
 const char * const sScrollBottomString = "cmd_scrollBottom";
 const char * const sScrollPageUpString = "cmd_scrollPageUp";
 const char * const sScrollPageDownString = "cmd_scrollPageDown";
+const char * const sScrollLineUpString = "cmd_scrollLineUp";
+const char * const sScrollLineDownString = "cmd_scrollLineDown";
+const char * const sScrollLeftString = "cmd_scrollLeft";
+const char * const sScrollRightString = "cmd_scrollRight";
 const char * const sMoveTopString = "cmd_moveTop";
 const char * const sMoveBottomString = "cmd_moveBottom";
 const char * const sMovePageUpString = "cmd_movePageUp";
 const char * const sMovePageDownString = "cmd_movePageDown";
 const char * const sLinePreviousString = "cmd_linePrevious";
 const char * const sLineNextString = "cmd_lineNext";
 const char * const sCharPreviousString = "cmd_charPrevious";
 const char * const sCharNextString = "cmd_charNext";
@@ -116,50 +121,44 @@ const char * const sSelectBottomString =
 #endif
 
 // a base class for selection-related commands, for code sharing
 class nsSelectionCommandsBase : public nsIControllerCommand
 {
 public:
 
   NS_DECL_ISUPPORTS
-  NS_DECL_NSICONTROLLERCOMMAND
+  NS_IMETHOD IsCommandEnabled(const char * aCommandName, nsISupports *aCommandContext, bool *_retval NS_OUTPARAM);
+  NS_IMETHOD GetCommandStateParams(const char * aCommandName, nsICommandParams *aParams, nsISupports *aCommandContext);
+  NS_IMETHOD DoCommandParams(const char * aCommandName, nsICommandParams *aParams, nsISupports *aCommandContext);
 
 protected:
 
-  // subclasses override DoSelectCommand
-  virtual nsresult DoSelectCommand(const char *aCommandName, nsIDOMWindow *aWindow) = 0;
-
-  static nsresult  GetPresShellFromWindow(nsIDOMWindow *aWindow, nsIPresShell **aPresShell);
-  static nsresult  GetSelectionControllerFromWindow(nsIDOMWindow *aWindow, nsISelectionController **aSelCon);
+  static nsresult  GetPresShellFromWindow(nsPIDOMWindow *aWindow, nsIPresShell **aPresShell);
+  static nsresult  GetSelectionControllerFromWindow(nsPIDOMWindow *aWindow, nsISelectionController **aSelCon);
 
   // no member variables, please, we're stateless!
 };
 
 // this class implements commands whose behavior depends on the 'browse with caret' setting
 class nsSelectMoveScrollCommand : public nsSelectionCommandsBase
 {
-protected:
+public:
 
-  virtual nsresult DoSelectCommand(const char *aCommandName, nsIDOMWindow *aWindow);
-  
-  nsresult    DoCommandBrowseWithCaretOn(const char *aCommandName,
-                                         nsIDOMWindow *aWindow,
-                                         nsISelectionController *aSelectionController);
-  nsresult    DoCommandBrowseWithCaretOff(const char *aCommandName, nsISelectionController *aSelectionController);
+  NS_IMETHOD DoCommand(const char * aCommandName, nsISupports *aCommandContext);
 
   // no member variables, please, we're stateless!
 };
 
 // this class implements other selection commands
 class nsSelectCommand : public nsSelectionCommandsBase
 {
-protected:
+public:
 
-  virtual nsresult DoSelectCommand(const char *aCommandName, nsIDOMWindow *aWindow);
+  NS_IMETHOD DoCommand(const char * aCommandName, nsISupports *aCommandContext);
 
   // no member variables, please, we're stateless!
 };
 
 #if 0
 #pragma mark -
 #endif
 
@@ -182,223 +181,147 @@ nsSelectionCommandsBase::IsCommandEnable
 NS_IMETHODIMP
 nsSelectionCommandsBase::GetCommandStateParams(const char *aCommandName,
                                             nsICommandParams *aParams, nsISupports *aCommandContext)
 {
   // XXX we should probably return the enabled state
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-NS_IMETHODIMP
-nsSelectionCommandsBase::DoCommand(const char *aCommandName, nsISupports *aCommandContext)
-{
-  nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(aCommandContext);
-  NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);       
-
-  return DoSelectCommand(aCommandName, window);
-}
-
 /* void doCommandParams (in string aCommandName, in nsICommandParams aParams, in nsISupports aCommandContext); */
 NS_IMETHODIMP
 nsSelectionCommandsBase::DoCommandParams(const char *aCommandName,
                                        nsICommandParams *aParams, nsISupports *aCommandContext)
 {
   return DoCommand(aCommandName, aCommandContext);
 }
 
 // protected methods
 
 nsresult
-nsSelectionCommandsBase::GetPresShellFromWindow(nsIDOMWindow *aWindow, nsIPresShell **aPresShell)
+nsSelectionCommandsBase::GetPresShellFromWindow(nsPIDOMWindow *aWindow, nsIPresShell **aPresShell)
 {
   *aPresShell = nsnull;
+  NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE);
 
-  nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aWindow));
-  NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
-
-  nsIDocShell *docShell = win->GetDocShell();
+  nsIDocShell *docShell = aWindow->GetDocShell();
   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
 
   return docShell->GetPresShell(aPresShell);
 }
 
 nsresult
-nsSelectionCommandsBase::GetSelectionControllerFromWindow(nsIDOMWindow *aWindow, nsISelectionController **aSelCon)
+nsSelectionCommandsBase::GetSelectionControllerFromWindow(nsPIDOMWindow *aWindow, nsISelectionController **aSelCon)
 {
   *aSelCon = nsnull;
 
   nsCOMPtr<nsIPresShell> presShell;
   GetPresShellFromWindow(aWindow, getter_AddRefs(presShell));
   if (presShell)
     return CallQueryInterface(presShell, aSelCon);
 
   return NS_ERROR_FAILURE;
 }
 
 #if 0
 #pragma mark -
 #endif
 
+static const struct BrowseCommand {
+  const char *reverse, *forward;
+  nsresult (NS_STDCALL nsISelectionController::*scroll)(bool);
+  nsresult (NS_STDCALL nsISelectionController::*move)(bool, bool);
+} browseCommands[] = {
+ { sScrollTopString, sScrollBottomString,
+   &nsISelectionController::CompleteScroll },
+ { sScrollPageUpString, sScrollPageDownString,
+   &nsISelectionController::ScrollPage },
+ { sScrollLineUpString, sScrollLineDownString,
+   &nsISelectionController::ScrollLine },
+ { sScrollLeftString, sScrollRightString,
+   &nsISelectionController::ScrollCharacter },
+ { sMoveTopString, sMoveBottomString,
+   &nsISelectionController::CompleteScroll,
+   &nsISelectionController::CompleteMove },
+ { sMovePageUpString, sMovePageDownString,
+   &nsISelectionController::ScrollPage,
+   &nsISelectionController::PageMove },
+ { sLinePreviousString, sLineNextString,
+   &nsISelectionController::ScrollLine,
+   &nsISelectionController::LineMove },
+ { sWordPreviousString, sWordNextString,
+   &nsISelectionController::ScrollCharacter,
+   &nsISelectionController::WordMove },
+ { sCharPreviousString, sCharNextString,
+   &nsISelectionController::ScrollCharacter,
+   &nsISelectionController::CharacterMove },
+ { sBeginLineString, sEndLineString,
+   &nsISelectionController::CompleteScroll,
+   &nsISelectionController::IntraLineMove }
+};
+
 nsresult
-nsSelectMoveScrollCommand::DoSelectCommand(const char *aCommandName, nsIDOMWindow *aWindow)
+nsSelectMoveScrollCommand::DoCommand(const char *aCommandName, nsISupports *aCommandContext)
 {
+  nsCOMPtr<nsPIDOMWindow> piWindow(do_QueryInterface(aCommandContext));
   nsCOMPtr<nsISelectionController> selCont;
-  GetSelectionControllerFromWindow(aWindow, getter_AddRefs(selCont));
+  GetSelectionControllerFromWindow(piWindow, getter_AddRefs(selCont));
   NS_ENSURE_TRUE(selCont, NS_ERROR_NOT_INITIALIZED);       
 
   // We allow the caret to be moved with arrow keys on any window for which
   // the caret is enabled. In particular, this includes caret-browsing mode
   // in non-chrome documents.
   bool caretOn = false;
   selCont->GetCaretEnabled(&caretOn);
   if (!caretOn) {
     caretOn = Preferences::GetBool("accessibility.browsewithcaret");
     if (caretOn) {
-      nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aWindow);
-      if (piWindow) {
-        nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(piWindow->GetDocShell());
-        if (dsti) {
-          PRInt32 itemType;
-          dsti->GetItemType(&itemType);
-          if (itemType == nsIDocShellTreeItem::typeChrome) {
-            caretOn = false;
-          }
+      nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(piWindow->GetDocShell());
+      if (dsti) {
+        PRInt32 itemType;
+        dsti->GetItemType(&itemType);
+        if (itemType == nsIDocShellTreeItem::typeChrome) {
+          caretOn = false;
         }
       }
     }
   }
 
-  if (caretOn) {
-    return DoCommandBrowseWithCaretOn(aCommandName, aWindow, selCont);
-  }
-
-  return DoCommandBrowseWithCaretOff(aCommandName, selCont);
-}
-
-nsresult
-nsSelectMoveScrollCommand::DoCommandBrowseWithCaretOn(const char *aCommandName,
-                  nsIDOMWindow *aWindow, nsISelectionController *aSelectionController)
-{
-  nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
-
-  // cmd_MoveTop/Bottom are used on Window/Unix. They move the caret
-  // in caret browsing mode.
-  if (!nsCRT::strcmp(aCommandName, sMoveTopString))
-    rv = aSelectionController->CompleteMove(false, false);
-  else if (!nsCRT::strcmp(aCommandName,sMoveBottomString))
-    rv = aSelectionController->CompleteMove(true, false);
-  // cmd_ScrollTop/Bottom are used on Mac. They do not move the
-  // caret in caret browsing mode.
-  else if (!nsCRT::strcmp(aCommandName, sScrollTopString))
-    rv = aSelectionController->CompleteScroll(false);
-  else if (!nsCRT::strcmp(aCommandName,sScrollBottomString))
-    rv = aSelectionController->CompleteScroll(true);
-  // cmd_MovePageUp/Down are used on Window/Unix. They move the caret
-  // in caret browsing mode.
-  else if (!nsCRT::strcmp(aCommandName, sMovePageUpString))
-    rv = aSelectionController->PageMove(false, false);
-  else if (!nsCRT::strcmp(aCommandName, sMovePageDownString))
-    rv = aSelectionController->PageMove(true, false);
-  // cmd_ScrollPageUp/Down are used on Mac, and for the spacebar on all platforms.
-  // They do not move the caret in caret browsing mode.
-  else if (!nsCRT::strcmp(aCommandName, sScrollPageUpString))
-    rv = aSelectionController->ScrollPage(false);
-  else if (!nsCRT::strcmp(aCommandName, sScrollPageDownString))
-    rv = aSelectionController->ScrollPage(true);
-  else if (!nsCRT::strcmp(aCommandName, sLinePreviousString))
-    rv = aSelectionController->LineMove(false, false);
-  else if (!nsCRT::strcmp(aCommandName, sLineNextString))
-    rv = aSelectionController->LineMove(true, false);
-  else if (!nsCRT::strcmp(aCommandName, sWordPreviousString))
-    rv = aSelectionController->WordMove(false, false);
-  else if (!nsCRT::strcmp(aCommandName, sWordNextString))
-    rv = aSelectionController->WordMove(true, false);
-  else if (!nsCRT::strcmp(aCommandName, sCharPreviousString))
-    rv = aSelectionController->CharacterMove(false, false);
-  else if (!nsCRT::strcmp(aCommandName, sCharNextString))
-    rv = aSelectionController->CharacterMove(true, false);
-  else if (!nsCRT::strcmp(aCommandName, sBeginLineString))
-    rv = aSelectionController->IntraLineMove(false, false);
-  else if (!nsCRT::strcmp(aCommandName, sEndLineString))
-    rv = aSelectionController->IntraLineMove(true, false);
-
-  if (NS_SUCCEEDED(rv))
-  {
-    // adjust the focus to the new caret position
-    nsIFocusManager* fm = nsFocusManager::GetFocusManager();
-    if (fm) {
-      nsCOMPtr<nsIDOMElement> result;
-      fm->MoveFocus(aWindow, nsnull, nsIFocusManager::MOVEFOCUS_CARET,
-                    nsIFocusManager::FLAG_NOSCROLL,
-                    getter_AddRefs(result));
+  for (int i = 0; i < mozilla::ArrayLength(browseCommands); i++) {
+    bool forward = !strcmp(aCommandName, browseCommands[i].forward);
+    if (forward || !strcmp(aCommandName, browseCommands[i].reverse)) {
+      if (caretOn && browseCommands[i].move &&
+          NS_SUCCEEDED((selCont->*(browseCommands[i].move))(forward, false))) {
+        // adjust the focus to the new caret position
+        nsIFocusManager* fm = nsFocusManager::GetFocusManager();
+        if (fm) {
+          nsCOMPtr<nsIDOMElement> result;
+          fm->MoveFocus(piWindow, nsnull, nsIFocusManager::MOVEFOCUS_CARET,
+                        nsIFocusManager::FLAG_NOSCROLL,
+                        getter_AddRefs(result));
+        }
+        return NS_OK;
+      }
+      return (selCont->*(browseCommands[i].scroll))(forward);
     }
   }
-
-  return rv;
-}
-
-nsresult
-nsSelectMoveScrollCommand::DoCommandBrowseWithCaretOff(const char *aCommandName, nsISelectionController *aSelectionController)
-{
-  nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
-
-  // cmd_MoveTop/Bottom are used on Window/Unix. They move the caret
-  // in caret browsing mode.
-  if (!nsCRT::strcmp(aCommandName, sMoveTopString))
-    rv = aSelectionController->CompleteScroll(false);
-  else if (!nsCRT::strcmp(aCommandName,sMoveBottomString))
-    rv = aSelectionController->CompleteScroll(true);
-  // cmd_ScrollTop/Bottom are used on Mac. They do not move the
-  // caret in caret browsing mode.
-  else if (!nsCRT::strcmp(aCommandName, sScrollTopString))
-    rv = aSelectionController->CompleteScroll(false);
-  else if (!nsCRT::strcmp(aCommandName,sScrollBottomString))
-    rv = aSelectionController->CompleteScroll(true);
-
-  // cmd_MovePageUp/Down are used on Window/Unix. They move the caret
-  // in caret browsing mode.
-  else if (!nsCRT::strcmp(aCommandName, sMovePageUpString))
-    rv = aSelectionController->ScrollPage(false);
-  else if (!nsCRT::strcmp(aCommandName, sMovePageDownString))
-    rv = aSelectionController->ScrollPage(true);
-  // cmd_ScrollPageUp/Down are used on Mac. They do not move the
-  // caret in caret browsing mode.
-  else if (!nsCRT::strcmp(aCommandName, sScrollPageUpString))
-    rv = aSelectionController->ScrollPage(false);
-  else if (!nsCRT::strcmp(aCommandName, sScrollPageDownString))
-    rv = aSelectionController->ScrollPage(true);
-
-  else if (!nsCRT::strcmp(aCommandName, sLinePreviousString))
-    rv = aSelectionController->ScrollLine(false);
-  else if (!nsCRT::strcmp(aCommandName, sLineNextString))
-    rv = aSelectionController->ScrollLine(true);
-  else if (!nsCRT::strcmp(aCommandName, sCharPreviousString))
-    rv = aSelectionController->ScrollHorizontal(true);
-  else if (!nsCRT::strcmp(aCommandName, sCharNextString))
-    rv = aSelectionController->ScrollHorizontal(false);
-  // cmd_beginLine/endLine with caret browsing off
-  // will act as cmd_moveTop/Bottom
-  else if (!nsCRT::strcmp(aCommandName, sBeginLineString))
-    rv = aSelectionController->CompleteScroll(false);
-  else if (!nsCRT::strcmp(aCommandName, sEndLineString))
-    rv = aSelectionController->CompleteScroll(true);
-
-  return rv;
+  return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 
 #if 0
 #pragma mark -
 #endif
 
 nsresult
-nsSelectCommand::DoSelectCommand(const char *aCommandName, nsIDOMWindow *aWindow)
+nsSelectCommand::DoCommand(const char *aCommandName, nsISupports *aCommandContext)
 {
+  nsCOMPtr<nsPIDOMWindow> piWindow(do_QueryInterface(aCommandContext));
   nsCOMPtr<nsISelectionController> selCont;
-  GetSelectionControllerFromWindow(aWindow, getter_AddRefs(selCont));
+  GetSelectionControllerFromWindow(piWindow, getter_AddRefs(selCont));
   NS_ENSURE_TRUE(selCont, NS_ERROR_NOT_INITIALIZED);       
 
   nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
 
   // These commands are so the browser can use caret navigation key bindings -
   // Helps with accessibility - aaronl@netscape.com
   if (!nsCRT::strcmp(aCommandName, sSelectCharPreviousString))
     rv = selCont->CharacterMove(false, true);
@@ -963,26 +886,30 @@ nsWindowCommandRegistration::RegisterWin
 {
   nsresult rv;
 
   // XXX rework the macros to use a loop is possible, reducing code size
   
   // this set of commands is affected by the 'browse with caret' setting
   NS_REGISTER_FIRST_COMMAND(nsSelectMoveScrollCommand, sScrollTopString);
   NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sScrollBottomString);
+  NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sScrollPageUpString);
+  NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sScrollPageDownString);
+  NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sScrollLineUpString);
+  NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sScrollLineDownString);
+  NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sScrollLeftString);
+  NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sScrollRightString);
   NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sMoveTopString);
   NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sMoveBottomString);
   NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sWordPreviousString);
   NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sWordNextString);
   NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sBeginLineString);
   NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sEndLineString);
   NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sMovePageUpString);
   NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sMovePageDownString);
-  NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sScrollPageUpString);
-  NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sScrollPageDownString);
   NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sLinePreviousString);
   NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sLineNextString);
   NS_REGISTER_NEXT_COMMAND(nsSelectMoveScrollCommand, sCharPreviousString);
   NS_REGISTER_LAST_COMMAND(nsSelectMoveScrollCommand, sCharNextString);
 
   NS_REGISTER_FIRST_COMMAND(nsSelectCommand, sSelectCharPreviousString);
   NS_REGISTER_NEXT_COMMAND(nsSelectCommand, sSelectCharNextString);
   NS_REGISTER_NEXT_COMMAND(nsSelectCommand, sSelectWordPreviousString);
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -247,23 +247,16 @@ IndexedDatabaseManager::GetOrCreate()
     nsCOMPtr<nsIObserverService> obs = GetObserverService();
     NS_ENSURE_TRUE(obs, nsnull);
 
     // We need this callback to know when to shut down all our threads.
     nsresult rv = obs->AddObserver(instance, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
                                    false);
     NS_ENSURE_SUCCESS(rv, nsnull);
 
-    // We don't really need this callback but we want the observer service to
-    // hold us alive until XPCOM shutdown. That way other consumers can continue
-    // to use this service until shutdown.
-    rv = obs->AddObserver(instance, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
-                          false);
-    NS_ENSURE_SUCCESS(rv, nsnull);
-
     // Make a lazy thread for any IO we need (like clearing or enumerating the
     // contents of indexedDB database directories).
     instance->mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
                                              LazyIdleThread::ManualShutdown);
 
     // We need one quota callback object to hand to SQLite.
     instance->mQuotaCallbackSingleton = new QuotaCallback();
 
@@ -1219,17 +1212,17 @@ IndexedDatabaseManager::ClearDatabasesFo
 
 NS_IMETHODIMP
 IndexedDatabaseManager::Observe(nsISupports* aSubject,
                                 const char* aTopic,
                                 const PRUnichar* aData)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) {
+  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     // Setting this flag prevents the service from being recreated and prevents
     // further databases from being created.
     if (PR_ATOMIC_SET(&gShutdown, 1)) {
       NS_ERROR("Shutdown more than once?!");
     }
 
     // Make sure to join with our IO thread.
     if (NS_FAILED(mIOThread->Shutdown())) {
@@ -1274,21 +1267,16 @@ IndexedDatabaseManager::Observe(nsISuppo
       for (PRUint32 index = 0; index < count; index++) {
         liveDatabases[index]->Invalidate();
       }
     }
 
     return NS_OK;
   }
 
-  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
-    // We're dying now.
-    return NS_OK;
-  }
-
   NS_NOTREACHED("Unknown topic!");
   return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::OriginClearRunnable,
                               nsIRunnable)
 
 NS_IMETHODIMP
--- a/dom/indexedDB/Key.cpp
+++ b/dom/indexedDB/Key.cpp
@@ -35,16 +35,17 @@
  * 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 "Key.h"
 #include "nsIStreamBufferAccess.h"
 #include "jsdate.h"
+#include "nsAlgorithm.h"
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
 #include "xpcpublic.h"
 
 USING_INDEXEDDB_NAMESPACE
 
 /*
  Here's how we encode keys:
@@ -406,38 +407,34 @@ Key::EncodeNumber(double aFloat, PRUint8
   *(buffer++) = aType;
 
   Float64Union pun;
   pun.d = aFloat;
   PRUint64 number = pun.u & PR_UINT64(0x8000000000000000) ?
                     -pun.u :
                     (pun.u | PR_UINT64(0x8000000000000000));
 
-  *reinterpret_cast<PRUint64*>(buffer) = NS_SWAP64(number);
+  number = NS_SWAP64(number);
+  memcpy(buffer, &number, sizeof(number));
 }
 
 // static
 double
 Key::DecodeNumber(const unsigned char*& aPos, const unsigned char* aEnd)
 {
   NS_ASSERTION(*aPos % eMaxType == eFloat ||
                *aPos % eMaxType == eDate, "Don't call me!");
 
   ++aPos;
+
   PRUint64 number = 0;
-  for (PRInt32 n = 7; n >= 0; --n) {
-    number <<= 8;
-    if (aPos < aEnd) {
-      number |= *(aPos++);
-    }
-    else {
-      number <<= 8 * n;
-      break;
-    }
-  }
+  memcpy(&number, aPos, NS_MIN<size_t>(sizeof(number), aEnd - aPos));
+  number = NS_SWAP64(number);
+
+  aPos += sizeof(number);
 
   Float64Union pun;
   pun.u = number & PR_UINT64(0x8000000000000000) ?
           (number & ~PR_UINT64(0x8000000000000000)) :
           -number;
 
   return pun.d;
 }
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -64,17 +64,17 @@ interface nsIDOMHTMLCanvasElement;
 interface nsIDOMEvent;
 interface nsITransferable;
 interface nsIQueryContentEventResult;
 interface nsIDOMWindow;
 interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
 
-[scriptable, uuid(c1fa9c82-acf2-4b27-8ca7-7d1864e606af)]
+[scriptable, uuid(15fcceb0-37ea-11e1-b86c-0800200c9a66)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -833,16 +833,19 @@ interface nsIDOMWindowUtils : nsISupport
 
   /**
    * What type of layer manager the widget associated with this window is
    * using. "Basic" is unaccelerated; other types are accelerated. Throws an
    * error if there is no widget associated with this window.
    */
   readonly attribute AString layerManagerType;
 
+  void startFrameTimeRecording();
+  void stopFrameTimeRecording([optional] out unsigned long frameCount,
+                              [retval, array, size_is(frameCount)] out float frameTime);
   /**
    * The DPI of the display
    */
   readonly attribute float displayDPI;
 
   /**
    * Return the outer window with the given ID, if any.  Can return null.
    */
--- a/dom/src/storage/nsDOMStoragePersistentDB.cpp
+++ b/dom/src/storage/nsDOMStoragePersistentDB.cpp
@@ -203,17 +203,17 @@ nsDOMStoragePersistentDB::Init(const nsS
     // delete the db and try opening again
     rv = storageFile->Remove(false);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = service->OpenDatabase(storageFile, getter_AddRefs(mConnection));
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
-        "PRAGMA temp_store = MEMORY"));
+        MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA temp_store = MEMORY"));
   NS_ENSURE_SUCCESS(rv, rv);
 
   mozStorageTransaction transaction(mConnection, false);
 
   // Ensure Gecko 1.9.1 storage table
   rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
          "CREATE TABLE IF NOT EXISTS webappsstore2 ("
          "scope TEXT, "
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -147,16 +147,18 @@ const char* gStringChars[] = {
 PR_STATIC_ASSERT(NS_ARRAY_LENGTH(gStringChars) == ID_COUNT);
 
 enum {
   PREF_strict = 0,
   PREF_werror,
   PREF_relimit,
   PREF_methodjit,
   PREF_methodjit_always,
+  PREF_typeinference,
+  PREF_jit_hardening,
   PREF_mem_max,
 
 #ifdef JS_GC_ZEAL
   PREF_gczeal,
 #endif
 
   PREF_COUNT
 };
@@ -164,16 +166,18 @@ enum {
 #define JS_OPTIONS_DOT_STR "javascript.options."
 
 const char* gPrefsToWatch[] = {
   JS_OPTIONS_DOT_STR "strict",
   JS_OPTIONS_DOT_STR "werror",
   JS_OPTIONS_DOT_STR "relimit",
   JS_OPTIONS_DOT_STR "methodjit.content",
   JS_OPTIONS_DOT_STR "methodjit_always",
+  JS_OPTIONS_DOT_STR "typeinference",
+  JS_OPTIONS_DOT_STR "jit_hardening",
   JS_OPTIONS_DOT_STR "mem.max"
 
 #ifdef JS_GC_ZEAL
   , PREF_WORKERS_GCZEAL
 #endif
 };
 
 PR_STATIC_ASSERT(NS_ARRAY_LENGTH(gPrefsToWatch) == PREF_COUNT);
@@ -208,16 +212,25 @@ PrefCallback(const char* aPrefName, void
       newOptions |= JSOPTION_RELIMIT;
     }
     if (Preferences::GetBool(gPrefsToWatch[PREF_methodjit])) {
       newOptions |= JSOPTION_METHODJIT;
     }
     if (Preferences::GetBool(gPrefsToWatch[PREF_methodjit_always])) {
       newOptions |= JSOPTION_METHODJIT_ALWAYS;
     }
+    if (Preferences::GetBool(gPrefsToWatch[PREF_typeinference])) {
+      newOptions |= JSOPTION_TYPE_INFERENCE;
+    }
+
+    // This one is special, it's enabled by default and only needs to be unset.
+    if (!Preferences::GetBool(gPrefsToWatch[PREF_jit_hardening])) {
+      newOptions |= JSOPTION_SOFTEN;
+    }
+
     RuntimeService::SetDefaultJSContextOptions(newOptions);
     rts->UpdateAllWorkerJSContextOptions();
   }
 #ifdef JS_GC_ZEAL
   else if (!strcmp(aPrefName, gPrefsToWatch[PREF_gczeal])) {
     PRInt32 gczeal = Preferences::GetInt(gPrefsToWatch[PREF_gczeal]);
     RuntimeService::SetDefaultGCZeal(PRUint8(clamped(gczeal, 0, 3)));
     rts->UpdateAllWorkerGCZeal();
--- a/editor/libeditor/base/tests/test_selection_move_commands.xul
+++ b/editor/libeditor/base/tests/test_selection_move_commands.xul
@@ -118,16 +118,26 @@ function execTests() {
   var pageHeight = -root.getBoundingClientRect().top;
   ok(pageHeight > 0, "cmd_scrollPageDown works");
   ok(pageHeight <= 100, "cmd_scrollPageDown doesn't scroll too much");
   doCommand("cmd_scrollBottom");
   doCommand("cmd_scrollPageUp");
   yield;
   testScrollCommand("cmd_scrollPageUp", root.scrollHeight - 100 - pageHeight);
 
+  doCommand("cmd_scrollTop");
+  doCommand("cmd_scrollLineDown");
+  yield;
+  var lineHeight = -root.getBoundingClientRect().top;
+  ok(lineHeight > 0, "Can scroll by lines");
+  doCommand("cmd_scrollBottom");
+  doCommand("cmd_scrollLineUp");
+  yield;
+  testScrollCommand("cmd_scrollLineUp", root.scrollHeight - 100 - lineHeight);
+
   var runSelectionTests = function(selectWordNextNode, selectWordNextOffset) {
     testMoveCommand("cmd_moveBottom", body, 23);
     testMoveCommand("cmd_moveTop", node(0), 0);
     testSelectCommand("cmd_selectBottom", body, 23);
     doCommand("cmd_moveBottom");
     testSelectCommand("cmd_selectTop", node(0), 0);
 
     doCommand("cmd_moveTop");
--- a/editor/libeditor/text/nsTextEditRules.cpp
+++ b/editor/libeditor/text/nsTextEditRules.cpp
@@ -939,27 +939,23 @@ nsTextEditRules::WillUndo(nsISelection *
  */
 nsresult
 nsTextEditRules::DidUndo(nsISelection *aSelection, nsresult aResult)
 {
   nsresult res = aResult;  // if aResult is an error, we return it.
   if (!aSelection) { return NS_ERROR_NULL_POINTER; }
   if (NS_SUCCEEDED(res)) 
   {
-    if (mBogusNode) {
-      mBogusNode = nsnull;
-    }
+    nsCOMPtr<nsIDOMElement> theRoot = do_QueryInterface(mEditor->GetRoot());
+    NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
+    nsCOMPtr<nsIDOMNode> node = mEditor->GetLeftmostChild(theRoot);
+    if (node && mEditor->IsMozEditorBogusNode(node))
+      mBogusNode = node;
     else
-    {
-      nsCOMPtr<nsIDOMElement> theRoot = do_QueryInterface(mEditor->GetRoot());
-      NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
-      nsCOMPtr<nsIDOMNode> node = mEditor->GetLeftmostChild(theRoot);
-      if (node && mEditor->IsMozEditorBogusNode(node))
-        mBogusNode = node;
-    }
+      mBogusNode = nsnull;
   }
   return res;
 }
 
 nsresult
 nsTextEditRules::WillRedo(nsISelection *aSelection, bool *aCancel, bool *aHandled)
 {
   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
@@ -972,40 +968,41 @@ nsTextEditRules::WillRedo(nsISelection *
 
 nsresult
 nsTextEditRules::DidRedo(nsISelection *aSelection, nsresult aResult)
 {
   nsresult res = aResult;  // if aResult is an error, we return it.
   if (!aSelection) { return NS_ERROR_NULL_POINTER; }
   if (NS_SUCCEEDED(res)) 
   {
-    if (mBogusNode) {
-      mBogusNode = nsnull;
-    }
-    else
+    nsCOMPtr<nsIDOMElement> theRoot = do_QueryInterface(mEditor->GetRoot());
+    NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
+    
+    nsCOMPtr<nsIDOMNodeList> nodeList;
+    res = theRoot->GetElementsByTagName(NS_LITERAL_STRING("br"),
+                                        getter_AddRefs(nodeList));
+    NS_ENSURE_SUCCESS(res, res);
+    if (nodeList)
     {
-      nsCOMPtr<nsIDOMElement> theRoot = do_QueryInterface(mEditor->GetRoot());
-      NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
+      PRUint32 len;
+      nodeList->GetLength(&len);
       
-      nsCOMPtr<nsIDOMNodeList> nodeList;
-      res = theRoot->GetElementsByTagName(NS_LITERAL_STRING("br"),
-                                          getter_AddRefs(nodeList));
-      NS_ENSURE_SUCCESS(res, res);
-      if (nodeList)
-      {
-        PRUint32 len;
-        nodeList->GetLength(&len);
-        
-        if (len != 1) return NS_OK;  // only in the case of one br could there be the bogus node
-        nsCOMPtr<nsIDOMNode> node;
-        nodeList->Item(0, getter_AddRefs(node));
-        NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
-        if (mEditor->IsMozEditorBogusNode(node))
-          mBogusNode = node;
+      if (len != 1) {
+        // only in the case of one br could there be the bogus node
+        mBogusNode = nsnull;
+        return NS_OK;  
       }
+
+      nsCOMPtr<nsIDOMNode> node;
+      nodeList->Item(0, getter_AddRefs(node));
+      NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
+      if (mEditor->IsMozEditorBogusNode(node))
+        mBogusNode = node;
+      else
+        mBogusNode = nsnull;
     }
   }
   return res;
 }
 
 nsresult
 nsTextEditRules::WillOutputText(nsISelection *aSelection, 
                                 const nsAString  *aOutputFormat,
--- a/editor/libeditor/text/tests/Makefile.in
+++ b/editor/libeditor/text/tests/Makefile.in
@@ -71,16 +71,17 @@ include $(topsrcdir)/config/rules.mk
 # on our editor, and the combinations depend on the system.
 ifneq ($(MOZ_WIDGET_TOOLKIT),gtk2)
 _TEST_FILES += \
 		test_texteditor_keyevent_handling.html \
 		$(NULL)
 endif
 
 _CHROME_TEST_FILES = \
+		test_bug471319.html \
 		test_bug483651.html \
 		test_bug636465.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_TEST_FILES)
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/text/tests/test_bug471319.html
@@ -0,0 +1,114 @@
+<!DOCTYPE HTML>
+<!-- ***** 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 Plaintext Editor Test code
+   -
+   - The Initial Developer of the Original Code is
+   - Graeme McCutcheon <graememcc_firefox@graeme-online.co.uk>.
+   - Portions created by the Initial Developer are Copyright (C) 2011
+   - the Initial Developer. All Rights Reserved.
+   -
+   - Contributor(s):
+   -
+   -
+   - 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 ***** -->
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=471319
+-->
+
+<head>
+  <title>Test for Bug 471319</title>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body onload="doTest();">
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=471319">Mozilla Bug 471319</a>
+  <p id="display"></p>
+  <div id="content" style="display: none">
+  </div>
+
+  <pre id="test">
+    <script type="application/javascript;version=1.7">
+
+      /** Test for Bug 471319 **/
+    
+      SimpleTest.waitForExplicitFinish();
+
+      function doTest() {
+        let t1 = $("t1");
+        let editor = null;
+
+        // Test 1: Undo on an empty editor - the editor should not forget about
+        // the bogus node
+        t1.QueryInterface(Components.interfaces.nsIDOMNSEditableElement);
+        t1Editor = t1.editor;
+
+        // Did the editor recognize the new bogus node?
+        t1Editor.undo(1);
+        ok(!t1.value, "<br> still recognized as bogus node on undo");
+
+
+        // Test 2: Redo on an empty editor - the editor should not forget about
+        // the bogus node
+        let t2 = $("t2");
+        t2.QueryInterface(Components.interfaces.nsIDOMNSEditableElement);
+        t2Editor = t2.editor;
+
+        // Did the editor recognize the new bogus node?
+        t2Editor.redo(1);
+        ok(!t2.value, "<br> still recognized as bogus node on redo");
+
+
+        // Test 3: Undoing a batched transaction where both end points of the
+        // transaction are the bogus node - the bogus node should still be
+        // recognized as bogus
+        t1Editor.transactionManager.beginBatch();
+        t1.value = "mozilla";
+        t1.value = "";
+        t1Editor.transactionManager.endBatch();
+        t1Editor.undo(1);
+        ok(!t1.value,
+           "recreated <br> from undo transaction recognized as bogus");
+
+
+        // Test 4: Redoing a batched transaction where both end points of the
+        // transaction are the bogus node - the bogus node should still be
+        // recognized as bogus
+        t1Editor.redo(1);
+        ok(!t1.value,
+           "recreated <br> from redo transaction recognized as bogus");
+        SimpleTest.finish();
+     }
+   </script>
+  </pre>
+
+  <input type="text" id="t1" />
+  <input type="text" id="t2" />
+</body>
+</html>
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -556,16 +556,41 @@ PlanarYCbCrImage::CopyData(Data& aDest, 
            aData.mCrChannel + i * aData.mCbCrStride,
            aDest.mCbCrStride);
   }
 
   aDestSize = aData.mPicSize;
   return buffer;
 }
                          
+void
+LayerManager::StartFrameTimeRecording()
+{
+  mLastFrameTime = TimeStamp::Now();
+}
+
+void
+LayerManager::PostPresent()
+{
+  if (!mLastFrameTime.IsNull()) {
+    TimeStamp now = TimeStamp::Now();
+    mFrameTimes.AppendElement((now - mLastFrameTime).ToMilliseconds());
+    mLastFrameTime = now;
+  }
+}
+
+nsTArray<float>
+LayerManager::StopFrameTimeRecording()
+{
+  mLastFrameTime = TimeStamp();
+  nsTArray<float> result = mFrameTimes;
+  mFrameTimes.Clear();
+  return result;
+}
+
 
 
 #ifdef MOZ_LAYERS_HAVE_LOG
 
 static nsACString& PrintInfo(nsACString& aTo, ShadowLayer* aShadowLayer);
 
 void
 Layer::Dump(FILE* aFile, const char* aPrefix)
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -46,16 +46,17 @@
 #include "nsISupportsImpl.h"
 #include "nsAutoPtr.h"
 #include "gfx3DMatrix.h"
 #include "gfxColor.h"
 #include "gfxPattern.h"
 #include "nsTArray.h"
 
 #include "mozilla/gfx/2D.h"
+#include "mozilla/TimeStamp.h"
 
 #if defined(DEBUG) || defined(PR_LOGGING)
 #  include <stdio.h>            // FILE
 #  include "prlog.h"
 #  define MOZ_LAYERS_HAVE_LOG
 #  define MOZ_LAYERS_LOG(_args)                             \
   PR_LOG(LayerManager::GetLog(), PR_LOG_DEBUG, _args)
 #else
@@ -507,16 +508,21 @@ public:
    */
   void Log(const char* aPrefix="");
   /**
    * Log information about just this layer manager itself to the NSPR
    * log (if enabled for "Layers").
    */
   void LogSelf(const char* aPrefix="");
 
+  void StartFrameTimeRecording();
+  nsTArray<float> StopFrameTimeRecording();
+
+  void PostPresent();
+
   static bool IsLogEnabled();
   static PRLogModuleInfo* GetLog() { return sLog; }
 
   bool IsCompositingCheap(LayerManager::LayersBackend aBackend)
   { return LAYERS_BASIC != aBackend; }
 
   virtual bool IsCompositingCheap() { return true; }
 
@@ -527,16 +533,19 @@ protected:
   bool mSnapEffectiveTransforms;
 
   // Print interesting information about this into aTo.  Internally
   // used to implement Dump*() and Log*().
   virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix);
 
   static void InitLog();
   static PRLogModuleInfo* sLog;
+private:
+  TimeStamp mLastFrameTime;
+  nsTArray<float> mFrameTimes;
 };
 
 class ThebesLayer;
 
 /**
  * A Layer represents anything that can be rendered onto a destination
  * surface.
  */
--- a/gfx/layers/d3d10/LayerManagerD3D10.cpp
+++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp
@@ -773,16 +773,17 @@ LayerManagerD3D10::Render()
     ShadowLayerForwarder::EndTransaction(&replies);
     // We expect only 1 reply, but might get none if the parent
     // process crashed
 
     swap(mBackBuffer, mRemoteFrontBuffer);
   } else {
     mSwapChain->Present(0, 0);
   }
+  LayerManager::PostPresent();
 }
 
 void
 LayerManagerD3D10::PaintToTarget()
 {
   nsRefPtr<ID3D10Texture2D> backBuf;
   
   mSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (void**)backBuf.StartAssignment());
--- a/gfx/layers/d3d9/LayerManagerD3D9.cpp
+++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp
@@ -349,16 +349,17 @@ LayerManagerD3D9::Render()
   device()->EndScene();
 
   if (!mTarget) {
     const nsIntRect *r;
     for (nsIntRegionRectIterator iter(mClippingRegion);
          (r = iter.Next()) != nsnull;) {
       mSwapChain->Present(*r);
     }
+    LayerManager::PostPresent();
   } else {
     PaintToTarget();
   }
 }
 
 void
 LayerManagerD3D9::SetupPipeline()
 {
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -814,16 +814,17 @@ LayerManagerOGL::Render()
   }
 
   if (sDrawFPS) {
     mFPS.DrawFPS(mGLContext, GetCopy2DProgram());
   }
 
   if (mGLContext->IsDoubleBuffered()) {
     mGLContext->SwapBuffers();
+    LayerManager::PostPresent();
     mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
     return;
   }
 
   mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
 
   mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
 
--- a/gfx/src/nsFontMetrics.cpp
+++ b/gfx/src/nsFontMetrics.cpp
@@ -36,48 +36,54 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsFontMetrics.h"
 #include "nsBoundingMetrics.h"
 #include "nsRenderingContext.h"
 #include "nsDeviceContext.h"
 #include "nsStyleConsts.h"
-#include "gfxTextRunCache.h"
 
 namespace {
 
-class AutoTextRun : public gfxTextRunCache::AutoTextRun {
+class AutoTextRun {
 public:
     AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC,
                 const char* aString, PRInt32 aLength)
-        : gfxTextRunCache::AutoTextRun(gfxTextRunCache::MakeTextRun(
+    {
+        mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
             reinterpret_cast<const PRUint8*>(aString), aLength,
-            aMetrics->GetThebesFontGroup(), aRC->ThebesContext(),
+            aRC->ThebesContext(),
             aMetrics->AppUnitsPerDevPixel(),
-            ComputeFlags(aMetrics)))
-    {}
+            ComputeFlags(aMetrics));
+    }
 
     AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC,
                 const PRUnichar* aString, PRInt32 aLength)
-        : gfxTextRunCache::AutoTextRun(gfxTextRunCache::MakeTextRun(
-            aString, aLength, aMetrics->GetThebesFontGroup(),
+    {
+        mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
+            aString, aLength,
             aRC->ThebesContext(),
             aMetrics->AppUnitsPerDevPixel(),
-            ComputeFlags(aMetrics)))
-    {}
+            ComputeFlags(aMetrics));
+    }
+
+    gfxTextRun *get() { return mTextRun; }
+    gfxTextRun *operator->() { return mTextRun; }
 
 private:
     static PRUint32 ComputeFlags(nsFontMetrics* aMetrics) {
         PRUint32 flags = 0;
         if (aMetrics->GetTextRunRTL()) {
             flags |= gfxTextRunFactory::TEXT_IS_RTL;
         }
         return flags;
     }
+
+    nsAutoPtr<gfxTextRun> mTextRun;
 };
 
 class StubPropertyProvider : public gfxTextRun::PropertyProvider {
 public:
     virtual void GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength,
                                       bool* aBreakBefore) {
         NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
     }
--- a/gfx/tests/gfxFontSelectionTest.cpp
+++ b/gfx/tests/gfxFontSelectionTest.cpp
@@ -40,17 +40,16 @@
 #include "nsString.h"
 #include "nsDependentString.h"
 
 #include "mozilla/Preferences.h"
 
 #include "gfxContext.h"
 #include "gfxFont.h"
 #include "gfxPlatform.h"
-#include "gfxTextRunWordCache.h"
 
 #include "gfxFontTest.h"
 
 #if defined(XP_MACOSX)
 #include "gfxTestCocoaHelper.h"
 #endif
 
 #ifdef MOZ_WIDGET_GTK2
@@ -61,18 +60,16 @@ using namespace mozilla;
 
 enum {
     S_UTF8 = 0,
     S_ASCII = 1
 };
 
 class FrameTextRunCache;
 
-static gfxTextRunWordCache *gTextRunCache;
-
 struct LiteralArray {
     LiteralArray (unsigned long l1) {
         data.AppendElement(l1);
     }
     LiteralArray (unsigned long l1, unsigned long l2) {
         data.AppendElement(l1);
         data.AppendElement(l2);
     }
@@ -299,21 +296,21 @@ RunTest (TestEntry *test, gfxContext *ct
     PRUint32 flags = gfxTextRunFactory::TEXT_IS_PERSISTENT;
     if (test->isRTL) {
         flags |= gfxTextRunFactory::TEXT_IS_RTL;
     }
     PRUint32 length;
     if (test->stringType == S_ASCII) {
         flags |= gfxTextRunFactory::TEXT_IS_ASCII | gfxTextRunFactory::TEXT_IS_8BIT;
         length = strlen(test->string);
-        textRun = gfxTextRunWordCache::MakeTextRun(reinterpret_cast<const PRUint8*>(test->string), length, fontGroup, &params, flags);
+        textRun = fontGroup->MakeTextRun(reinterpret_cast<const PRUint8*>(test->string), length, &params, flags);
     } else {
         NS_ConvertUTF8toUTF16 str(nsDependentCString(test->string));
         length = str.Length();
-        textRun = gfxTextRunWordCache::MakeTextRun(str.get(), length, fontGroup, &params, flags);
+        textRun = fontGroup->MakeTextRun(str.get(), length, &params, flags);
     }
 
     gfxFontTestStore::NewStore();
     textRun->Draw(ctx, gfxPoint(0,0), 0, length, nsnull, nsnull);
     gfxFontTestStore *s = gfxFontTestStore::CurrentStore();
 
     gTextRunCache->RemoveTextRun(textRun);
 
@@ -343,18 +340,16 @@ main (int argc, char **argv) {
     nsresult rv = NS_InitXPCOM2(nsnull, nsnull, nsnull);
     if (NS_FAILED(rv))
         return -1;
 
     rv = gfxPlatform::Init();
     if (NS_FAILED(rv))
         return -1;
 
-    gTextRunCache = new gfxTextRunWordCache();
-
     // let's get all the xpcom goop out of the system
     fflush (stderr);
     fflush (stdout);
 
     // don't need to query, we might need to set up some prefs later
     if (0) {
         nsresult rv;
 
--- a/gfx/tests/gfxWordCacheTest.cpp
+++ b/gfx/tests/gfxWordCacheTest.cpp
@@ -43,18 +43,16 @@
 #include "prinrval.h"
 
 #include "gfxContext.h"
 #include "gfxFont.h"
 #include "gfxPlatform.h"
 
 #include "gfxFontTest.h"
 
-#include "gfxTextRunWordCache.h"
-
 #if defined(XP_MACOSX)
 #include "gfxTestCocoaHelper.h"
 #endif
 
 #ifdef MOZ_WIDGET_GTK2
 #include "gtk/gtk.h"
 #endif
 
@@ -73,17 +71,16 @@ public:
  ~FrameTextRunCache() {
    AgeAllGenerations();
  }
 
  void RemoveFromCache(gfxTextRun* aTextRun) {
    if (aTextRun->GetExpirationState()->IsTracked()) {
      RemoveObject(aTextRun);
    }
-   gfxTextRunWordCache::RemoveTextRun(aTextRun);
  }
 
  // This gets called when the timeout has expired on a gfxTextRun
  virtual void NotifyExpired(gfxTextRun* aTextRun) {
    RemoveFromCache(aTextRun);
    delete aTextRun;
  }
 };
@@ -94,18 +91,17 @@ MakeTextRun(const PRUnichar *aText, PRUi
            PRUint32 aFlags)
 {
    nsAutoPtr<gfxTextRun> textRun;
    if (aLength == 0) {
        textRun = aFontGroup->MakeEmptyTextRun(aParams, aFlags);
    } else if (aLength == 1 && aText[0] == ' ') {
        textRun = aFontGroup->MakeSpaceTextRun(aParams, aFlags);
    } else {
-       textRun = gfxTextRunWordCache::MakeTextRun(aText, aLength, aFontGroup,
-           aParams, aFlags);
+       textRun = aFontGroup->MakeTextRun(aText, aLength, aParams, aFlags);
    }
    if (!textRun)
        return nsnull;
    nsresult rv = gTextRuns->AddObject(textRun);
    if (NS_FAILED(rv)) {
        gTextRuns->RemoveFromCache(textRun);
        return nsnull;
    }
--- a/gfx/thebes/Makefile.in
+++ b/gfx/thebes/Makefile.in
@@ -70,18 +70,16 @@ EXPORTS	= \
 	gfxPoint3D.h \
 	gfxPointH3D.h \
 	gfxQuad.h \
 	gfxQuaternion.h \
 	gfxRect.h \
 	gfxSkipChars.h \
 	gfxTeeSurface.h \
 	gfxTypes.h \
-	gfxTextRunCache.h \
-	gfxTextRunWordCache.h \
 	gfxUnicodeProperties.h \
 	gfxUtils.h \
 	gfxUserFontSet.h \
 	nsCoreAnimationSupport.h \
 	gfxSharedImageSurface.h \
 	$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),android)
@@ -197,18 +195,16 @@ CPPSRCS	= \
 	gfxMatrix.cpp \
 	gfxPath.cpp \
 	gfxPattern.cpp \
 	gfxPlatform.cpp \
 	gfxPlatformFontList.cpp \
 	gfxRect.cpp \
 	gfxSkipChars.cpp \
 	gfxTeeSurface.cpp \
-	gfxTextRunCache.cpp \
-	gfxTextRunWordCache.cpp \
 	gfxUserFontSet.cpp \
 	gfxUtils.cpp \
 	gfxUnicodeProperties.cpp \
 	gfxScriptItemizer.cpp \
 	gfxHarfBuzzShaper.cpp \
 	gfxSharedImageSurface.cpp \
 	$(NULL)
 
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -52,19 +52,34 @@ struct nsIntPoint;
 struct nsIntRect;
 
 /**
  * A surface is something you can draw on. Instantiate a subclass of this
  * abstract class, and use gfxContext to draw on this surface.
  */
 class THEBES_API gfxASurface {
 public:
+#ifdef MOZILLA_INTERNAL_API
     nsrefcnt AddRef(void);
     nsrefcnt Release(void);
 
+    // These functions exist so that browsercomps can refcount a gfxASurface
+    virtual nsresult AddRefExternal(void)
+    {
+      return AddRef();
+    }
+    virtual nsresult ReleaseExternal(void)
+    {
+      return Release();
+    }
+#else
+    virtual nsresult AddRef(void);
+    virtual nsresult Release(void);
+#endif
+
 public:
     /**
      * The format for an image surface. For all formats with alpha data, 0
      * means transparent, 1 or 255 means fully opaque.
      */
     typedef enum {
         ImageFormatARGB32, ///< ARGB data in native endianness, using premultiplied alpha
         ImageFormatRGB24,  ///< xRGB data in native endianness
--- a/gfx/thebes/gfxCoreTextShaper.cpp
+++ b/gfx/thebes/gfxCoreTextShaper.cpp
@@ -103,79 +103,69 @@ gfxCoreTextShaper::~gfxCoreTextShaper()
         ::CFRelease(mAttributesDict);
     }
     if (mCTFont) {
         ::CFRelease(mCTFont);
     }
 }
 
 bool
-gfxCoreTextShaper::InitTextRun(gfxContext *aContext,
-                               gfxTextRun *aTextRun,
-                               const PRUnichar *aString,
-                               PRUint32 aRunStart,
-                               PRUint32 aRunLength,
-                               PRInt32 aRunScript)
+gfxCoreTextShaper::ShapeWord(gfxContext      *aContext,
+                             gfxShapedWord   *aShapedWord,
+                             const PRUnichar *aText)
 {
-    // aRunStart and aRunLength define the section of the textRun and of aString
-    // that is to be drawn with this particular font
-
-    bool disableLigatures = (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) != 0;
-
     // Create a CFAttributedString with text and style info, so we can use CoreText to lay it out.
 
-    bool isRTL = aTextRun->IsRightToLeft();
+    bool isRightToLeft = aShapedWord->IsRightToLeft();
+    PRUint32 length = aShapedWord->Length();
 
     // we need to bidi-wrap the text if the run is RTL,
     // or if it is an LTR run but may contain (overridden) RTL chars
-    bool bidiWrap = isRTL;
-    if (!bidiWrap && (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) == 0) {
+    bool bidiWrap = isRightToLeft;
+    if (!bidiWrap && !aShapedWord->TextIs8Bit()) {
+        const PRUnichar *text = aShapedWord->TextUnicode();
         PRUint32 i;
-        for (i = aRunStart; i < aRunStart + aRunLength; ++i) {
-            if (gfxFontUtils::PotentialRTLChar(aString[i])) {
+        for (i = 0; i < length; ++i) {
+            if (gfxFontUtils::PotentialRTLChar(text[i])) {
                 bidiWrap = true;
                 break;
             }
         }
     }
 
     // If there's a possibility of any bidi, we wrap the text with direction overrides
     // to ensure neutrals or characters that were bidi-overridden in HTML behave properly.
     const UniChar beginLTR[]    = { 0x202d, 0x20 };
     const UniChar beginRTL[]    = { 0x202e, 0x20 };
     const UniChar endBidiWrap[] = { 0x20, 0x2e, 0x202c };
 
     PRUint32 startOffset;
     CFStringRef stringObj;
     if (bidiWrap) {
-        startOffset = isRTL ?
-            sizeof(beginRTL) / sizeof(beginRTL[0]) : sizeof(beginLTR) / sizeof(beginLTR[0]);
+        startOffset = isRightToLeft ?
+            mozilla::ArrayLength(beginRTL) : mozilla::ArrayLength(beginLTR);
         CFMutableStringRef mutableString =
             ::CFStringCreateMutable(kCFAllocatorDefault,
-                                    aRunLength + startOffset +
-                                    sizeof(endBidiWrap) / sizeof(endBidiWrap[0]));
+                                    length + startOffset + mozilla::ArrayLength(endBidiWrap));
         ::CFStringAppendCharacters(mutableString,
-                                   isRTL ? beginRTL : beginLTR,
+                                   isRightToLeft ? beginRTL : beginLTR,
                                    startOffset);
-        ::CFStringAppendCharacters(mutableString,
-                                   aString + aRunStart, aRunLength);
+        ::CFStringAppendCharacters(mutableString, aText, length);
         ::CFStringAppendCharacters(mutableString,
-                                   endBidiWrap,
-                                   sizeof(endBidiWrap) / sizeof(endBidiWrap[0]));
+                                   endBidiWrap, mozilla::ArrayLength(endBidiWrap));
         stringObj = mutableString;
     } else {
         startOffset = 0;
         stringObj = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
-                                                         aString + aRunStart,
-                                                         aRunLength,
+                                                         aText, length,
                                                          kCFAllocatorNull);
     }
 
     CFDictionaryRef attrObj;
-    if (disableLigatures) {
+    if (aShapedWord->DisableLigatures()) {
         // For letterspacing (or maybe other situations) we need to make a copy of the CTFont
         // with the ligature feature disabled
         CTFontRef ctFont =
             CreateCTFontWithDisabledLigatures(::CTFontGetSize(mCTFont));
 
         attrObj =
             ::CFDictionaryCreate(kCFAllocatorDefault,
                                  (const void**) &kCTFontAttributeName,
@@ -204,69 +194,64 @@ gfxCoreTextShaper::InitTextRun(gfxContex
     CFArrayRef glyphRuns = ::CTLineGetGlyphRuns(line);
     PRUint32 numRuns = ::CFArrayGetCount(glyphRuns);
 
     // Iterate through the glyph runs.
     // Note that this includes the bidi wrapper, so we have to be careful
     // not to include the extra glyphs from there
     bool success = true;
     for (PRUint32 runIndex = 0; runIndex < numRuns; runIndex++) {
-        CTRunRef aCTRun = (CTRunRef)::CFArrayGetValueAtIndex(glyphRuns, runIndex);
-        if (SetGlyphsFromRun(aTextRun, aCTRun, startOffset,
-                             aRunStart, aRunLength) != NS_OK) {
+        CTRunRef aCTRun =
+            (CTRunRef)::CFArrayGetValueAtIndex(glyphRuns, runIndex);
+        if (SetGlyphsFromRun(aShapedWord, aCTRun, startOffset) != NS_OK) {
             success = false;
             break;
         }
     }
 
     ::CFRelease(line);
 
     return success;
 }
 
 #define SMALL_GLYPH_RUN 128 // preallocated size of our auto arrays for per-glyph data;
                             // some testing indicates that 90%+ of glyph runs will fit
                             // without requiring a separate allocation
 
 nsresult
-gfxCoreTextShaper::SetGlyphsFromRun(gfxTextRun *aTextRun,
+gfxCoreTextShaper::SetGlyphsFromRun(gfxShapedWord *aShapedWord,
                                     CTRunRef aCTRun,
-                                    PRInt32 aStringOffset, // offset in the string used to build the CTLine
-                                    PRInt32 aRunStart,     // starting offset of this font run in the gfxTextRun
-                                    PRInt32 aRunLength)    // length of this font run in characters
+                                    PRInt32 aStringOffset)
 {
-    // The textRun has been bidi-wrapped; aStringOffset is the number
+    // The word has been bidi-wrapped; aStringOffset is the number
     // of chars at the beginning of the CTLine that we should skip.
-    // aRunStart and aRunLength define the range of characters
-    // within the textRun that are "real" data we need to handle.
     // aCTRun is a glyph run from the CoreText layout process.
 
-    bool isLTR = !aTextRun->IsRightToLeft();
-    PRInt32 direction = isLTR ? 1 : -1;
+    PRInt32 direction = aShapedWord->IsRightToLeft() ? -1 : 1;
 
     PRInt32 numGlyphs = ::CTRunGetGlyphCount(aCTRun);
     if (numGlyphs == 0) {
         return NS_OK;
     }
 
+    PRInt32 wordLength = aShapedWord->Length();
+
     // character offsets get really confusing here, as we have to keep track of
     // (a) the text in the actual textRun we're constructing
-    // (b) the "font run" being rendered with the current font, defined by aRunStart and aRunLength
-    //     parameters to InitTextRun
     // (c) the string that was handed to CoreText, which contains the text of the font run
     //     plus directional-override padding
     // (d) the CTRun currently being processed, which may be a sub-run of the CoreText line
     //     (but may extend beyond the actual font run into the bidi wrapping text).
     //     aStringOffset tells us how many initial characters of the line to ignore.
 
     // get the source string range within the CTLine's text
     CFRange stringRange = ::CTRunGetStringRange(aCTRun);
     // skip the run if it is entirely outside the actual range of the font run
     if (stringRange.location - aStringOffset + stringRange.length <= 0 ||
-        stringRange.location - aStringOffset >= aRunLength) {
+        stringRange.location - aStringOffset >= wordLength) {
         return NS_OK;
     }
 
     // retrieve the laid-out glyph data from the CTRun
     nsAutoArrayPtr<CGGlyph> glyphsArray;
     nsAutoArrayPtr<CGPoint> positionsArray;
     nsAutoArrayPtr<CFIndex> glyphToCharArray;
     const CGGlyph* glyphs = NULL;
@@ -312,17 +297,16 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxT
         ::CTRunGetStringIndices(aCTRun, ::CFRangeMake(0, 0), glyphToCharArray.get());
         glyphToChar = glyphToCharArray.get();
     }
 
     double runWidth = ::CTRunGetTypographicBounds(aCTRun, ::CFRangeMake(0, 0), NULL, NULL, NULL);
 
     nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
     gfxTextRun::CompressedGlyph g;
-    const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
 
     // CoreText gives us the glyphindex-to-charindex mapping, which relates each glyph
     // to a source text character; we also need the charindex-to-glyphindex mapping to
     // find the glyph for a given char. Note that some chars may not map to any glyph
     // (ligature continuations), and some may map to several glyphs (eg Indic split vowels).
     // We set the glyph index to NO_GLYPH for chars that have no associated glyph, and we
     // record the last glyph index for cases where the char maps to several glyphs,
     // so that our clumping will include all the glyph fragments for the character.
@@ -352,31 +336,33 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxT
     // and extend it until it includes the character associated with the first glyph;
     // we also extend it as long as there are "holes" in the range of glyphs. So we
     // will eventually have a contiguous sequence of characters, starting at the beginning
     // of the range, that map to a contiguous sequence of glyphs, starting at the beginning
     // of the glyph array. That's a clump; then we update the starting positions and repeat.
     //
     // NB: In the case of RTL layouts, we iterate over the stringRange in reverse.
     //
-    // This may find characters that fall outside the range aRunStart:aRunLength,
+
+    // This may find characters that fall outside the range 0:wordLength,
     // so we won't necessarily use everything we find here.
 
+    bool isRightToLeft = aShapedWord->IsRightToLeft();
     PRInt32 glyphStart = 0; // looking for a clump that starts at this glyph index
-    PRInt32 charStart = isLTR ?
-        0 : stringRange.length-1; // and this char index (in the stringRange of the glyph run)
+    PRInt32 charStart = isRightToLeft ?
+        stringRange.length - 1 : 0; // and this char index (in the stringRange of the glyph run)
 
     while (glyphStart < numGlyphs) { // keep finding groups until all glyphs are accounted for
 
         bool inOrder = true;
         PRInt32 charEnd = glyphToChar[glyphStart] - stringRange.location;
         NS_ASSERTION(charEnd >= 0 && charEnd < stringRange.length,
                      "glyph-to-char mapping points outside string range");
         PRInt32 glyphEnd = glyphStart;
-        PRInt32 charLimit = isLTR ? stringRange.length : -1;
+        PRInt32 charLimit = isRightToLeft ? -1 : stringRange.length;
         do {
             // This is normally executed once for each iteration of the outer loop,
             // but in unusual cases where the character/glyph association is complex,
             // the initial character range might correspond to a non-contiguous
             // glyph range with "holes" in it. If so, we will repeat this loop to
             // extend the character range until we have a contiguous glyph sequence.
             NS_ASSERTION((direction > 0 && charEnd < charLimit) ||
                          (direction < 0 && charEnd > charLimit),
@@ -405,93 +391,95 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxT
 
             // check whether all glyphs in the range are associated with the characters
             // in our clump; if not, we have a discontinuous range, and should extend it
             // unless we've reached the end of the text
             bool allGlyphsAreWithinCluster = true;
             PRInt32 prevGlyphCharIndex = charStart;
             for (PRInt32 i = glyphStart; i < glyphEnd; ++i) {
                 PRInt32 glyphCharIndex = glyphToChar[i] - stringRange.location;
-                if (isLTR) {
+                if (isRightToLeft) {
+                    if (glyphCharIndex > charStart || glyphCharIndex <= charEnd) {
+                        allGlyphsAreWithinCluster = false;
+                        break;
+                    }
+                    if (glyphCharIndex > prevGlyphCharIndex) {
+                        inOrder = false;
+                    }
+                    prevGlyphCharIndex = glyphCharIndex;
+                } else {
                     if (glyphCharIndex < charStart || glyphCharIndex >= charEnd) {
                         allGlyphsAreWithinCluster = false;
                         break;
                     }
                     if (glyphCharIndex < prevGlyphCharIndex) {
                         inOrder = false;
                     }
                     prevGlyphCharIndex = glyphCharIndex;
-                } else {
-                    if (glyphCharIndex > charStart || glyphCharIndex <= charEnd) {
-                        allGlyphsAreWithinCluster = false;
-                        break;
-                    }
-                    if (glyphCharIndex > prevGlyphCharIndex) {
-                        inOrder = false;
-                    }
-                    prevGlyphCharIndex = glyphCharIndex;
                 }
             }
             if (allGlyphsAreWithinCluster) {
                 break;
             }
         } while (charEnd != charLimit);
 
         NS_ASSERTION(glyphStart < glyphEnd, "character/glyph clump contains no glyphs!");
         NS_ASSERTION(charStart != charEnd, "character/glyph contains no characters!");
 
         // Now charStart..charEnd is a ligature clump, corresponding to glyphStart..glyphEnd;
         // Set baseCharIndex to the char we'll actually attach the glyphs to (1st of ligature),
         // and endCharIndex to the limit (position beyond the last char),
         // adjusting for the offset of the stringRange relative to the textRun.
         PRInt32 baseCharIndex, endCharIndex;
-        if (isLTR) {
+        if (isRightToLeft) {
+            while (charEnd >= 0 && charToGlyph[charEnd] == NO_GLYPH) {
+                charEnd--;
+            }
+            baseCharIndex = charEnd + stringRange.location - aStringOffset + 1;
+            endCharIndex = charStart + stringRange.location - aStringOffset + 1;
+        } else {
             while (charEnd < stringRange.length && charToGlyph[charEnd] == NO_GLYPH) {
                 charEnd++;
             }
-            baseCharIndex = charStart + stringRange.location - aStringOffset + aRunStart;
-            endCharIndex = charEnd + stringRange.location - aStringOffset + aRunStart;
-        } else {
-            while (charEnd >= 0 && charToGlyph[charEnd] == NO_GLYPH) {
-                charEnd--;
-            }
-            baseCharIndex = charEnd + stringRange.location - aStringOffset + aRunStart + 1;
-            endCharIndex = charStart + stringRange.location - aStringOffset + aRunStart + 1;
+            baseCharIndex = charStart + stringRange.location - aStringOffset;
+            endCharIndex = charEnd + stringRange.location - aStringOffset;
         }
 
         // Then we check if the clump falls outside our actual string range; if so, just go to the next.
-        if (endCharIndex <= aRunStart || baseCharIndex >= aRunStart + aRunLength) {
+        if (endCharIndex <= 0 || baseCharIndex >= wordLength) {
             glyphStart = glyphEnd;
             charStart = charEnd;
             continue;
         }
-        // Ensure we won't try to go beyond the valid length of the textRun's text
-        baseCharIndex = NS_MAX(baseCharIndex, aRunStart);
-        endCharIndex = NS_MIN(endCharIndex, aRunStart + aRunLength);
+        // Ensure we won't try to go beyond the valid length of the word's text
+        baseCharIndex = NS_MAX(baseCharIndex, 0);
+        endCharIndex = NS_MIN(endCharIndex, wordLength);
 
         // Now we're ready to set the glyph info in the textRun; measure the glyph width
         // of the first (perhaps only) glyph, to see if it is "Simple"
+        PRInt32 appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
         double toNextGlyph;
         if (glyphStart < numGlyphs-1) {
             toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
         } else {
             toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x;
         }
         PRInt32 advance = PRInt32(toNextGlyph * appUnitsPerDevUnit);
 
         // Check if it's a simple one-to-one mapping
         PRInt32 glyphsInClump = glyphEnd - glyphStart;
         if (glyphsInClump == 1 &&
             gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyphs[glyphStart]) &&
             gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
-            aTextRun->IsClusterStart(baseCharIndex) &&
+            aShapedWord->IsClusterStart(baseCharIndex) &&
             positions[glyphStart].y == 0.0)
         {
-            aTextRun->SetSimpleGlyph(baseCharIndex,
-                                     g.SetSimpleGlyph(advance, glyphs[glyphStart]));
+            aShapedWord->SetSimpleGlyph(baseCharIndex,
+                                        g.SetSimpleGlyph(advance,
+                                                         glyphs[glyphStart]));
         } else {
             // collect all glyphs in a list to be assigned to the first char;
             // there must be at least one in the clump, and we already measured its advance,
             // hence the placement of the loop-exit test and the measurement of the next glyph
             while (1) {
                 gfxTextRun::DetailedGlyph *details = detailedGlyphs.AppendElement();
                 details->mGlyphID = glyphs[glyphStart];
                 details->mXOffset = 0;
@@ -504,28 +492,28 @@ gfxCoreTextShaper::SetGlyphsFromRun(gfxT
                     toNextGlyph = positions[glyphStart+1].x - positions[glyphStart].x;
                 } else {
                     toNextGlyph = positions[0].x + runWidth - positions[glyphStart].x;
                 }
                 advance = PRInt32(toNextGlyph * appUnitsPerDevUnit);
             }
 
             gfxTextRun::CompressedGlyph g;
-            g.SetComplex(aTextRun->IsClusterStart(baseCharIndex),
+            g.SetComplex(aShapedWord->IsClusterStart(baseCharIndex),
                          true, detailedGlyphs.Length());
-            aTextRun->SetGlyphs(baseCharIndex, g, detailedGlyphs.Elements());
+            aShapedWord->SetGlyphs(baseCharIndex, g, detailedGlyphs.Elements());
 
             detailedGlyphs.Clear();
         }
 
         // the rest of the chars in the group are ligature continuations, no associated glyphs
-        while (++baseCharIndex != endCharIndex && baseCharIndex < aRunStart + aRunLength) {
-            g.SetComplex(inOrder && aTextRun->IsClusterStart(baseCharIndex),
+        while (++baseCharIndex != endCharIndex && baseCharIndex < wordLength) {
+            g.SetComplex(inOrder && aShapedWord->IsClusterStart(baseCharIndex),
                          false, 0);
-            aTextRun->SetGlyphs(baseCharIndex, g, nsnull);
+            aShapedWord->SetGlyphs(baseCharIndex, g, nsnull);
         }
 
         glyphStart = glyphEnd;
         charStart = charEnd;
     }
 
     return NS_OK;
 }
--- a/gfx/thebes/gfxCoreTextShaper.h
+++ b/gfx/thebes/gfxCoreTextShaper.h
@@ -52,35 +52,30 @@
 class gfxMacFont;
 
 class gfxCoreTextShaper : public gfxFontShaper {
 public:
     gfxCoreTextShaper(gfxMacFont *aFont);
 
     virtual ~gfxCoreTextShaper();
 
-    virtual bool InitTextRun(gfxContext *aContext,
-                               gfxTextRun *aTextRun,
-                               const PRUnichar *aString,
-                               PRUint32 aRunStart,
-                               PRUint32 aRunLength,
-                               PRInt32 aRunScript);
+    virtual bool ShapeWord(gfxContext *aContext,
+                           gfxShapedWord *aShapedWord,
+                           const PRUnichar *aText);
 
     // clean up static objects that may have been cached
     static void Shutdown();
 
 protected:
     CTFontRef mCTFont;
     CFDictionaryRef mAttributesDict;
 
-    nsresult SetGlyphsFromRun(gfxTextRun *aTextRun,
+    nsresult SetGlyphsFromRun(gfxShapedWord *aShapedWord,
                               CTRunRef aCTRun,
-                              PRInt32 aStringOffset,
-                              PRInt32 aLayoutStart,
-                              PRInt32 aLayoutLength);
+                              PRInt32 aStringOffset);
 
     CTFontRef CreateCTFontWithDisabledLigatures(CGFloat aSize);
 
     static void CreateDefaultFeaturesDescriptor();
 
     static CTFontDescriptorRef GetDefaultFeaturesDescriptor() {
         if (sDefaultFeaturesDescriptor == NULL) {
             CreateDefaultFeaturesDescriptor();
--- a/gfx/thebes/gfxDWriteShaper.cpp
+++ b/gfx/thebes/gfxDWriteShaper.cpp
@@ -39,261 +39,219 @@
 #include "gfxWindowsPlatform.h"
 
 #include <dwrite.h>
 
 #include "gfxDWriteTextAnalysis.h"
 
 #include "nsCRT.h"
 
-#define MAX_RANGE_LENGTH 25000
-
 bool
-gfxDWriteShaper::InitTextRun(gfxContext *aContext,
-                             gfxTextRun *aTextRun,
-                             const PRUnichar *aString,
-                             PRUint32 aRunStart,
-                             PRUint32 aRunLength,
-                             PRInt32 aRunScript)
+gfxDWriteShaper::ShapeWord(gfxContext *aContext,
+                           gfxShapedWord *aShapedWord,
+                           const PRUnichar *aString)
 {
     HRESULT hr;
     // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES
 
     DWRITE_READING_DIRECTION readingDirection = 
-        aTextRun->IsRightToLeft()
+        aShapedWord->IsRightToLeft()
             ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT
             : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
 
     gfxDWriteFont *font = static_cast<gfxDWriteFont*>(mFont);
 
-    gfxTextRun::CompressedGlyph g;
-
-    nsRefPtr<IDWriteTextAnalyzer> analyzer;
+    gfxShapedWord::CompressedGlyph g;
 
-    hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
-        CreateTextAnalyzer(getter_AddRefs(analyzer));
-    if (FAILED(hr)) {
+    IDWriteTextAnalyzer *analyzer =
+        gfxWindowsPlatform::GetPlatform()->GetDWriteAnalyzer();
+    if (!analyzer) {
         return false;
     }
 
     /**
-     * There's an internal 16-bit limit on some things inside the analyzer.
-     * to be on the safe side here we split up any ranges over MAX_RANGE_LENGTH 
-     * characters.
-     * TODO: Figure out what exactly is going on, and what is a safe number, and 
-     * why.
+     * There's an internal 16-bit limit on some things inside the analyzer,
+     * but we never attempt to shape a word longer than 64K characters
+     * in a single gfxShapedWord, so we cannot exceed that limit.
      */
-    bool result = true;
-    UINT32 rangeOffset = 0;
-    while (rangeOffset < aRunLength) {
-        PRUint32 rangeLen = NS_MIN<PRUint32>(aRunLength - rangeOffset,
-                                             MAX_RANGE_LENGTH);
-        if (rangeOffset + rangeLen < aRunLength) {
-            // Iterate backwards to find a decent place to break shaping:
-            // look for a whitespace char, and if that's not found then
-            // at least a char where IsClusterStart is true.
-            // However, we never iterate back further than half-way;
-            // if a decent break is not found by then, we just chop anyway
-            // at the original range length.
-            PRUint32 adjRangeLen = 0;
-            const PRUnichar *rangeStr = aString + aRunStart + rangeOffset;
-            for (PRUint32 i = rangeLen; i > MAX_RANGE_LENGTH / 2; i--) {
-                if (nsCRT::IsAsciiSpace(rangeStr[i])) {
-                    adjRangeLen = i;
-                    break;
-                }
-                if (adjRangeLen == 0 &&
-                    aTextRun->IsClusterStart(aRunStart + rangeOffset + i)) {
-                    adjRangeLen = i;
-                }
-            }
-            if (adjRangeLen != 0) {
-                rangeLen = adjRangeLen;
-            }
-        }
+    UINT32 length = aShapedWord->Length();
+
+    TextAnalysis analysis(aString, length, NULL, readingDirection);
+    TextAnalysis::Run *runHead;
+    hr = analysis.GenerateResults(analyzer, &runHead);
+
+    if (FAILED(hr)) {
+        NS_WARNING("Analyzer failed to generate results.");
+        return false;
+    }
+
+    PRUint32 appUnitsPerDevPixel = aShapedWord->AppUnitsPerDevUnit();
 
-        PRUint32 rangeStart = aRunStart + rangeOffset;
-        rangeOffset += rangeLen;
-        TextAnalysis analysis(aString + rangeStart, rangeLen,
-            NULL, 
-            readingDirection);
-        TextAnalysis::Run *runHead;
-        DWRITE_LINE_BREAKPOINT *linebreaks;
-        hr = analysis.GenerateResults(analyzer, &runHead, &linebreaks);
-
-        if (FAILED(hr)) {
-            NS_WARNING("Analyzer failed to generate results.");
-            result = false;
-            break;
-        }
-
-        PRUint32 appUnitsPerDevPixel = aTextRun->GetAppUnitsPerDevUnit();
-
-        UINT32 maxGlyphs = 0;
+    UINT32 maxGlyphs = 0;
 trymoreglyphs:
-        if ((PR_UINT32_MAX - 3 * rangeLen / 2 + 16) < maxGlyphs) {
-            // This isn't going to work, we're going to cross the UINT32 upper
-            // limit. Next range it is.
-            continue;
-        }
-        maxGlyphs += 3 * rangeLen / 2 + 16;
+    if ((PR_UINT32_MAX - 3 * length / 2 + 16) < maxGlyphs) {
+        // This isn't going to work, we're going to cross the UINT32 upper
+        // limit. Give up.
+        NS_WARNING("Shaper needs to generate more than 2^32 glyphs?!");
+        return false;
+    }
+    maxGlyphs += 3 * length / 2 + 16;
 
-        nsAutoTArray<UINT16, 400> clusters;
-        nsAutoTArray<UINT16, 400> indices;
-        nsAutoTArray<DWRITE_SHAPING_TEXT_PROPERTIES, 400> textProperties;
-        nsAutoTArray<DWRITE_SHAPING_GLYPH_PROPERTIES, 400> glyphProperties;
-        if (!clusters.SetLength(rangeLen) ||
-            !indices.SetLength(maxGlyphs) || 
-            !textProperties.SetLength(maxGlyphs) ||
-            !glyphProperties.SetLength(maxGlyphs)) {
-                continue;
-        }
+    nsAutoTArray<UINT16, 400> clusters;
+    nsAutoTArray<UINT16, 400> indices;
+    nsAutoTArray<DWRITE_SHAPING_TEXT_PROPERTIES, 400> textProperties;
+    nsAutoTArray<DWRITE_SHAPING_GLYPH_PROPERTIES, 400> glyphProperties;
+    if (!clusters.SetLength(length) ||
+        !indices.SetLength(maxGlyphs) || 
+        !textProperties.SetLength(maxGlyphs) ||
+        !glyphProperties.SetLength(maxGlyphs)) {
+        NS_WARNING("Shaper failed to allocate memory.");
+        return false;
+    }
 
-        UINT32 actualGlyphs;
+    UINT32 actualGlyphs;
 
-        hr = analyzer->GetGlyphs(aString + rangeStart, rangeLen,
+    hr = analyzer->GetGlyphs(aString, length,
             font->GetFontFace(), FALSE, 
             readingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
             &runHead->mScript, NULL, NULL, NULL, NULL, 0,
             maxGlyphs, clusters.Elements(), textProperties.Elements(),
             indices.Elements(), glyphProperties.Elements(), &actualGlyphs);
 
-        if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
-            // We increase the amount of glyphs and try again.
-            goto trymoreglyphs;
-        }
-        if (FAILED(hr)) {
-            NS_WARNING("Analyzer failed to get glyphs.");
-            result = false;
-            break;
-        }
+    if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
+        // We increase the amount of glyphs and try again.
+        goto trymoreglyphs;
+    }
+    if (FAILED(hr)) {
+        NS_WARNING("Analyzer failed to get glyphs.");
+        return false;
+    }
+
+    WORD gID = indices[0];
+    nsAutoTArray<FLOAT, 400> advances;
+    nsAutoTArray<DWRITE_GLYPH_OFFSET, 400> glyphOffsets;
+    if (!advances.SetLength(actualGlyphs) || 
+        !glyphOffsets.SetLength(actualGlyphs)) {
+        NS_WARNING("Shaper failed to allocate memory.");
+        return false;
+    }
 
-        WORD gID = indices[0];
-        nsAutoTArray<FLOAT, 400> advances;
-        nsAutoTArray<DWRITE_GLYPH_OFFSET, 400> glyphOffsets;
-        if (!advances.SetLength(actualGlyphs) || 
-            !glyphOffsets.SetLength(actualGlyphs)) {
+    if (!static_cast<gfxDWriteFont*>(mFont)->mUseSubpixelPositions) {
+        hr = analyzer->GetGdiCompatibleGlyphPlacements(
+                                          aString,
+                                          clusters.Elements(),
+                                          textProperties.Elements(),
+                                          length,
+                                          indices.Elements(),
+                                          glyphProperties.Elements(),
+                                          actualGlyphs,
+                                          font->GetFontFace(),
+                                          font->GetAdjustedSize(),
+                                          1.0,
+                                          nsnull,
+                                          FALSE,
+                                          FALSE,
+                                          FALSE,
+                                          &runHead->mScript,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          0,
+                                          advances.Elements(),
+                                          glyphOffsets.Elements());
+    } else {
+        hr = analyzer->GetGlyphPlacements(aString,
+                                          clusters.Elements(),
+                                          textProperties.Elements(),
+                                          length,
+                                          indices.Elements(),
+                                          glyphProperties.Elements(),
+                                          actualGlyphs,
+                                          font->GetFontFace(),
+                                          font->GetAdjustedSize(),
+                                          FALSE,
+                                          FALSE,
+                                          &runHead->mScript,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          0,
+                                          advances.Elements(),
+                                          glyphOffsets.Elements());
+    }
+    if (FAILED(hr)) {
+        NS_WARNING("Analyzer failed to get glyph placements.");
+        return false;
+    }
+
+    nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
+
+    for (unsigned int c = 0; c < length; c++) {
+        PRUint32 k = clusters[c];
+        PRUint32 absC = c;
+
+        if (c > 0 && k == clusters[c - 1]) {
+            g.SetComplex(aShapedWord->IsClusterStart(absC), false, 0);
+            aShapedWord->SetGlyphs(absC, g, nsnull);
+            // This is a cluster continuation. No glyph here.
             continue;
         }
 
-        if (!static_cast<gfxDWriteFont*>(mFont)->mUseSubpixelPositions) {
-            hr = analyzer->GetGdiCompatibleGlyphPlacements(
-                                              aString + rangeStart,
-                                              clusters.Elements(),
-                                              textProperties.Elements(),
-                                              rangeLen,
-                                              indices.Elements(),
-                                              glyphProperties.Elements(),
-                                              actualGlyphs,
-                                              font->GetFontFace(),
-                                              font->GetAdjustedSize(),
-                                              1.0,
-                                              nsnull,
-                                              FALSE,
-                                              FALSE,
-                                              FALSE,
-                                              &runHead->mScript,
-                                              NULL,
-                                              NULL,
-                                              NULL,
-                                              0,
-                                              advances.Elements(),
-                                              glyphOffsets.Elements());
+        // Count glyphs for this character
+        PRUint32 glyphCount = actualGlyphs - k;
+        PRUint32 nextClusterOffset;
+        for (nextClusterOffset = c + 1; 
+            nextClusterOffset < length; ++nextClusterOffset) {
+            if (clusters[nextClusterOffset] > k) {
+                glyphCount = clusters[nextClusterOffset] - k;
+                break;
+            }
+        }
+        PRInt32 advance = (PRInt32)(advances[k] * appUnitsPerDevPixel);
+        if (glyphCount == 1 && advance >= 0 &&
+            glyphOffsets[k].advanceOffset == 0 &&
+            glyphOffsets[k].ascenderOffset == 0 &&
+            aShapedWord->IsClusterStart(absC) &&
+            gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advance) &&
+            gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(indices[k])) {
+              aShapedWord->SetSimpleGlyph(absC, 
+                                          g.SetSimpleGlyph(advance, 
+                                                           indices[k]));
         } else {
-            hr = analyzer->GetGlyphPlacements(aString + rangeStart,
-                                              clusters.Elements(),
-                                              textProperties.Elements(),
-                                              rangeLen,
-                                              indices.Elements(),
-                                              glyphProperties.Elements(),
-                                              actualGlyphs,
-                                              font->GetFontFace(),
-                                              font->GetAdjustedSize(),
-                                              FALSE,
-                                              FALSE,
-                                              &runHead->mScript,
-                                              NULL,
-                                              NULL,
-                                              NULL,
-                                              0,
-                                              advances.Elements(),
-                                              glyphOffsets.Elements());
-        }
-        if (FAILED(hr)) {
-            NS_WARNING("Analyzer failed to get glyph placements.");
-            result = false;
-            break;
-        }
-
-        nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
-
-        for (unsigned int c = 0; c < rangeLen; c++) {
-            PRUint32 k = clusters[c];
-            PRUint32 absC = rangeStart + c;
-
-            if (c > 0 && k == clusters[c - 1]) {
-                g.SetComplex(aTextRun->IsClusterStart(absC), false, 0);
-                aTextRun->SetGlyphs(absC, g, nsnull);
-                // This is a cluster continuation. No glyph here.
-                continue;
-            }
-
-            // Count glyphs for this character
-            PRUint32 glyphCount = actualGlyphs - k;
-            PRUint32 nextClusterOffset;
-            for (nextClusterOffset = c + 1; 
-                nextClusterOffset < rangeLen; ++nextClusterOffset) {
-                if (clusters[nextClusterOffset] > k) {
-                    glyphCount = clusters[nextClusterOffset] - k;
-                    break;
+            if (detailedGlyphs.Length() < glyphCount) {
+                if (!detailedGlyphs.AppendElements(
+                    glyphCount - detailedGlyphs.Length())) {
+                    continue;
                 }
             }
-            PRInt32 advance = (PRInt32)(advances[k] * appUnitsPerDevPixel);
-            if (glyphCount == 1 && advance >= 0 &&
-                glyphOffsets[k].advanceOffset == 0 &&
-                glyphOffsets[k].ascenderOffset == 0 &&
-                aTextRun->IsClusterStart(absC) &&
-                gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
-                gfxTextRun::CompressedGlyph::IsSimpleGlyphID(indices[k])) {
-                  aTextRun->SetSimpleGlyph(absC, 
-                                          g.SetSimpleGlyph(advance, 
-                                                           indices[k]));
-            } else {
-                if (detailedGlyphs.Length() < glyphCount) {
-                    if (!detailedGlyphs.AppendElements(
-                        glyphCount - detailedGlyphs.Length())) {
-                        continue;
-                    }
+            float totalAdvance = 0;
+            for (unsigned int z = 0; z < glyphCount; z++) {
+                detailedGlyphs[z].mGlyphID = indices[k + z];
+                detailedGlyphs[z].mAdvance = 
+                    (PRInt32)(advances[k + z]
+                       * appUnitsPerDevPixel);
+                if (readingDirection == 
+                    DWRITE_READING_DIRECTION_RIGHT_TO_LEFT) {
+                    detailedGlyphs[z].mXOffset = 
+                        (totalAdvance + 
+                          glyphOffsets[k + z].advanceOffset)
+                        * appUnitsPerDevPixel;
+                } else {
+                    detailedGlyphs[z].mXOffset = 
+                        glyphOffsets[k + z].advanceOffset *
+                        appUnitsPerDevPixel;
                 }
-                float totalAdvance = 0;
-                for (unsigned int z = 0; z < glyphCount; z++) {
-                    detailedGlyphs[z].mGlyphID = indices[k + z];
-                    detailedGlyphs[z].mAdvance = 
-                        (PRInt32)(advances[k + z]
-                           * appUnitsPerDevPixel);
-                    if (readingDirection == 
-                        DWRITE_READING_DIRECTION_RIGHT_TO_LEFT) {
-                        detailedGlyphs[z].mXOffset = 
-                            (totalAdvance + 
-                              glyphOffsets[k + z].advanceOffset)
-                            * appUnitsPerDevPixel;
-                    } else {
-                        detailedGlyphs[z].mXOffset = 
-                            glyphOffsets[k + z].advanceOffset *
-                            appUnitsPerDevPixel;
-                    }
-                    detailedGlyphs[z].mYOffset = 
-                        -glyphOffsets[k + z].ascenderOffset *
-                        appUnitsPerDevPixel;
-                    totalAdvance += advances[k + z];
-                }
-                aTextRun->SetGlyphs(
-                    absC,
-                    g.SetComplex(aTextRun->IsClusterStart(absC),
-                                 true,
-                                 glyphCount),
-                    detailedGlyphs.Elements());
+                detailedGlyphs[z].mYOffset = 
+                    -glyphOffsets[k + z].ascenderOffset *
+                    appUnitsPerDevPixel;
+                totalAdvance += advances[k + z];
             }
+            aShapedWord->SetGlyphs(
+                absC,
+                g.SetComplex(aShapedWord->IsClusterStart(absC),
+                             true,
+                             glyphCount),
+                detailedGlyphs.Elements());
         }
     }
 
-    return result;
+    return true;
 }
--- a/gfx/thebes/gfxDWriteShaper.h
+++ b/gfx/thebes/gfxDWriteShaper.h
@@ -52,17 +52,14 @@ public:
         MOZ_COUNT_CTOR(gfxDWriteShaper);
     }
 
     virtual ~gfxDWriteShaper()
     {
         MOZ_COUNT_DTOR(gfxDWriteShaper);
     }
 
-    virtual bool InitTextRun(gfxContext *aContext,
-                               gfxTextRun *aTextRun,
-                               const PRUnichar *aString,
-                               PRUint32 aRunStart,
-                               PRUint32 aRunLength,
-                               PRInt32 aRunScript);
+    virtual bool ShapeWord(gfxContext *aContext,
+                           gfxShapedWord *aShapedWord,
+                           const PRUnichar *aString);
 };
 
 #endif /* GFX_DWRITESHAPER_H */
--- a/gfx/thebes/gfxDWriteTextAnalysis.cpp
+++ b/gfx/thebes/gfxDWriteTextAnalysis.cpp
@@ -40,80 +40,54 @@
 TextAnalysis::TextAnalysis(const wchar_t* text,
                            UINT32 textLength,
                            const wchar_t* localeName,
                            DWRITE_READING_DIRECTION readingDirection)
   : mText(text)
   , mTextLength(textLength)
   , mLocaleName(localeName)
   , mReadingDirection(readingDirection)
-  , mBreakpoints(NULL)
-  , mRunHead(NULL)
   , mCurrentRun(NULL)
 {
 }
 
 TextAnalysis::~TextAnalysis()
 {
-    delete [] mBreakpoints;
-    for (Run *run = mRunHead; run;) {
+    // delete runs, except mRunHead which is part of the TextAnalysis object
+    for (Run *run = mRunHead.nextRun; run;) {
         Run *origRun = run;
         run = run->nextRun;
         delete origRun;
     }
 }
 
 STDMETHODIMP 
 TextAnalysis::GenerateResults(IDWriteTextAnalyzer* textAnalyzer,
-                              OUT Run **runHead,
-                              OUT DWRITE_LINE_BREAKPOINT **breakpoints)
+                              OUT Run **runHead)
 {
-    // Analyzes the text using each of the analyzers and returns
-    // their results as a series of runs.
+    // Analyzes the text using the script analyzer and returns
+    // the result as a series of runs.
 
     HRESULT hr = S_OK;
 
     // Initially start out with one result that covers the entire range.
     // This result will be subdivided by the analysis processes.
-    mRunHead = new Run;
-    
-    mRunHead->mTextStart = 0;
-    mRunHead->mTextLength = mTextLength;
-    mRunHead->mBidiLevel = 
+    mRunHead.mTextStart = 0;
+    mRunHead.mTextLength = mTextLength;
+    mRunHead.mBidiLevel = 
         (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
-    mRunHead->nextRun = NULL;
-    mCurrentRun = mRunHead;
-#ifdef USE_DWRITE_BREAKPOINTS
-    delete [] mBreakpoints;
-    mBreakpoints = new DWRITE_LINE_BREAKPOINT[mTextLength];
-#endif
+    mRunHead.nextRun = NULL;
+    mCurrentRun = &mRunHead;
 
     // Call each of the analyzers in sequence, recording their results.
-    if (
-#ifdef USE_DWRITE_BREAKPOINTS
-        SUCCEEDED(hr = textAnalyzer->AnalyzeLineBreakpoints(this,
-                                                            0,
-                                                            mTextLength,
-                                                            this)) && 
-#endif
-        SUCCEEDED(hr = textAnalyzer->AnalyzeBidi(this,
-                                                 0,
-                                                 mTextLength,
-                                                 this)) &&
-        SUCCEEDED(hr = textAnalyzer->AnalyzeScript(this,
+    if (SUCCEEDED(hr = textAnalyzer->AnalyzeScript(this,
                                                    0,
                                                    mTextLength,
-                                                   this)) &&
-        SUCCEEDED(hr = textAnalyzer->AnalyzeNumberSubstitution(this,
-                                                               0,
-                                                               mTextLength,
-                                                               this))) {
-        *breakpoints = mBreakpoints;
-
-        *runHead = mRunHead;
+                                                   this))) {
+        *runHead = &mRunHead;
     }
 
     return hr;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // IDWriteTextAnalysisSource source implementation
@@ -190,21 +164,17 @@ TextAnalysis::GetNumberSubstitution(UINT
 ////////////////////////////////////////////////////////////////////////////////
 // IDWriteTextAnalysisSink implementation
 
 IFACEMETHODIMP 
 TextAnalysis::SetLineBreakpoints(UINT32 textPosition,
                                  UINT32 textLength,
                                  DWRITE_LINE_BREAKPOINT const* lineBreakpoints)
 {
-    if (textLength > 0) {
-        memcpy(mBreakpoints + textPosition,
-               lineBreakpoints,
-               textLength * sizeof(DWRITE_LINE_BREAKPOINT));
-    }
+    // We don't use this for now.
     return S_OK;
 }
 
 
 IFACEMETHODIMP 
 TextAnalysis::SetScriptAnalysis(UINT32 textPosition,
                                 UINT32 textLength,
                                 DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
@@ -221,23 +191,17 @@ TextAnalysis::SetScriptAnalysis(UINT32 t
 
 
 IFACEMETHODIMP 
 TextAnalysis::SetBidiLevel(UINT32 textPosition,
                            UINT32 textLength,
                            UINT8 explicitLevel,
                            UINT8 resolvedLevel)
 {
-    SetCurrentRun(textPosition);
-    SplitCurrentRun(textPosition);
-    while (textLength > 0) {
-        Run *run = FetchNextRun(&textLength);
-        run->mBidiLevel = resolvedLevel;
-    }
-
+    // We don't use this for now.
     return S_OK;
 }
 
 
 IFACEMETHODIMP 
 TextAnalysis::SetNumberSubstitution(UINT32 textPosition,
                                     UINT32 textLength,
                                     IDWriteNumberSubstitution* numberSubstitution)
@@ -279,17 +243,17 @@ void TextAnalysis::SetCurrentRun(UINT32 
     // Since the analyzers generally return results in a forward manner,
     // this will usually just return early. If not, find the
     // corresponding run for the text position.
 
     if (mCurrentRun && mCurrentRun->ContainsTextPosition(textPosition)) {
         return;
     }
 
-    for (Run *run = mRunHead; run; run = run->nextRun) {
+    for (Run *run = &mRunHead; run; run = run->nextRun) {
         if (run->ContainsTextPosition(textPosition)) {
             mCurrentRun = run;
             return;
         }
     }
     NS_NOTREACHED("We should always be able to find the text position in one \
         of our runs");
 }
--- a/gfx/thebes/gfxDWriteTextAnalysis.h
+++ b/gfx/thebes/gfxDWriteTextAnalysis.h
@@ -101,18 +101,17 @@ public:
     TextAnalysis(const wchar_t* text,
                  UINT32 textLength,
                  const wchar_t* localeName,
                  DWRITE_READING_DIRECTION readingDirection);
 
     ~TextAnalysis();
 
     STDMETHODIMP GenerateResults(IDWriteTextAnalyzer* textAnalyzer,
-                                 Run **runHead,
-                                 DWRITE_LINE_BREAKPOINT **breakpoints);
+                                 Run **runHead);
 
     // IDWriteTextAnalysisSource implementation
 
     IFACEMETHODIMP GetTextAtPosition(UINT32 textPosition,
                                      OUT WCHAR const** textString,
                                      OUT UINT32* textLength);
 
     IFACEMETHODIMP GetTextBeforePosition(UINT32 textPosition,
@@ -167,16 +166,13 @@ protected:
     UINT32 mTextLength;
     const wchar_t* mText;
     const wchar_t* mLocaleName;
     DWRITE_READING_DIRECTION mReadingDirection;
 
     // Current processing state.
     Run *mCurrentRun;
 
-    // Output
-    Run *mRunHead;
-
-    // We do not use this for now, store anyway
-    DWRITE_LINE_BREAKPOINT *mBreakpoints;
+    // Output is a list of runs starting here
+    Run  mRunHead;
 };
 
 #endif /* GFX_DWRITETEXTANALYSIS_H */
--- a/gfx/thebes/gfxFT2Fonts.cpp
+++ b/gfx/thebes/gfxFT2Fonts.cpp
@@ -420,79 +420,78 @@ gfxFT2FontGroup::WhichSystemFontSupports
 
 #endif // !ANDROID
 
 /**
  * gfxFT2Font
  */
 
 bool
-gfxFT2Font::InitTextRun(gfxContext *aContext,
-                        gfxTextRun *aTextRun,
-                        const PRUnichar *aString,
-                        PRUint32 aRunStart,
-                        PRUint32 aRunLength,
-                        PRInt32 aRunScript,
-                        bool aPreferPlatformShaping)
+gfxFT2Font::ShapeWord(gfxContext *aContext,
+                      gfxShapedWord *aShapedWord,
+                      const PRUnichar *aString,
+                      bool aPreferPlatformShaping)
 {
     bool ok = false;
 
 #ifdef MOZ_GRAPHITE
     if (FontCanSupportGraphite()) {
         if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
             if (!mGraphiteShaper) {
                 mGraphiteShaper = new gfxGraphiteShaper(this);
             }
-            ok = mGraphiteShaper->InitTextRun(aContext, aTextRun, aString,
-                                              aRunStart, aRunLength,
-                                              aRunScript);
+            ok = mGraphiteShaper->ShapeWord(aContext, aShapedWord, aString);
         }
     }
 #endif
 
-    if (!ok && gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aRunScript)) {
+    if (!ok && gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aShapedWord->Script())) {
         if (!mHarfBuzzShaper) {
             gfxFT2LockedFace face(this);
             mFUnitsConvFactor = face.XScale();
 
             mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
         }
-        ok = mHarfBuzzShaper->InitTextRun(aContext, aTextRun, aString,
-                                          aRunStart, aRunLength, aRunScript);
+        ok = mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aString);
     }
 
     if (!ok) {
-        AddRange(aTextRun, aString, aRunStart, aRunLength);
+        AddRange(aShapedWord, aString);
     }
 
-    aTextRun->AdjustAdvancesForSyntheticBold(aContext, aRunStart, aRunLength);
+    if (IsSyntheticBold()) {
+        float synBoldOffset =
+            GetSyntheticBoldOffset() * CalcXScale(aContext);
+        aShapedWord->AdjustAdvancesForSyntheticBold(synBoldOffset);
+    }
 
     return true;
 }
 
 void
-gfxFT2Font::AddRange(gfxTextRun *aTextRun, const PRUnichar *str, PRUint32 offset, PRUint32 len)
+gfxFT2Font::AddRange(gfxShapedWord *aShapedWord, const PRUnichar *str)
 {
-    const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
+    const PRUint32 appUnitsPerDevUnit = aShapedWord->AppUnitsPerDevUnit();
     // we'll pass this in/figure it out dynamically, but at this point there can be only one face.
     gfxFT2LockedFace faceLock(this);
     FT_Face face = faceLock.get();
 
-    gfxTextRun::CompressedGlyph g;
+    gfxShapedWord::CompressedGlyph g;
 
     const gfxFT2Font::CachedGlyphData *cgd = nsnull, *cgdNext = nsnull;
 
     FT_UInt spaceGlyph = GetSpaceGlyph();
 
+    PRUint32 len = aShapedWord->Length();
     for (PRUint32 i = 0; i < len; i++) {
-        PRUint32 ch = str[offset + i];
+        PRUnichar ch = str[i];
 
         if (ch == 0) {
             // treat this null byte as a missing glyph, don't create a glyph for it
-            aTextRun->SetMissingGlyph(offset + i, 0);
+            aShapedWord->SetMissingGlyph(i, 0, this);
             continue;
         }
 
         NS_ASSERTION(!gfxFontGroup::IsInvalidChar(ch), "Invalid char detected");
 
         if (cgdNext) {
             cgd = cgdNext;
             cgdNext = nsnull;
@@ -503,22 +502,22 @@ gfxFT2Font::AddRange(gfxTextRun *aTextRu
         FT_UInt gid = cgd->glyphIndex;
         PRInt32 advance = 0;
 
         if (gid == 0) {
             advance = -1; // trigger the missing glyphs case below
         } else {
             // find next character and its glyph -- in case they exist
             // and exist in the current font face -- to compute kerning
-            PRUint32 chNext = 0;
+            PRUnichar chNext = 0;
             FT_UInt gidNext = 0;
             FT_Pos lsbDeltaNext = 0;
 
             if (FT_HAS_KERNING(face) && i + 1 < len) {
-                chNext = str[offset + i + 1];
+                chNext = str[i + 1];
                 if (chNext != 0) {
                     cgdNext = GetGlyphDataForChar(chNext);
                     gidNext = cgdNext->glyphIndex;
                     if (gidNext && gidNext != spaceGlyph)
                         lsbDeltaNext = cgdNext->lsbDelta;
                 }
             }
 
@@ -536,37 +535,33 @@ gfxFT2Font::AddRange(gfxTextRun *aTextRu
                 }
             }
 
             // convert 26.6 fixed point to app units
             // round rather than truncate to nearest pixel
             // because these advances are often scaled
             advance = ((advance * appUnitsPerDevUnit + 32) >> 6);
         }
-#ifdef DEBUG_thebes_2
-        printf(" gid=%d, advance=%d (%s)\n", gid, advance,
-               NS_LossyConvertUTF16toASCII(font->GetName()).get());
-#endif
 
         if (advance >= 0 &&
-            gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
-            gfxTextRun::CompressedGlyph::IsSimpleGlyphID(gid)) {
-            aTextRun->SetSimpleGlyph(offset + i, g.SetSimpleGlyph(advance, gid));
+            gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advance) &&
+            gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(gid)) {
+            aShapedWord->SetSimpleGlyph(i, g.SetSimpleGlyph(advance, gid));
         } else if (gid == 0) {
             // gid = 0 only happens when the glyph is missing from the font
-            aTextRun->SetMissingGlyph(offset + i, ch);
+            aShapedWord->SetMissingGlyph(i, ch, this);
         } else {
             gfxTextRun::DetailedGlyph details;
             details.mGlyphID = gid;
             NS_ASSERTION(details.mGlyphID == gid, "Seriously weird glyph ID detected!");
             details.mAdvance = advance;
             details.mXOffset = 0;
             details.mYOffset = 0;
-            g.SetComplex(aTextRun->IsClusterStart(offset + i), true, 1);
-            aTextRun->SetGlyphs(offset + i, g, &details);
+            g.SetComplex(aShapedWord->IsClusterStart(i), true, 1);
+            aShapedWord->SetGlyphs(i, g, &details);
         }
     }
 }
 
 gfxFT2Font::gfxFT2Font(cairo_scaled_font_t *aCairoFont,
                        FT2FontEntry *aFontEntry,
                        const gfxFontStyle *aFontStyle,
                        bool aNeedsBold)
--- a/gfx/thebes/gfxFT2Fonts.h
+++ b/gfx/thebes/gfxFT2Fonts.h
@@ -92,27 +92,24 @@ public: // new functions
             // this is a new entry, fill it
             FillGlyphDataForChar(ch, &entry->mData);
         }
 
         return &entry->mData;
     }
 
 protected:
-    virtual bool InitTextRun(gfxContext *aContext,
-                               gfxTextRun *aTextRun,
-                               const PRUnichar *aString,
-                               PRUint32 aRunStart,
-                               PRUint32 aRunLength,
-                               PRInt32 aRunScript,
-                               bool aPreferPlatformShaping = false);
+    virtual bool ShapeWord(gfxContext *aContext,
+                           gfxShapedWord *aShapedWord,
+                           const PRUnichar *aString,
+                           bool aPreferPlatformShaping = false);
 
     void FillGlyphDataForChar(PRUint32 ch, CachedGlyphData *gd);
 
-    void AddRange(gfxTextRun *aTextRun, const PRUnichar *str, PRUint32 offset, PRUint32 len);
+    void AddRange(gfxShapedWord *aShapedWord, const PRUnichar *str);
 
     typedef nsBaseHashtableET<nsUint32HashKey, CachedGlyphData> CharGlyphMapEntryType;
     typedef nsTHashtable<CharGlyphMapEntryType> CharGlyphMap;
     CharGlyphMap mCharGlyphCache;
 };
 
 #ifndef ANDROID // not needed on Android, uses the standard gfxFontGroup directly
 class THEBES_API gfxFT2FontGroup : public gfxFontGroup {
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -43,16 +43,17 @@
 #endif
 #include "prlog.h"
 
 #include "nsServiceManagerUtils.h"
 #include "nsReadableUtils.h"
 #include "nsExpirationTracker.h"
 #include "nsILanguageAtomService.h"
 #include "nsIMemoryReporter.h"
+#include "nsITimer.h"
 
 #include "gfxFont.h"
 #include "gfxPlatform.h"
 #include "gfxAtoms.h"
 
 #include "prtypes.h"
 #include "gfxTypes.h"
 #include "nsAlgorithm.h"
@@ -976,16 +977,46 @@ gfxFontCache::Shutdown()
     printf("Total glyph extents width-storage size allocated=%d\n", gGlyphExtentsWidthsTotalSize);
     printf("Number of simple glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerSimple);
     printf("Number of tight glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerTight);
     printf("Number of tight glyph extents lazily requested=%d\n", gGlyphExtentsSetupLazyTight);
     printf("Number of simple glyph extent setups that fell back to tight=%d\n", gGlyphExtentsSetupFallBackToTight);
 #endif
 }
 
+gfxFontCache::gfxFontCache()
+    : nsExpirationTracker<gfxFont,3>(FONT_TIMEOUT_SECONDS * 1000)
+{
+    mFonts.Init();
+    mWordCacheExpirationTimer = do_CreateInstance("@mozilla.org/timer;1");
+    if (mWordCacheExpirationTimer) {
+        mWordCacheExpirationTimer->
+            InitWithFuncCallback(WordCacheExpirationTimerCallback, this,
+                                 SHAPED_WORD_TIMEOUT_SECONDS * 1000,
+                                 nsITimer::TYPE_REPEATING_SLACK);
+    }
+}
+
+gfxFontCache::~gfxFontCache()
+{
+    if (mWordCacheExpirationTimer) {
+        mWordCacheExpirationTimer->Cancel();
+        mWordCacheExpirationTimer = nsnull;
+    }
+
+    // Expire everything that has a zero refcount, so we don't leak them.
+    AgeAllGenerations();
+    // All fonts should be gone.
+    NS_WARN_IF_FALSE(mFonts.Count() == 0,
+                     "Fonts still alive while shutting down gfxFontCache");
+    // Note that we have to delete everything through the expiration
+    // tracker, since there might be fonts not in the hashtable but in
+    // the tracker.
+}
+
 bool
 gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const
 {
     return aKey->mFontEntry == mFont->GetFontEntry() &&
            aKey->mStyle->Equals(*mFont->GetStyle());
 }
 
 already_AddRefed<gfxFont>
@@ -1050,16 +1081,32 @@ gfxFontCache::DestroyFont(gfxFont *aFont
     HashEntry *entry = mFonts.GetEntry(key);
     if (entry && entry->mFont == aFont)
         mFonts.RemoveEntry(key);
     NS_ASSERTION(aFont->GetRefCount() == 0,
                  "Destroying with non-zero ref count!");
     delete aFont;
 }
 
+/*static*/
+PLDHashOperator
+gfxFontCache::AgeCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
+{
+    aHashEntry->mFont->AgeCachedWords();
+    return PL_DHASH_NEXT;
+}
+
+/*static*/
+void
+gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache)
+{
+    gfxFontCache* cache = static_cast<gfxFontCache*>(aCache);
+    cache->mFonts.EnumerateEntries(AgeCachedWordsForFont, nsnull);
+}
+
 void
 gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
 {
     mAscent = NS_MAX(mAscent, aOther.mAscent);
     mDescent = NS_MAX(mDescent, aOther.mDescent);
     if (aOtherIsOnLeft) {
         mBoundingBox =
             (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox);
@@ -1091,16 +1138,30 @@ gfxFont::~gfxFont()
     // We destroy the contents of mGlyphExtentsArray explicitly instead of
     // using nsAutoPtr because VC++ can't deal with nsTArrays of nsAutoPtrs
     // of classes that lack a proper copy constructor
     for (i = 0; i < mGlyphExtentsArray.Length(); ++i) {
         delete mGlyphExtentsArray[i];
     }
 }
 
+/*static*/
+PLDHashOperator
+gfxFont::AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData)
+{
+    if (!aEntry->mShapedWord) {
+        NS_ASSERTION(aEntry->mShapedWord, "cache entry has no gfxShapedWord!");
+        return PL_DHASH_REMOVE;
+    }
+    if (aEntry->mShapedWord->IncrementAge() == kShapedWordCacheMaxAge) {
+        return PL_DHASH_REMOVE;
+    }
+    return PL_DHASH_NEXT;
+}
+
 hb_blob_t *
 gfxFont::GetFontTable(PRUint32 aTag) {
     hb_blob_t *blob;
     if (mFontEntry->GetExistingFontTable(aTag, &blob))
         return blob;
 
     FallibleTArray<PRUint8> buffer;
     bool haveTable = NS_SUCCEEDED(mFontEntry->GetFontTable(aTag, buffer));
@@ -1192,18 +1253,18 @@ struct GlyphBufferAzure {
 };
 
 // Bug 674909. When synthetic bolding text by drawing twice, need to
 // render using a pixel offset in device pixels, otherwise text
 // doesn't appear bolded, it appears as if a bad text shadow exists
 // when a non-identity transform exists.  Use an offset factor so that
 // the second draw occurs at a constant offset in device pixels.
 
-static double
-CalcXScale(gfxContext *aContext)
+double
+gfxFont::CalcXScale(gfxContext *aContext)
 {
     // determine magnitude of a 1px x offset in device space
     gfxSize t = aContext->UserToDevice(gfxSize(1.0, 0.0));
     if (t.width == 1.0 && t.height == 0.0) {
         // short-circuit the most common case to avoid sqrt() and division
         return 1.0;
     }
 
@@ -1723,144 +1784,325 @@ gfxFont::Measure(gfxTextRun *aTextRun,
 #define MAX_SHAPING_LENGTH  32760 // slightly less than 32K, trying to avoid
                                   // over-stressing platform shapers
 
 #define BACKTRACK_LIMIT  1024 // If we can't find a space or a cluster start
                               // within 1K chars, just chop arbitrarily.
                               // Limiting backtrack here avoids pathological
                               // behavior on long runs with no whitespace.
 
-bool
-gfxFont::SplitAndInitTextRun(gfxContext *aContext,
-                             gfxTextRun *aTextRun,
-                             const PRUnichar *aString,
-                             PRUint32 aRunStart,
-                             PRUint32 aRunLength,
-                             PRInt32 aRunScript)
+static bool
+IsClusterExtender(PRUint32 aUSV)
+{
+    PRUint8 category = gfxUnicodeProperties::GetGeneralCategory(aUSV);
+    return ((category >= HB_CATEGORY_COMBINING_MARK &&
+             category <= HB_CATEGORY_NON_SPACING_MARK) ||
+            (aUSV >= 0x200c && aUSV <= 0x200d) || // ZWJ, ZWNJ
+            (aUSV >= 0xff9e && aUSV <= 0xff9f));  // katakana sound marks
+}
+
+static bool
+IsBoundarySpace(PRUnichar aChar, PRUnichar aNextChar)
+{
+    return (aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar);
+}
+
+static inline PRUint32
+HashMix(PRUint32 aHash, PRUnichar aCh)
 {
+    return (aHash >> 28) ^ (aHash << 4) ^ aCh;
+}
+
+template<typename T>
+gfxShapedWord*
+gfxFont::GetShapedWord(gfxContext *aContext,
+                       const T *aText,
+                       PRUint32 aLength,
+                       PRUint32 aHash,
+                       PRInt32 aRunScript,
+                       PRInt32 aAppUnitsPerDevUnit,
+                       PRUint32 aFlags)
+{
+    // if there's a cached entry for this word, just return it
+    CacheHashKey key(aText, aLength, aHash,
+                     aRunScript,
+                     aAppUnitsPerDevUnit,
+                     aFlags);
+
+    CacheHashEntry *entry = mWordCache.PutEntry(key);
+    gfxShapedWord *sw = entry->mShapedWord;
+    if (sw) {
+        sw->ResetAge();
+        return sw;
+    }
+
+    sw = entry->mShapedWord = gfxShapedWord::Create(aText, aLength,
+                                                    aRunScript,
+                                                    aAppUnitsPerDevUnit,
+                                                    aFlags);
+    NS_ASSERTION(sw != nsnull,
+                 "failed to create gfxShapedWord - expect missing text");
+    if (!sw) {
+        return nsnull;
+    }
+
     bool ok;
-
-#ifdef PR_LOGGING
-    PRLogModuleInfo *log = (mStyle.systemFont ?
-                            gfxPlatform::GetLog(eGfxLog_textrunui) :
-                            gfxPlatform::GetLog(eGfxLog_textrun));
-
-    if (NS_UNLIKELY(log)) {
-        nsCAutoString lang;
-        mStyle.language->ToUTF8String(lang);
-        PR_LOG(log, PR_LOG_DEBUG,\
-               ("(%s-fontmatching) font: [%s] lang: %s script: %d len: %d "
-                "TEXTRUN [%s] ENDTEXTRUN\n",
-                (mStyle.systemFont ? "textrunui" : "textrun"),
-                NS_ConvertUTF16toUTF8(GetName()).get(),
-                lang.get(), aRunScript, aRunLength,
-                NS_ConvertUTF16toUTF8(aString + aRunStart, aRunLength).get()));
-    }
-#endif
-
-    do {
-        // Because various shaping backends struggle with very long runs,
-        // we look for appropriate break locations (preferring whitespace),
-        // and shape sub-runs of no more than 32K characters at a time.
-        // See bug 606714 (CoreText), and similar Uniscribe issues.
-        // This loop always executes at least once, and "processes" up to
-        // MAX_RUN_LENGTH_FOR_SHAPING characters, updating aRunStart and
-        // aRunLength accordingly. It terminates when the entire run has
-        // been processed, or when shaping fails.
-
-        PRUint32 thisRunLength;
-        ok = false;
-
-        if (aRunLength <= MAX_SHAPING_LENGTH) {
-            thisRunLength = aRunLength;
-        } else {
-            // We're splitting this font run because it's very long
-            PRUint32 offset = aRunStart + MAX_SHAPING_LENGTH;
-            PRUint32 clusterStart = 0;
-            while (offset > aRunStart + MAX_SHAPING_LENGTH - BACKTRACK_LIMIT) {
-                if (aTextRun->IsClusterStart(offset)) {
-                    if (!clusterStart) {
-                        clusterStart = offset;
-                    }
-                    if (aString[offset] == ' ' || aString[offset - 1] == ' ') {
-                        break;
-                    }
-                }
-                --offset;
-            }
-            
-            if (offset > MAX_SHAPING_LENGTH - BACKTRACK_LIMIT) {
-                // we found a space, so break the run there
-                thisRunLength = offset - aRunStart;
-            } else if (clusterStart != 0) {
-                // didn't find a space, but we found a cluster start
-                thisRunLength = clusterStart - aRunStart;
-            } else {
-                // otherwise we'll simply break at MAX_SHAPING_LENGTH chars,
-                // which may interfere with shaping behavior (but in practice
-                // only pathological cases will lack ANY whitespace or cluster
-                // boundaries, so we don't really care; it won't affect any
-                // "real" text)
-                thisRunLength = MAX_SHAPING_LENGTH;
-            }
+    if (sizeof(T) == sizeof(PRUnichar)) {
+        ok = ShapeWord(aContext, sw, (const PRUnichar*)aText);
+    } else {
+        nsAutoString utf16;
+        AppendASCIItoUTF16(nsDependentCSubstring((const char*)aText, aLength),
+                           utf16);
+        ok = ShapeWord(aContext, sw, utf16.BeginReading());
+    }
+    NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
+
+    for (PRUint32 i = 0; i < aLength; ++i) {
+        if (aText[i] == ' ') {
+            sw->SetIsSpace(i);
+        } else if (i > 0 &&
+                   NS_IS_HIGH_SURROGATE(aText[i - 1]) &&
+                   NS_IS_LOW_SURROGATE(aText[i])) {
+            sw->SetIsLowSurrogate(i);
         }
-
-        ok = InitTextRun(aContext, aTextRun, aString,
-                         aRunStart, thisRunLength, aRunScript);
-
-        aRunStart += thisRunLength;
-        aRunLength -= thisRunLength;
-    } while (ok && aRunLength > 0);
-
-    NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text");
-    return ok;
+    }
+
+    return sw;
 }
 
 bool
-gfxFont::InitTextRun(gfxContext *aContext,
-                     gfxTextRun *aTextRun,
-                     const PRUnichar *aString,
-                     PRUint32 aRunStart,
-                     PRUint32 aRunLength,
-                     PRInt32 aRunScript,
-                     bool aPreferPlatformShaping)
+gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
+{
+    const gfxShapedWord *sw = mShapedWord;
+    if (!sw) {
+        return false;
+    }
+    if (sw->Length() != aKey->mLength ||
+        sw->Flags() != aKey->mFlags ||
+        sw->AppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
+        sw->Script() != aKey->mScript) {
+        return false;
+    }
+    if (sw->TextIs8Bit()) {
+        if (aKey->mTextIs8Bit) {
+            return (0 == memcmp(sw->Text8Bit(), aKey->mText.mSingle,
+                                aKey->mLength * sizeof(PRUint8)));
+        }
+        // The key has 16-bit text, even though all the characters are < 256,
+        // so the TEXT_IS_8BIT flag was set and the cached ShapedWord we're
+        // comparing with will have 8-bit text.
+        const PRUint8   *s1 = sw->Text8Bit();
+        const PRUnichar *s2 = aKey->mText.mDouble;
+        const PRUnichar *s2end = s2 + aKey->mLength;
+        while (s2 < s2end) {
+            if (*s1++ != *s2++) {
+                return false;
+            }
+        }
+        return true;
+    }
+    NS_ASSERTION((aKey->mFlags & gfxTextRunFactory::TEXT_IS_8BIT) == 0 &&
+                 !aKey->mTextIs8Bit, "didn't expect 8-bit text here");
+    return (0 == memcmp(sw->TextUnicode(), aKey->mText.mDouble,
+                        aKey->mLength * sizeof(PRUnichar)));
+}
+
+bool
+gfxFont::ShapeWord(gfxContext *aContext,
+                   gfxShapedWord *aShapedWord,
+                   const PRUnichar *aText,
+                   bool aPreferPlatformShaping)
 {
     bool ok = false;
 
 #ifdef MOZ_GRAPHITE
     if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
-        ok = mGraphiteShaper->InitTextRun(aContext, aTextRun, aString,
-                                          aRunStart, aRunLength,
-                                          aRunScript);
+        ok = mGraphiteShaper->ShapeWord(aContext, aShapedWord, aText);
     }
 #endif
 
     if (!ok && mHarfBuzzShaper && !aPreferPlatformShaping) {
-        if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aRunScript)) {
-            ok = mHarfBuzzShaper->InitTextRun(aContext, aTextRun, aString,
-                                              aRunStart, aRunLength,
-                                              aRunScript);
+        if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aShapedWord->Script())) {
+            ok = mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aText);
         }
     }
 
     if (!ok) {
         if (!mPlatformShaper) {
             CreatePlatformShaper();
             NS_ASSERTION(mPlatformShaper, "no platform shaper available!");
         }
         if (mPlatformShaper) {
-            ok = mPlatformShaper->InitTextRun(aContext, aTextRun, aString,
-                                              aRunStart, aRunLength,
-                                              aRunScript);
+            ok = mPlatformShaper->ShapeWord(aContext, aShapedWord, aText);
         }
     }
 
+    if (ok && IsSyntheticBold()) {
+        float synBoldOffset =
+                GetSyntheticBoldOffset() * CalcXScale(aContext);
+        aShapedWord->AdjustAdvancesForSyntheticBold(synBoldOffset);
+    }
+
     return ok;
 }
 
+template<typename T>
+bool
+gfxFont::SplitAndInitTextRun(gfxContext *aContext,
+                             gfxTextRun *aTextRun,
+                             const T *aString,
+                             PRUint32 aRunStart,
+                             PRUint32 aRunLength,
+                             PRInt32 aRunScript)
+{
+    if (aRunLength == 0) {
+        return true;
+    }
+
+    InitWordCache();
+
+    // the only flags we care about for ShapedWord construction/caching
+    PRUint32 flags = aTextRun->GetFlags() &
+        (gfxTextRunFactory::TEXT_IS_RTL |
+         gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES);
+    if (sizeof(T) == sizeof(PRUint8)) {
+        flags |= gfxTextRunFactory::TEXT_IS_8BIT;
+    }
+
+    const T *text = aString + aRunStart;
+    PRUint32 wordStart = 0;
+    PRUint32 hash = 0;
+    bool wordIs8Bit = true;
+    PRInt32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
+
+    T nextCh = text[0];
+    for (PRUint32 i = 0; i <= aRunLength; ++i) {
+        T ch = nextCh;
+        nextCh = (i < aRunLength - 1) ? text[i + 1] : '\n';
+        bool boundary = IsBoundarySpace(ch, nextCh);
+        bool invalid = !boundary && gfxFontGroup::IsInvalidChar(ch);
+        PRUint32 length = i - wordStart;
+
+        // break into separate ShapedWords when we hit an invalid char,
+        // or a boundary space (always handled individually),
+        // or the first non-space after a space
+        bool breakHere = boundary || invalid;
+
+        if (!breakHere) {
+            // if we're approaching the max ShapedWord length, break anyway...
+            if (sizeof(T) == sizeof(PRUint8)) {
+                // in 8-bit text, no clusters or surrogates to worry about
+                if (length >= gfxShapedWord::kMaxLength) {
+                    breakHere = true;
+                }
+            } else {
+                // try to avoid breaking before combining mark or low surrogate
+                if (length >= gfxShapedWord::kMaxLength - 15) {
+                    if (!NS_IS_LOW_SURROGATE(ch)) {
+                        if (!IsClusterExtender(ch)) {
+                            breakHere = true;
+                        }
+                    }
+                    if (!breakHere && length >= gfxShapedWord::kMaxLength - 3) {
+                        if (!NS_IS_LOW_SURROGATE(ch)) {
+                            breakHere = true;
+                        }
+                    }
+                    if (!breakHere && length >= gfxShapedWord::kMaxLength) {
+                        breakHere = true;
+                    }
+                }
+            }
+        }
+
+        if (!breakHere) {
+            if (ch >= 0x100) {
+                wordIs8Bit = false;
+            }
+            // include this character in the hash, and move on to next
+            hash = HashMix(hash, ch);
+            continue;
+        }
+
+        // We've decided to break here (i.e. we're at the end of a "word",
+        // or the word is becoming excessively long): shape the word and
+        // add it to the textrun
+        if (length > 0) {
+            PRUint32 wordFlags = flags;
+            // in the 8-bit version of this method, TEXT_IS_8BIT was
+            // already set as part of |flags|, so no need for a per-word
+            // adjustment here
+            if (sizeof(T) == sizeof(PRUnichar)) {
+                if (wordIs8Bit) {
+                    wordFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
+                }
+            }
+            gfxShapedWord *sw = GetShapedWord(aContext,
+                                              text + wordStart, length,
+                                              hash, aRunScript,
+                                              appUnitsPerDevUnit,
+                                              wordFlags);
+            if (sw) {
+                aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart);
+            } else {
+                return false; // failed, presumably out of memory?
+            }
+        }
+
+        if (boundary) {
+            // word was terminated by a space: add that to the textrun
+            if (!aTextRun->SetSpaceGlyphIfSimple(this, aContext,
+                                                 aRunStart + i, ch))
+            {
+                static const PRUint8 space = ' ';
+                gfxShapedWord *sw =
+                    GetShapedWord(aContext,
+                                  &space, 1,
+                                  HashMix(0, ' '), aRunScript,
+                                  appUnitsPerDevUnit,
+                                  flags | gfxTextRunFactory::TEXT_IS_8BIT);
+                if (sw) {
+                    aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
+                } else {
+                    return false;
+                }
+            }
+            hash = 0;
+            wordStart = i + 1;
+            wordIs8Bit = true;
+            continue;
+        }
+
+        if (i == aRunLength) {
+            break;
+        }
+
+        if (invalid) {
+            // word was terminated by an invalid char: skip it,
+            // but record where TAB or NEWLINE occur
+            if (ch == '\t') {
+                aTextRun->SetIsTab(aRunStart + i);
+            } else if (ch == '\n') {
+                aTextRun->SetIsNewline(aRunStart + i);
+            }
+            hash = 0;
+            wordStart = i + 1;
+            wordIs8Bit = true;
+            continue;
+        }
+
+        // word was forcibly broken, so current char will begin next word
+        hash = HashMix(0, ch);
+        wordStart = i;
+        wordIs8Bit = (ch < 0x100);
+    }
+
+    return true;
+}
+
 gfxGlyphExtents *
 gfxFont::GetOrCreateGlyphExtents(PRUint32 aAppUnitsPerDevUnit) {
     PRUint32 i;
     for (i = 0; i < mGlyphExtentsArray.Length(); ++i) {
         if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
             return mGlyphExtentsArray[i];
     }
     gfxGlyphExtents *glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit);
@@ -2425,28 +2667,35 @@ gfxFontGroup::~gfxFontGroup() {
 
 gfxFontGroup *
 gfxFontGroup::Copy(const gfxFontStyle *aStyle)
 {
     return new gfxFontGroup(mFamilies, aStyle, mUserFontSet);
 }
 
 bool 
-gfxFontGroup::IsInvalidChar(PRUnichar ch) {
-    if (ch >= 32) {
-        return ch == 0x0085/*NEL*/ ||
-            ((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
-             (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/ ||
-              IS_BIDI_CONTROL_CHAR(ch)));
-    }
-    // We could just blacklist all control characters, but it seems better
-    // to only blacklist the ones we know cause problems for native font
-    // engines.
-    return ch == 0x0B || ch == '\t' || ch == '\r' || ch == '\n' || ch == '\f' ||
-        (ch >= 0x1c && ch <= 0x1f);
+gfxFontGroup::IsInvalidChar(PRUint8 ch)
+{
+    return ((ch & 0x7f) < 0x20);
+}
+
+bool 
+gfxFontGroup::IsInvalidChar(PRUnichar ch)
+{
+    // All printable 7-bit ASCII values are OK
+    if (ch >= ' ' && ch < 0x80) {
+        return false;
+    }
+    // No point in sending non-printing control chars through font shaping
+    if (ch <= 0x9f) {
+        return true;
+    }
+    return ((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
+         (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/ ||
+          IS_BIDI_CONTROL_CHAR(ch)));
 }
 
 bool
 gfxFontGroup::ForEachFont(FontCreationCallback fc,
                           void *closure)
 {
     return ForEachFontInternal(mFamilies, mStyle.language,
                                true, true, true, fc, closure);
@@ -2648,214 +2897,308 @@ gfxFontGroup::MakeEmptyTextRun(const Par
 }
 
 gfxTextRun *
 gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, PRUint32 aFlags)
 {
     aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
     static const PRUint8 space = ' ';
 
-    nsAutoPtr<gfxTextRun> textRun;
-    textRun = gfxTextRun::Create(aParams, &space, 1, this, aFlags);
-    if (!textRun)
+    gfxTextRun *textRun = gfxTextRun::Create(aParams, &space, 1, this, aFlags);
+    if (!textRun) {
         return nsnull;
+    }
 
     gfxFont *font = GetFontAt(0);
     if (NS_UNLIKELY(GetStyle()->size == 0)) {
         // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
         // them, and always create at least size 1 fonts, i.e. they still
         // render something for size 0 fonts.
         textRun->AddGlyphRun(font, gfxTextRange::kFontGroup, 0, false);
     }
     else {
         textRun->SetSpaceGlyph(font, aParams->mContext, 0);
     }
+
     // Note that the gfxGlyphExtents glyph bounds storage for the font will
     // always contain an entry for the font's space glyph, so we don't have
     // to call FetchGlyphExtents here.
-    return textRun.forget();
-}
-
-#define UNICODE_LRO 0x202d
-#define UNICODE_RLO 0x202e
-#define UNICODE_PDF 0x202c
-
-inline void
-AppendDirectionalIndicatorStart(PRUint32 aFlags, nsAString& aString)
-{
-    static const PRUnichar overrides[2] = { UNICODE_LRO, UNICODE_RLO };
-    aString.Append(overrides[(aFlags & gfxTextRunFactory::TEXT_IS_RTL) != 0]);    
-    aString.Append(' ');
+    return textRun;
 }
 
-inline void
-AppendDirectionalIndicatorEnd(bool aNeedDirection, nsAString& aString)
+gfxTextRun *
+gfxFontGroup::MakeBlankTextRun(const void* aText, PRUint32 aLength,
+                               const Parameters *aParams, PRUint32 aFlags)
 {
-    // append a space (always, for consistent treatment of last char,
-    // and a direction control if required (we skip this for 8-bit text,
-    // which is known to be unidirectional LTR, unless the direction was
-    // forced RTL via overrides)
-    aString.Append(' ');
-    if (!aNeedDirection)
-        return;
-
-    aString.Append('.');
-    aString.Append(UNICODE_PDF);
+    gfxTextRun *textRun =
+        gfxTextRun::Create(aParams, aText, aLength, this, aFlags);
+    if (!textRun) {
+        return nsnull;
+    }
+
+    textRun->AddGlyphRun(GetFontAt(0), gfxTextRange::kFontGroup, 0, false);
+    return textRun;
 }
 
 gfxTextRun *
 gfxFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
                           const Parameters *aParams, PRUint32 aFlags)
 {
-    NS_ASSERTION(aLength > 0, "should use MakeEmptyTextRun for zero-length text");
-    NS_ASSERTION(aFlags & TEXT_IS_8BIT, "should be marked 8bit");
-    gfxTextRun *textRun = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
-    if (!textRun)
+    if (aLength == 0) {
+        return MakeEmptyTextRun(aParams, aFlags);
+    }
+    if (aLength == 1 && aString[0] == ' ') {
+        return MakeSpaceTextRun(aParams, aFlags);
+    }
+
+    aFlags |= TEXT_IS_8BIT;
+
+    if (GetStyle()->size == 0) {
+        // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
+        // them, and always create at least size 1 fonts, i.e. they still
+        // render something for size 0 fonts.
+        return MakeBlankTextRun(aString, aLength, aParams, aFlags);
+    }
+
+    gfxTextRun *textRun = gfxTextRun::Create(aParams, aString, aLength,
+                                             this, aFlags);
+    if (!textRun) {
         return nsnull;
-
-    nsDependentCSubstring cString(reinterpret_cast<const char*>(aString),
-                                  reinterpret_cast<const char*>(aString) + aLength);
-
-    nsAutoString utf16;
-    AppendASCIItoUTF16(cString, utf16);
-
-    InitTextRun(aParams->mContext, textRun, utf16.get(), utf16.Length());
+    }
+
+    InitTextRun(aParams->mContext, textRun, aString, aLength);
 
     textRun->FetchGlyphExtents(aParams->mContext);
 
     return textRun;
 }
 
 gfxTextRun *
 gfxFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
                           const Parameters *aParams, PRUint32 aFlags)
 {
-    NS_ASSERTION(aLength > 0, "should use MakeEmptyTextRun for zero-length text");
-    gfxTextRun *textRun = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
-    if (!textRun)
+    if (aLength == 0) {
+        return MakeEmptyTextRun(aParams, aFlags);
+    }
+    if (aLength == 1 && aString[0] == ' ') {
+        return MakeSpaceTextRun(aParams, aFlags);
+    }
+    if (GetStyle()->size == 0) {
+        return MakeBlankTextRun(aString, aLength, aParams, aFlags);
+    }
+
+    gfxTextRun *textRun = gfxTextRun::Create(aParams, aString, aLength,
+                                             this, aFlags);
+    if (!textRun) {
         return nsnull;
-
-    gfxPlatform::GetPlatform()->SetupClusterBoundaries(textRun, aString);
+    }
 
     InitTextRun(aParams->mContext, textRun, aString, aLength);
 
     textRun->FetchGlyphExtents(aParams->mContext);
 
     return textRun;
 }
 
+template<typename T>
 void
 gfxFontGroup::InitTextRun(gfxContext *aContext,
                           gfxTextRun *aTextRun,
-                          const PRUnichar *aString,
+                          const T *aString,
                           PRUint32 aLength)
 {
-    // split into script runs so that script can potentially influence
-    // the font matching process below
-    gfxScriptItemizer scriptRuns(aString, aLength);
-
-#ifdef PR_LOGGING
-    PRLogModuleInfo *log = (mStyle.systemFont ?
-                            gfxPlatform::GetLog(eGfxLog_textrunui) :
-                            gfxPlatform::GetLog(eGfxLog_textrun));
-#endif
-
-    PRUint32 runStart = 0, runLimit = aLength;
-    PRInt32 runScript = HB_SCRIPT_LATIN;
-    while (scriptRuns.Next(runStart, runLimit, runScript)) {
+    // we need to do numeral processing even on 8-bit text,
+    // in case we're converting Western to Hindi/Arabic digits
+    PRInt32 numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
+    nsAutoArrayPtr<PRUnichar> transformedString;
+    if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
+        // scan the string for numerals that may need to be transformed;
+        // if we find any, we'll make a local copy here and use that for
+        // font matching and glyph generation/shaping
+        bool prevIsArabic =
+            (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR) != 0;
+        for (PRUint32 i = 0; i < aLength; ++i) {
+            PRUnichar origCh = aString[i];
+            PRUnichar newCh = HandleNumberInChar(origCh, prevIsArabic, numOption);
+            if (newCh != origCh) {
+                if (!transformedString) {
+                    transformedString = new PRUnichar[aLength];
+                    if (sizeof(T) == sizeof(PRUnichar)) {
+                        memcpy(transformedString.get(), aString, i * sizeof(PRUnichar));
+                    } else {
+                        for (PRUint32 j = 0; j < i; ++j) {
+                            transformedString[j] = aString[j];
+                        }
+                    }
+                }
+            }
+            if (transformedString) {
+                transformedString[i] = newCh;
+            }
+            prevIsArabic = IS_ARABIC_CHAR(newCh);
+        }
+    }
+
+    if (sizeof(T) == sizeof(PRUint8) && !transformedString) {
+        // the text is still purely 8-bit; bypass the script-run itemizer
+        // and treat it as a single Latin run
+        InitScriptRun(aContext, aTextRun, aString,
+                      0, aLength, HB_SCRIPT_LATIN);
+    } else {
+        const PRUnichar *textPtr;
+        if (transformedString) {
+            textPtr = transformedString.get();
+        } else {
+            // typecast to avoid compilation error for the 8-bit version,
+            // even though this is dead code in that case
+            textPtr = reinterpret_cast<const PRUnichar*>(aString);
+        }
+
+        // split into script runs so that script can potentially influence
+        // the font matching process below
+        gfxScriptItemizer scriptRuns(textPtr, aLength);
 
 #ifdef PR_LOGGING
-        if (NS_UNLIKELY(log)) {
-            nsCAutoString lang;
-            mStyle.language->ToUTF8String(lang);
-            PRUint32 runLen = runLimit - runStart;
-            PR_LOG(log, PR_LOG_DEBUG,\
-                   ("(%s) fontgroup: [%s] lang: %s script: %d len %d "
-                    "weight: %d width: %d style: %s "
-                    "TEXTRUN [%s] ENDTEXTRUN\n",
-                    (mStyle.systemFont ? "textrunui" : "textrun"),
-                    NS_ConvertUTF16toUTF8(mFamilies).get(),
-                    lang.get(), runScript, runLen,
-                    PRUint32(mStyle.weight), PRUint32(mStyle.stretch),
-                    (mStyle.style & FONT_STYLE_ITALIC ? "italic" :
-                    (mStyle.style & FONT_STYLE_OBLIQUE ? "oblique" :
-                                                            "normal")),
-                    NS_ConvertUTF16toUTF8(aString + runStart, runLen).get()));
+        PRLogModuleInfo *log = (mStyle.systemFont ?
+                                gfxPlatform::GetLog(eGfxLog_textrunui) :
+                                gfxPlatform::GetLog(eGfxLog_textrun));
+#endif
+
+        PRUint32 runStart = 0, runLimit = aLength;
+        PRInt32 runScript = HB_SCRIPT_LATIN;
+        while (scriptRuns.Next(runStart, runLimit, runScript)) {
+
+#ifdef PR_LOGGING
+            if (NS_UNLIKELY(log)) {
+                nsCAutoString lang;
+                mStyle.language->ToUTF8String(lang);
+                PRUint32 runLen = runLimit - runStart;
+                PR_LOG(log, PR_LOG_DEBUG,\
+                       ("(%s) fontgroup: [%s] lang: %s script: %d len %d "
+                        "weight: %d width: %d style: %s "
+                        "TEXTRUN [%s] ENDTEXTRUN\n",
+                        (mStyle.systemFont ? "textrunui" : "textrun"),
+                        NS_ConvertUTF16toUTF8(mFamilies).get(),
+                        lang.get(), runScript, runLen,
+                        PRUint32(mStyle.weight), PRUint32(mStyle.stretch),
+                        (mStyle.style & FONT_STYLE_ITALIC ? "italic" :
+                        (mStyle.style & FONT_STYLE_OBLIQUE ? "oblique" :
+                                                                "normal")),
+                        NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
+            }
+#endif
+
+            InitScriptRun(aContext, aTextRun, textPtr,
+                          runStart, runLimit, runScript);
         }
-#endif
-
-        InitScriptRun(aContext, aTextRun, aString, aLength,
-                      runStart, runLimit, runScript);
+    }
+
+    if (sizeof(T) == sizeof(PRUnichar) && aLength > 0) {
+        gfxTextRun::CompressedGlyph *glyph = aTextRun->GetCharacterGlyphs();
+        if (!glyph->IsSimpleGlyph()) {
+            glyph->SetClusterStart(true);
+        }
     }
 
     // It's possible for CoreText to omit glyph runs if it decides they contain
     // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we
     // need to eliminate them from the glyph run array to avoid drawing "partial
     // ligatures" with the wrong font.
     // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because
     // it will iterate back over all glyphruns in the textrun, which leads to
     // pathologically-bad perf in the case where a textrun contains many script
     // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs
     // every time a new script subrun is processed.
     aTextRun->SanitizeGlyphRuns();
 
     aTextRun->SortGlyphRuns();
 }
 
+template<typename T>
 void
 gfxFontGroup::InitScriptRun(gfxContext *aContext,
                             gfxTextRun *aTextRun,
-                            const PRUnichar *aString,
-                            PRUint32 aTotalLength,
+                            const T *aString,
                             PRUint32 aScriptRunStart,
                             PRUint32 aScriptRunEnd,
                             PRInt32 aRunScript)
 {
-    gfxFont *mainFont = mFonts[0].get();
+    gfxFont *mainFont = GetFontAt(0);
 
     PRUint32 runStart = aScriptRunStart;
     nsAutoTArray<gfxTextRange,3> fontRanges;
-    ComputeRanges(fontRanges, aString,
-                  aScriptRunStart, aScriptRunEnd, aRunScript);
+    ComputeRanges(fontRanges, aString + aScriptRunStart,
+                  aScriptRunEnd - aScriptRunStart, aRunScript);
     PRUint32 numRanges = fontRanges.Length();
 
     for (PRUint32 r = 0; r < numRanges; r++) {
         const gfxTextRange& range = fontRanges[r];
         PRUint32 matchedLength = range.Length();
         gfxFont *matchedFont = (range.font ? range.font.get() : nsnull);
 
         // create the glyph run for this range
         if (matchedFont) {
             aTextRun->AddGlyphRun(matchedFont, range.matchType,
                                   runStart, (matchedLength > 0));
-        } else {
-            aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
-                                  runStart, (matchedLength > 0));
-        }
-        if (matchedFont) {
             // do glyph layout and record the resulting positioned glyphs
             if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun, aString,
                                                   runStart, matchedLength,
                                                   aRunScript)) {
                 // glyph layout failed! treat as missing glyphs
                 matchedFont = nsnull;
             }
+        } else {
+            aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
+                                  runStart, (matchedLength > 0));
         }
+
         if (!matchedFont) {
-            for (PRUint32 index = runStart; index < runStart + matchedLength; index++) {
-                // Record the char code so we can draw a box with the Unicode value
-                PRUint32 ch = aString[index];
-                if (NS_IS_HIGH_SURROGATE(ch) &&
-                    index + 1 < aScriptRunEnd &&
-                    NS_IS_LOW_SURROGATE(aString[index+1])) {
-                    aTextRun->SetMissingGlyph(index,
-                                              SURROGATE_TO_UCS4(aString[index],
-                                                                aString[index+1]));
-                    index++;
-                } else {
+            // for PRUnichar text, we need to set cluster boundaries so that
+            // surrogate pairs, combining characters, etc behave properly,
+            // even if we don't have glyphs for them
+            if (sizeof(T) == sizeof(PRUnichar)) {
+                gfxShapedWord::SetupClusterBoundaries(aTextRun->GetCharacterGlyphs() + runStart,
+                                                      reinterpret_cast<const PRUnichar*>(aString) + runStart,
+                                                      matchedLength);
+            }
+
+            // various "missing" characters may need special handling,
+            // so we check for them here
+            PRUint32 runLimit = runStart + matchedLength;
+            for (PRUint32 index = runStart; index < runLimit; index++) {
+                T ch = aString[index];
+
+                // tab and newline are not to be displayed as hexboxes,
+                // but do need to be recorded in the textrun
+                if (ch == '\n') {
+                    aTextRun->SetIsNewline(index);
+                    continue;
+                }
+                if (ch == '\t') {
+                    aTextRun->SetIsTab(index);
+                    continue;
+                }
+
+                // for 16-bit textruns only, check for surrogate pairs and
+                // special Unicode spaces; omit these checks in 8-bit runs
+                if (sizeof(T) == sizeof(PRUnichar)) {
+                    if (NS_IS_HIGH_SURROGATE(ch) &&
+                        index + 1 < aScriptRunEnd &&
+                        NS_IS_LOW_SURROGATE(aString[index + 1]))
+                    {
+                        aTextRun->SetMissingGlyph(index,
+                                                  SURROGATE_TO_UCS4(ch,
+                                                                    aString[index + 1]));
+                        index++;
+                        aTextRun->SetIsLowSurrogate(index);
+                        continue;
+                    }
+
+                    // check if this is a known Unicode whitespace character that
+                    // we can render using the space glyph with a custom width
                     gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
                     if (wid >= 0.0) {
                         nscoord advance =
                             aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
                         gfxTextRun::CompressedGlyph g;
                         if (gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance)) {
                             aTextRun->SetSimpleGlyph(index,
                                                      g.SetSimpleGlyph(advance,
@@ -2864,28 +3207,34 @@ gfxFontGroup::InitScriptRun(gfxContext *
                             gfxTextRun::DetailedGlyph detailedGlyph;
                             detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
                             detailedGlyph.mAdvance = advance;
                             detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
                             g.SetComplex(true, true, 1);
                             aTextRun->SetGlyphs(index,
                                                 g, &detailedGlyph);
                         }
-                    } else {
-                        aTextRun->SetMissingGlyph(index, ch);
+                        continue;
                     }
                 }
+
+                if (IsInvalidChar(ch)) {
+                    // invalid chars are left as zero-width/invisible
+                    continue;
+                }
+
+                // record char code so we can draw a box with the Unicode value
+                aTextRun->SetMissingGlyph(index, ch);
             }
         }
 
         runStart += matchedLength;
     }
 }
 
-
 already_AddRefed<gfxFont>
 gfxFontGroup::FindFontForChar(PRUint32 aCh, PRUint32 aPrevCh,
                               PRInt32 aRunScript, gfxFont *aPrevMatchedFont,
                               PRUint8 *aMatchType)
 {
     nsRefPtr<gfxFont>    selectedFont;
 
     // if this character is a join-control or the previous is a join-causer,
@@ -2950,44 +3299,49 @@ gfxFontGroup::FindFontForChar(PRUint32 a
         *aMatchType = gfxTextRange::kSystemFallback;
         selectedFont = WhichSystemFontSupportsChar(aCh);
         return selectedFont.forget();
     }
 
     return nsnull;
 }
 
-
+template<typename T>
 void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
-                                 const PRUnichar *aString,
-                                 PRUint32 begin, PRUint32 end,
+                                 const T *aString, PRUint32 aLength,
                                  PRInt32 aRunScript)
 {
-    const PRUnichar *str = aString + begin;
-    PRUint32 len = end - begin;
-
     aRanges.Clear();
 
-    if (len == 0) {
+    if (aLength == 0) {
         return;
     }
 
     PRUint32 prevCh = 0;
     gfxFont *prevFont = nsnull;
     PRUint8 matchType = 0;
 
-    for (PRUint32 i = 0; i < len; i++) {
+    for (PRUint32 i = 0; i < aLength; i++) {
 
         const PRUint32 origI = i; // save off in case we increase for surrogate
 
         // set up current ch
-        PRUint32 ch = str[i];
-        if ((i+1 < len) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(str[i+1])) {
-            i++;
-            ch = SURROGATE_TO_UCS4(ch, str[i]);
+        PRUint32 ch = aString[i];
+
+        // in 16-bit case only, check for surrogate pair
+        if (sizeof(T) == sizeof(PRUnichar)) {
+            if ((i + 1 < aLength) && NS_IS_HIGH_SURROGATE(ch) &&
+                                 NS_IS_LOW_SURROGATE(aString[i + 1])) {
+                i++;
+                ch = SURROGATE_TO_UCS4(ch, aString[i]);
+            }
+        }
+
+        if (ch == 0xa0) {
+            ch = ' ';
         }
 
         // find the font for this char
         nsRefPtr<gfxFont> font =
             FindFontForChar(ch, prevCh, aRunScript, prevFont, &matchType);
 
         prevCh = ch;
 
@@ -2996,28 +3350,31 @@ void gfxFontGroup::ComputeRanges(nsTArra
             aRanges.AppendElement(gfxTextRange(0, 1, font, matchType));
             prevFont = font;
         } else {
             // if font has changed, make a new range
             gfxTextRange& prevRange = aRanges[aRanges.Length() - 1];
             if (prevRange.font != font || prevRange.matchType != matchType) {
                 // close out the previous range
                 prevRange.end = origI;
-                aRanges.AppendElement(gfxTextRange(origI, i+1, font, matchType));
+                aRanges.AppendElement(gfxTextRange(origI, i + 1,
+                                                   font, matchType));
 
                 // update prevFont for the next match, *unless* we switched
                 // fonts on a ZWJ, in which case propagating the changed font
                 // is probably not a good idea (see bug 619511)
-                if (!gfxFontUtils::IsJoinCauser(ch)) {
+                if (sizeof(T) == sizeof(PRUint8) ||
+                    !gfxFontUtils::IsJoinCauser(ch))
+                {
                     prevFont = font;
                 }
             }
         }
     }
-    aRanges[aRanges.Length()-1].end = len;
+    aRanges[aRanges.Length() - 1].end = aLength;
 }
 
 gfxUserFontSet* 
 gfxFontGroup::GetUserFontSet()
 {
     return mUserFontSet;
 }
 
@@ -3301,16 +3658,240 @@ gfxFontStyle::ComputeWeight() const
     if (baseWeight < 0)
         baseWeight = 0;
     if (baseWeight > 9)
         baseWeight = 9;
 
     return baseWeight;
 }
 
+// This is not a member function of gfxShapedWord because it is also used
+// by gfxFontGroup on missing-glyph runs, where we don't actually "shape"
+// anything but still need to set cluster info.
+/*static*/ void
+gfxShapedWord::SetupClusterBoundaries(CompressedGlyph *aGlyphs,
+                                      const PRUnichar *aString, PRUint32 aLength)
+{
+    gfxTextRun::CompressedGlyph extendCluster;
+    extendCluster.SetComplex(false, true, 0);
+
+    gfxUnicodeProperties::HSType hangulState = gfxUnicodeProperties::HST_NONE;
+
+    for (PRUint32 i = 0; i < aLength; ++i) {
+        bool surrogatePair = false;
+        PRUint32 ch = aString[i];
+        if (NS_IS_HIGH_SURROGATE(ch) &&
+            i < aLength - 1 && NS_IS_LOW_SURROGATE(aString[i+1]))
+        {
+            ch = SURROGATE_TO_UCS4(ch, aString[i+1]);
+            surrogatePair = true;
+        }
+
+        PRUint8 category = gfxUnicodeProperties::GetGeneralCategory(ch);
+        gfxUnicodeProperties::HSType hangulType = gfxUnicodeProperties::HST_NONE;
+
+        // combining marks extend the cluster
+        if (IsClusterExtender(ch)) {
+            aGlyphs[i] = extendCluster;
+        } else if (category == HB_CATEGORY_OTHER_LETTER) {
+            // handle special cases in Letter_Other category
+#if 0
+            // Currently disabled. This would follow the UAX#29 specification
+            // for extended grapheme clusters, but this is not favored by
+            // Thai users, at least for editing behavior.
+            // See discussion of equivalent Pango issue in bug 474068 and
+            // upstream at https://bugzilla.gnome.org/show_bug.cgi?id=576156.
+
+            if ((ch & ~0xff) == 0x0e00) {
+                // specific Thai & Lao (U+0Exx) chars that extend the cluster
+                if ( ch == 0x0e30 ||
+                    (ch >= 0x0e32 && ch <= 0x0e33) ||
+                     ch == 0x0e45 ||
+                     ch == 0x0eb0 ||
+                    (ch >= 0x0eb2 && ch <= 0x0eb3))
+                {
+                    if (i > 0) {
+                        aTextRun->SetGlyphs(i, extendCluster, nsnull);
+                    }
+                }
+                else if ((ch >= 0x0e40 && ch <= 0x0e44) ||
+                         (ch >= 0x0ec0 && ch <= 0x0ec4))
+                {
+                    // characters that are prepended to the following cluster
+                    if (i < length - 1) {
+                        aTextRun->SetGlyphs(i+1, extendCluster, nsnull);
+                    }
+                }
+            } else
+#endif
+            if ((ch & ~0xff) == 0x1100 ||
+                (ch >= 0xa960 && ch <= 0xa97f) ||
+                (ch >= 0xac00 && ch <= 0xd7ff))
+            {
+                // no break within Hangul syllables
+                hangulType = gfxUnicodeProperties::GetHangulSyllableType(ch);
+                switch (hangulType) {
+                case gfxUnicodeProperties::HST_L:
+                case gfxUnicodeProperties::HST_LV:
+                case gfxUnicodeProperties::HST_LVT:
+                    if (hangulState == gfxUnicodeProperties::HST_L) {
+                        aGlyphs[i] = extendCluster;
+                    }
+                    break;
+                case gfxUnicodeProperties::HST_V:
+                    if ( (hangulState != gfxUnicodeProperties::HST_NONE) &&
+                        !(hangulState & gfxUnicodeProperties::HST_T))
+                    {
+                        aGlyphs[i] = extendCluster;
+                    }
+                    break;
+                case gfxUnicodeProperties::HST_T:
+                    if (hangulState & (gfxUnicodeProperties::HST_V |
+                                       gfxUnicodeProperties::HST_T))
+                    {
+                        aGlyphs[i] = extendCluster;
+                    }
+                    break;
+                default:
+                    break;
+                }
+            }
+        }
+
+        if (surrogatePair) {
+            ++i;
+            aGlyphs[i] = extendCluster;
+        }
+
+        hangulState = hangulType;
+    }
+}
+
+gfxShapedWord::DetailedGlyph *
+gfxShapedWord::AllocateDetailedGlyphs(PRUint32 aIndex, PRUint32 aCount)
+{
+    NS_ASSERTION(aIndex < Length(), "Index out of range");
+
+    if (!mDetailedGlyphs) {
+        mDetailedGlyphs = new DetailedGlyphStore();
+    }
+
+    DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
+    if (!details) {
+        mCharacterGlyphs[aIndex].SetMissing(0);
+        return nsnull;
+    }
+
+    return details;
+}
+
+void
+gfxShapedWord::SetGlyphs(PRUint32 aIndex, CompressedGlyph aGlyph,
+                         const DetailedGlyph *aGlyphs)
+{
+    NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
+    NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
+                 "First character can't be a ligature continuation!");
+
+    PRUint32 glyphCount = aGlyph.GetGlyphCount();
+    if (glyphCount > 0) {
+        DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
+        if (!details) {
+            return;
+        }
+        memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
+    }
+    mCharacterGlyphs[aIndex] = aGlyph;
+}
+
+#include "ignorable.x-ccmap"
+DEFINE_X_CCMAP(gIgnorableCCMapExt, const);
+
+static inline bool
+IsDefaultIgnorable(PRUint32 aChar)
+{
+    return CCMAP_HAS_CHAR_EXT(gIgnorableCCMapExt, aChar);
+}
+
+void
+gfxShapedWord::SetMissingGlyph(PRUint32 aIndex, PRUint32 aChar, gfxFont *aFont)
+{
+    DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
+    if (!details) {
+        return;
+    }
+
+    details->mGlyphID = aChar;
+    if (IsDefaultIgnorable(aChar)) {
+        // Setting advance width to zero will prevent drawing the hexbox
+        details->mAdvance = 0;
+    } else {
+        gfxFloat width = NS_MAX(aFont->GetMetrics().aveCharWidth,
+                                gfxFontMissingGlyphs::GetDesiredMinWidth(aChar));
+        details->mAdvance = PRUint32(width * mAppUnitsPerDevUnit);
+    }
+    details->mXOffset = 0;
+    details->mYOffset = 0;
+    mCharacterGlyphs[aIndex].SetMissing(1);
+}
+
+bool
+gfxShapedWord::FilterIfIgnorable(PRUint32 aIndex)
+{
+    PRUint32 ch = GetCharAt(aIndex);
+    if (IsDefaultIgnorable(ch)) {
+        DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
+        if (details) {
+            details->mGlyphID = ch;
+            details->mAdvance = 0;
+            details->mXOffset = 0;
+            details->mYOffset = 0;
+            mCharacterGlyphs[aIndex].SetMissing(1);
+            return true;
+        }
+    }
+    return false;
+}
+
+void
+gfxShapedWord::AdjustAdvancesForSyntheticBold(float aSynBoldOffset)
+{
+    PRUint32 synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
+    for (PRUint32 i = 0; i < Length(); ++i) {
+         CompressedGlyph *glyphData = &mCharacterGlyphs[i];
+         if (glyphData->IsSimpleGlyph()) {
+             // simple glyphs ==> just add the advance
+             PRInt32 advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
+             if (CompressedGlyph::IsSimpleAdvance(advance)) {
+                 glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
+             } else {
+                 // rare case, tested by making this the default
+                 PRUint32 glyphIndex = glyphData->GetSimpleGlyph();
+                 glyphData->SetComplex(true, true, 1);
+                 DetailedGlyph detail = {glyphIndex, advance, 0, 0};
+                 SetGlyphs(i, *glyphData, &detail);
+             }
+         } else {
+             // complex glyphs ==> add offset at cluster/ligature boundaries
+             PRUint32 detailedLength = glyphData->GetGlyphCount();
+             if (detailedLength) {
+                 DetailedGlyph *details = GetDetailedGlyphs(i);
+                 if (!details) {
+                     continue;
+                 }
+                 if (IsRightToLeft()) {
+                     details[0].mAdvance += synAppUnitOffset;
+                 } else {
+                     details[detailedLength - 1].mAdvance += synAppUnitOffset;
+                 }
+             }
+         }
+    }
+}
+
 bool
 gfxTextRun::GlyphRunIterator::NextRun()  {
     if (mNextIndex >= mTextRun->mGlyphRuns.Length())
         return false;
     mGlyphRun = &mTextRun->mGlyphRuns[mNextIndex];
     if (mGlyphRun->mCharacterOffset >= mEndOffset)
         return false;
 
@@ -3330,169 +3911,112 @@ AccountStorageForTextRun(gfxTextRun *aTe
     // Ignores detailed glyphs... we don't know when those have been constructed
     // Also ignores gfxSkipChars dynamic storage (which won't be anything
     // for preformatted text)
     // Also ignores GlyphRun array, again because it hasn't been constructed
     // by the time this gets called. If there's only one glyphrun that's stored
     // directly in the textrun anyway so no additional overhead.
     PRUint32 length = aTextRun->GetLength();
     PRInt32 bytes = length * sizeof(gfxTextRun::CompressedGlyph);
-    if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_PERSISTENT) {
-      bytes += length * ((aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) ? 1 : 2);
-      bytes += sizeof(gfxTextRun::CompressedGlyph) - 1;
-      bytes &= ~(sizeof(gfxTextRun::CompressedGlyph) - 1);
-    }
     bytes += sizeof(gfxTextRun);
     gTextRunStorage += bytes*aSign;
     gTextRunStorageHighWaterMark = NS_MAX(gTextRunStorageHighWaterMark, gTextRunStorage);
 }
 #endif
 
-static PRUint64
-GlyphStorageAllocCount(PRUint32 aLength, PRUint32 aFlags)
-{
-    // always need to allocate storage for the glyph data
-    PRUint64 allocCount = aLength;
-
-    // if the text is not persistent, we also need space for a copy
-    if (!(aFlags & gfxTextRunFactory::TEXT_IS_PERSISTENT)) {
-        // figure out number of extra CompressedGlyph elements we need to
-        // get sufficient space for the text
-        typedef gfxTextRun::CompressedGlyph CompressedGlyph;
-        if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
-            allocCount += (aLength + sizeof(CompressedGlyph) - 1) /
-                          sizeof(CompressedGlyph);
-        } else {
-            allocCount += (aLength * sizeof(PRUnichar) +
-                              sizeof(CompressedGlyph) - 1) /
-                          sizeof(CompressedGlyph);
-        }
-    }
-    return allocCount;
-}
-
-// Helper for textRun creation to preallocate storage for glyphs and text;
-// this function returns a pointer to the newly-allocated glyph storage,
-// AND modifies the aText parameter if TEXT_IS_PERSISTENT was not set.
-// In that case, the text is appended to the glyph storage, so a single
-// delete[] operation in the textRun destructor will free both.
+// Helper for textRun creation to preallocate storage for glyph records;
+// this function returns a pointer to the newly-allocated glyph storage.
 // Returns nsnull if allocation fails.
-gfxTextRun::CompressedGlyph *
-gfxTextRun::AllocateStorage(const void*& aText, PRUint32 aLength, PRUint32 aFlags)
+void *
+gfxTextRun::AllocateStorageForTextRun(size_t aSize, PRUint32 aLength)
 {
-    // Here, we rely on CompressedGlyph being the largest unit we care about for
-    // allocation/alignment of either glyph data or text, so we allocate an array
-    // of CompressedGlyphs, then take the last chunk of that and cast a pointer to
-    // PRUint8* or PRUnichar* for text storage.
-
-    PRUint64 allocCount = GlyphStorageAllocCount(aLength, aFlags);
-
-    // allocate the storage we need, returning nsnull on failure rather than
-    // throwing an exception (because web content can create huge runs)
-    CompressedGlyph *storage = new (std::nothrow) CompressedGlyph[allocCount];
+    // Allocate the storage we need, returning nsnull on failure rather than
+    // throwing an exception (because web content can create huge runs).
+    void *storage = moz_malloc(aSize + aLength * sizeof(CompressedGlyph));
     if (!storage) {
-        NS_WARNING("failed to allocate glyph/text storage for text run!");
+        NS_WARNING("failed to allocate storage for text run!");
         return nsnull;
     }
 
-    // copy the text if we need to keep a copy in the textrun
-    if (!(aFlags & gfxTextRunFactory::TEXT_IS_PERSISTENT)) {
-        if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
-            PRUint8 *newText = reinterpret_cast<PRUint8*>(storage + aLength);
-            memcpy(newText, aText, aLength);
-            aText = newText;
-        } else {
-            PRUnichar *newText = reinterpret_cast<PRUnichar*>(storage + aLength);
-            memcpy(newText, aText, aLength*sizeof(PRUnichar));
-            aText = newText;
-        }
-    }
+    // Initialize the glyph storage (beyond aSize) to zero
+    memset(reinterpret_cast<char*>(storage) + aSize, 0,
+           aLength * sizeof(CompressedGlyph));
 
     return storage;
 }
 
 gfxTextRun *
 gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams, const void *aText,
                    PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags)
 {
-    CompressedGlyph *glyphStorage = AllocateStorage(aText, aLength, aFlags);
-    if (!glyphStorage) {
+    void *storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
+    if (!storage) {
         return nsnull;
     }
 
-    return new gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags, glyphStorage);
+    return new (storage) gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags);
 }
 
 gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams, const void *aText,
-                       PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags,
-                       CompressedGlyph *aGlyphStorage)
-  : mCharacterGlyphs(aGlyphStorage),
-    mUserData(aParams->mUserData),
+                       PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags)
+  : mUserData(aParams->mUserData),
     mFontGroup(aFontGroup),
     mAppUnitsPerDevUnit(aParams->mAppUnitsPerDevUnit),
-    mFlags(aFlags), mCharacterCount(aLength), mHashCode(0)
+    mFlags(aFlags), mCharacterCount(aLength)
 {
     NS_ASSERTION(mAppUnitsPerDevUnit != 0, "Invalid app unit scale");
     MOZ_COUNT_CTOR(gfxTextRun);
     NS_ADDREF(mFontGroup);
+
+    mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
+
     if (aParams->mSkipChars) {
         mSkipChars.TakeFrom(aParams->mSkipChars);
     }
 
-    if (mFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
-        mText.mSingle = static_cast<const PRUint8 *>(aText);
-    } else {
-        mText.mDouble = static_cast<const PRUnichar *>(aText);
-    }
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     AccountStorageForTextRun(this, 1);
 #endif
 
-    mUserFontSetGeneration = mFontGroup->GetGeneration();
     mSkipDrawing = mFontGroup->ShouldSkipDrawing();
 }
 
 gfxTextRun::~gfxTextRun()
 {
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     AccountStorageForTextRun(this, -1);
 #endif
 #ifdef DEBUG
     // Make it easy to detect a dead text run
     mFlags = 0xFFFFFFFF;
 #endif
 
-    // this will also delete the text, if it is owned by the run,
-    // because we merge the storage allocations
-    delete [] mCharacterGlyphs;
-
     NS_RELEASE(mFontGroup);
     MOZ_COUNT_DTOR(gfxTextRun);
 }
 
 bool
 gfxTextRun::SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
                                    PRUint8 *aBreakBefore,
                                    gfxContext *aRefContext)
 {
     NS_ASSERTION(aStart + aLength <= mCharacterCount, "Overflow");
 
-    if (!mCharacterGlyphs)
-        return true;
     PRUint32 changed = 0;
     PRUint32 i;
+    CompressedGlyph *charGlyphs = mCharacterGlyphs + aStart;
     for (i = 0; i < aLength; ++i) {
         PRUint8 canBreak = aBreakBefore[i];
-        if (canBreak && !mCharacterGlyphs[aStart + i].IsClusterStart()) {
+        if (canBreak && !charGlyphs[i].IsClusterStart()) {
             // This can happen ... there is no guarantee that our linebreaking rules
             // align with the platform's idea of what constitutes a cluster.
             NS_WARNING("Break suggested inside cluster!");
             canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE;
         }
-        changed |= mCharacterGlyphs[aStart + i].SetCanBreakBefore(canBreak);
+        changed |= charGlyphs[i].SetCanBreakBefore(canBreak);
     }
     return changed != 0;
 }
 
 gfxTextRun::LigatureData
 gfxTextRun::ComputeLigatureData(PRUint32 aPartStart, PRUint32 aPartEnd,
                                 PropertyProvider *aProvider)
 {
@@ -3798,71 +4322,16 @@ struct BufferAlphaColor {
         mContext->Restore();
     }
 
     gfxContext *mContext;
     gfxFloat mAlpha;
 };
 
 void
-gfxTextRun::AdjustAdvancesForSyntheticBold(gfxContext *aContext,
-                                           PRUint32 aStart,
-                                           PRUint32 aLength)
-{
-    const PRUint32 appUnitsPerDevUnit = GetAppUnitsPerDevUnit();
-    bool isRTL = IsRightToLeft();
-
-    GlyphRunIterator iter(this, aStart, aLength);
-    while (iter.NextRun()) {
-        gfxFont *font = iter.GetGlyphRun()->mFont;
-        if (font->IsSyntheticBold()) {
-            PRUint32 synAppUnitOffset =
-                font->GetSyntheticBoldOffset() *
-                    appUnitsPerDevUnit * CalcXScale(aContext);
-            PRUint32 start = iter.GetStringStart();
-            PRUint32 end = iter.GetStringEnd();
-            PRUint32 i;
-            
-            // iterate over glyphs, start to end
-            for (i = start; i < end; ++i) {
-                gfxTextRun::CompressedGlyph *glyphData = &mCharacterGlyphs[i];
-                
-                if (glyphData->IsSimpleGlyph()) {
-                    // simple glyphs ==> just add the advance
-                    PRInt32 advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
-                    if (CompressedGlyph::IsSimpleAdvance(advance)) {
-                        glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
-                    } else {
-                        // rare case, tested by making this the default
-                        PRUint32 glyphIndex = glyphData->GetSimpleGlyph();
-                        glyphData->SetComplex(true, true, 1);
-                        DetailedGlyph detail = {glyphIndex, advance, 0, 0};
-                        SetGlyphs(i, *glyphData, &detail);
-                    }
-                } else {
-                    // complex glyphs ==> add offset at cluster/ligature boundaries
-                    PRUint32 detailedLength = glyphData->GetGlyphCount();
-                    if (detailedLength) {
-                        gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(i);
-                        if (!details) {
-                            continue;
-                        }
-                        if (isRTL) {
-                            details[0].mAdvance += synAppUnitOffset;
-                        } else {
-                            details[detailedLength - 1].mAdvance += synAppUnitOffset;
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-void
 gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt,
                  PRUint32 aStart, PRUint32 aLength,
                  PropertyProvider *aProvider, gfxFloat *aAdvanceWidth)
 {
     NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
 
     gfxFloat direction = GetDirection();
 
@@ -4160,17 +4629,17 @@ gfxTextRun::BreakAndMeasureText(PRUint32
                 charAdvance += space->mBefore + space->mAfter;
             }
         } else {
             charAdvance = ComputePartialLigatureWidth(i, i + 1, aProvider);
         }
         
         advance += charAdvance;
         if (aTrimWhitespace) {
-            if (GetChar(i) == ' ') {
+            if (mCharacterGlyphs[i].CharIsSpace()) {
                 ++trimmableChars;
                 trimmableAdvance += charAdvance;
             } else {
                 trimmableAdvance = 0;
                 trimmableChars = 0;
             }
         }
     }
@@ -4284,16 +4753,20 @@ gfxTextRun::FindFirstGlyphRunContaining(
                  "Hmm, something went wrong, aOffset should have been found");
     return start;
 }
 
 nsresult
 gfxTextRun::AddGlyphRun(gfxFont *aFont, PRUint8 aMatchType,
                         PRUint32 aUTF16Offset, bool aForceNewRun)
 {
+    NS_ASSERTION(aFont, "adding glyph run for null font!");
+    if (!aFont) {
+        return NS_OK;
+    }    
     PRUint32 numGlyphRuns = mGlyphRuns.Length();
     if (!aForceNewRun && numGlyphRuns > 0) {
         GlyphRun *lastGlyphRun = &mGlyphRuns[numGlyphRuns - 1];
 
         NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
                      "Glyph runs out of order (and run not forced)");
 
         // Don't append a run if the font is already the one we want
@@ -4373,19 +4846,20 @@ gfxTextRun::SanitizeGlyphRuns()
     if (mGlyphRuns.Length() <= 1)
         return;
 
     // If any glyph run starts with ligature-continuation characters, we need to advance it
     // to the first "real" character to avoid drawing partial ligature glyphs from wrong font
     // (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes
     // it appear as if a ligature has been formed)
     PRInt32 i, lastRunIndex = mGlyphRuns.Length() - 1;
+    const CompressedGlyph *charGlyphs = mCharacterGlyphs;
     for (i = lastRunIndex; i >= 0; --i) {
         GlyphRun& run = mGlyphRuns[i];
-        while (mCharacterGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
+        while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
                run.mCharacterOffset < mCharacterCount) {
             run.mCharacterOffset++;
         }
         // if the run has become empty, eliminate it
         if ((i < lastRunIndex &&
              run.mCharacterOffset >= mGlyphRuns[i+1].mCharacterOffset) ||
             (i == lastRunIndex && run.mCharacterOffset == mCharacterCount)) {
             mGlyphRuns.RemoveElementAt(i);
@@ -4407,20 +4881,16 @@ gfxTextRun::CountMissingGlyphs()
     return count;
 }
 
 gfxTextRun::DetailedGlyph *
 gfxTextRun::AllocateDetailedGlyphs(PRUint32 aIndex, PRUint32 aCount)
 {
     NS_ASSERTION(aIndex < mCharacterCount, "Index out of range");
 
-    if (!mCharacterGlyphs) {
-        return nsnull;
-    }
-
     if (!mDetailedGlyphs) {
         mDetailedGlyphs = new DetailedGlyphStore();
     }
 
     DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
     if (!details) {
         mCharacterGlyphs[aIndex].SetMissing(0);
         return nsnull;
@@ -4429,42 +4899,39 @@ gfxTextRun::AllocateDetailedGlyphs(PRUin
     return details;
 }
 
 void
 gfxTextRun::SetGlyphs(PRUint32 aIndex, CompressedGlyph aGlyph,
                       const DetailedGlyph *aGlyphs)
 {
     NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
-    NS_ASSERTION(aIndex > 0 ||
-                 (aGlyph.IsClusterStart() && aGlyph.IsLigatureGroupStart()),
-                 "First character must be the start of a cluster and can't be a ligature continuation!");
+    NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
+                 "First character can't be a ligature continuation!");
 
     PRUint32 glyphCount = aGlyph.GetGlyphCount();
     if (glyphCount > 0) {
         DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
         if (!details)
             return;
         memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
     }
     mCharacterGlyphs[aIndex] = aGlyph;
 }
 
-#include "ignorable.x-ccmap"
-DEFINE_X_CCMAP(gIgnorableCCMapExt, const);
-
-static inline bool
-IsDefaultIgnorable(PRUint32 aChar)
-{
-    return CCMAP_HAS_CHAR_EXT(gIgnorableCCMapExt, aChar);
-}
-
 void
 gfxTextRun::SetMissingGlyph(PRUint32 aIndex, PRUint32 aChar)
 {
+    PRUint8 category = gfxUnicodeProperties::GetGeneralCategory(aChar);
+    if (category >= HB_CATEGORY_COMBINING_MARK &&
+        category <= HB_CATEGORY_NON_SPACING_MARK)
+    {
+        mCharacterGlyphs[aIndex].SetComplex(false, true, 0);
+    }
+
     DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
     if (!details)
         return;
 
     details->mGlyphID = aChar;
     GlyphRun *glyphRun = &mGlyphRuns[FindFirstGlyphRunContaining(aIndex)];
     if (IsDefaultIgnorable(aChar)) {
         // Setting advance width to zero will prevent drawing the hexbox
@@ -4474,68 +4941,78 @@ gfxTextRun::SetMissingGlyph(PRUint32 aIn
                                 gfxFontMissingGlyphs::GetDesiredMinWidth(aChar));
         details->mAdvance = PRUint32(width*GetAppUnitsPerDevUnit());
     }
     details->mXOffset = 0;
     details->mYOffset = 0;
     mCharacterGlyphs[aIndex].SetMissing(1);
 }
 
-bool
-gfxTextRun::FilterIfIgnorable(PRUint32 aIndex)
+void
+gfxTextRun::CopyGlyphDataFrom(const gfxShapedWord *aShapedWord, PRUint32 aOffset)
 {
-    PRUint32 ch = GetChar(aIndex);
-    if (IsDefaultIgnorable(ch)) {
-        DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
-        if (details) {
-            details->mGlyphID = ch;
-            details->mAdvance = 0;
-            details->mXOffset = 0;
-            details->mYOffset = 0;
-            mCharacterGlyphs[aIndex].SetMissing(1);
-            return true;
+    PRUint32 wordLen = aShapedWord->Length();
+    NS_ASSERTION(aOffset + wordLen <= GetLength(),
+                 "word overruns end of textrun!");
+
+    const CompressedGlyph *wordGlyphs = aShapedWord->GetCharacterGlyphs();
+    if (aShapedWord->HasDetailedGlyphs()) {
+        for (PRUint32 i = 0; i < wordLen; ++i, ++aOffset) {
+            const CompressedGlyph& g = wordGlyphs[i];
+            if (g.IsSimpleGlyph()) {
+                SetSimpleGlyph(aOffset, g);
+            } else {
+                const DetailedGlyph *details =
+                    g.GetGlyphCount() > 0 ?
+                        aShapedWord->GetDetailedGlyphs(i) : nsnull;
+                SetGlyphs(aOffset, g, details);
+            }
         }
-    }
-    return false;
+    } else {
+        memcpy(GetCharacterGlyphs() + aOffset, wordGlyphs,
+               wordLen * sizeof(CompressedGlyph));
+    }
 }
 
 void
 gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart,
                               PRUint32 aLength, PRUint32 aDest)
 {
     NS_ASSERTION(aStart + aLength <= aSource->GetLength(),
                  "Source substring out of range");
     NS_ASSERTION(aDest + aLength <= GetLength(),
                  "Destination substring out of range");
 
     if (aSource->mSkipDrawing) {
         mSkipDrawing = true;
     }
 
     // Copy base glyph data, and DetailedGlyph data where present
+    const CompressedGlyph *srcGlyphs = aSource->mCharacterGlyphs + aStart;
+    CompressedGlyph *dstGlyphs = mCharacterGlyphs + aDest;
     for (PRUint32 i = 0; i < aLength; ++i) {
-        CompressedGlyph g = aSource->mCharacterGlyphs[i + aStart];
-        g.SetCanBreakBefore(mCharacterGlyphs[i + aDest].CanBreakBefore());
+        CompressedGlyph g = srcGlyphs[i];
+        g.SetCanBreakBefore(dstGlyphs[i].CanBreakBefore());
         if (!g.IsSimpleGlyph()) {
             PRUint32 count = g.GetGlyphCount();
             if (count > 0) {
                 DetailedGlyph *dst = AllocateDetailedGlyphs(i + aDest, count);
                 if (dst) {
                     DetailedGlyph *src = aSource->GetDetailedGlyphs(i + aStart);
                     if (src) {
                         ::memcpy(dst, src, count * sizeof(DetailedGlyph));
                     } else {
                         g.SetMissing(0);
                     }
                 } else {
                     g.SetMissing(0);
                 }
             }
         }
-        mCharacterGlyphs[i + aDest] = g;
+        dstGlyphs[i] = g;
     }
 
     // Copy glyph runs
     GlyphRunIterator iter(aSource, aStart, aLength);
 #ifdef DEBUG
     gfxFont *lastFont = nsnull;
 #endif
     while (iter.NextRun()) {
@@ -4564,41 +5041,62 @@ gfxTextRun::CopyGlyphDataFrom(gfxTextRun
         nsresult rv = AddGlyphRun(font, iter.GetGlyphRun()->mMatchType,
                                   start - aStart + aDest, false);
         if (NS_FAILED(rv))
             return;
     }
 }
 
 void
-gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIndex)
+gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext,
+                          PRUint32 aCharIndex)
+{
+    if (SetSpaceGlyphIfSimple(aFont, aContext, aCharIndex, ' ')) {
+        return;
+    }
+
+    aFont->InitWordCache();
+    static const PRUint8 space = ' ';
+    gfxShapedWord *sw = aFont->GetShapedWord(aContext,
+                                             &space, 1,
+                                             HashMix(0, ' '), 
+                                             HB_SCRIPT_LATIN,
+                                             mAppUnitsPerDevUnit,
+                                             gfxTextRunFactory::TEXT_IS_8BIT |
+                                             gfxTextRunFactory::TEXT_IS_ASCII |
+                                             gfxTextRunFactory::TEXT_IS_PERSISTENT);
+    if (sw) {
+        AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
+        CopyGlyphDataFrom(sw, aCharIndex);
+    }
+}
+
+bool
+gfxTextRun::SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext,
+                                  PRUint32 aCharIndex, PRUnichar aSpaceChar)
 {
     PRUint32 spaceGlyph = aFont->GetSpaceGlyph();
-    float spaceWidth = aFont->GetMetrics().spaceWidth;
-    PRUint32 spaceWidthAppUnits = NS_lroundf(spaceWidth*mAppUnitsPerDevUnit);
-    if (!spaceGlyph ||
-        !CompressedGlyph::IsSimpleGlyphID(spaceGlyph) ||
-        !CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
-        gfxTextRunFactory::Parameters params = {
-            aContext, nsnull, nsnull, nsnull, 0, mAppUnitsPerDevUnit
-        };
-        static const PRUint8 space = ' ';
-        nsAutoPtr<gfxTextRun> textRun;
-        textRun = mFontGroup->MakeTextRun(&space, 1, &params,
-            gfxTextRunFactory::TEXT_IS_8BIT | gfxTextRunFactory::TEXT_IS_ASCII |
-            gfxTextRunFactory::TEXT_IS_PERSISTENT);
-        if (!textRun || !textRun->mCharacterGlyphs)
-            return;
-        CopyGlyphDataFrom(textRun, 0, 1, aCharIndex);
-        return;
-    }
+    if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
+        return false;
+    }
+
+    PRUint32 spaceWidthAppUnits =
+        NS_lroundf(aFont->GetMetrics().spaceWidth * mAppUnitsPerDevUnit);
+    if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
+        return false;
+    }
+
     AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
     CompressedGlyph g;
     g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
+    if (aSpaceChar == ' ') {
+        g.SetIsSpace();
+    }
     SetSimpleGlyph(aCharIndex, g);
+    return true;
 }
 
 void
 gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
 {
     bool needsGlyphExtents = NeedsGlyphExtents(this);
     if (!needsGlyphExtents && !mDetailedGlyphs)
         return;
@@ -4711,56 +5209,44 @@ gfxTextRun::ClusterIterator::ClusterAdva
     return mTextRun->GetAdvanceWidth(mCurrentChar, ClusterLength(), aProvider);
 }
 
 size_t
 gfxTextRun::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf)
 {
     // The second arg is how much gfxTextRun::AllocateStorage would have
     // allocated.
-    size_t total =
-        aMallocSizeOf(mCharacterGlyphs,
-                      sizeof(CompressedGlyph) *
-                      GlyphStorageAllocCount(mCharacterCount, mFlags));
+    size_t total = mGlyphRuns.SizeOfExcludingThis(aMallocSizeOf);
 
     if (mDetailedGlyphs) {
         total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
     }
 
-    total += mGlyphRuns.SizeOfExcludingThis(aMallocSizeOf);
-
     return total;
 }
 
 size_t
 gfxTextRun::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf)
 {
-    return aMallocSizeOf(this, sizeof(gfxTextRun)) +
+    // The second arg is how much gfxTextRun::AllocateStorageForTextRun would
+    // have allocated, given the character count of this run.
+    return aMallocSizeOf(this, sizeof(gfxTextRun) + sizeof(CompressedGlyph) * GetLength()) +
            SizeOfExcludingThis(aMallocSizeOf);
 }
 
 
 #ifdef DEBUG
 void
 gfxTextRun::Dump(FILE* aOutput) {
     if (!aOutput) {
         aOutput = stdout;
     }
 
     PRUint32 i;
-    fputc('"', aOutput);
-    for (i = 0; i < mCharacterCount; ++i) {
-        PRUnichar ch = GetChar(i);
-        if (ch >= 32 && ch < 128) {
-            fputc(ch, aOutput);
-        } else {
-            fprintf(aOutput, "\\u%4x", ch);
-        }
-    }
-    fputs("\" [", aOutput);
+    fputc('[', aOutput);
     for (i = 0; i < mGlyphRuns.Length(); ++i) {
         if (i > 0) {
             fputc(',', aOutput);
         }
         gfxFont* font = mGlyphRuns[i].mFont;
         const gfxFontStyle* style = font->GetStyle();
         NS_ConvertUTF16toUTF8 fontName(font->GetName());
         nsCAutoString lang;
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -66,16 +66,17 @@ typedef struct _cairo_scaled_font cairo_
 
 class gfxContext;
 class gfxTextRun;
 class gfxFont;
 class gfxFontFamily;
 class gfxFontGroup;
 class gfxUserFontSet;
 class gfxUserFontData;
+class gfxShapedWord;
 
 class nsILanguageAtomService;
 
 typedef struct _hb_blob_t hb_blob_t;
 
 // We should eliminate these synonyms when it won't cause many merge conflicts.
 #define FONT_STYLE_NORMAL              NS_FONT_STYLE_NORMAL
 #define FONT_STYLE_ITALIC              NS_FONT_STYLE_ITALIC
@@ -665,49 +666,54 @@ struct gfxTextRange {
  * When a font's refcount decreases to zero, instead of deleting it we
  * add it to our expiration tracker.
  * The expiration tracker tracks fonts with zero refcount. After a certain
  * period of time, such fonts expire and are deleted.
  *
  * We're using 3 generations with a ten-second generation interval, so
  * zero-refcount fonts will be deleted 20-30 seconds after their refcount
  * goes to zero, if timer events fire in a timely manner.
+ *
+ * The font cache also handles timed expiration of cached ShapedWords
+ * for "persistent" fonts: it has a repeating timer, and notifies
+ * each cached font to "age" its shaped words. The words will be released
+ * by the fonts if they get aged three times without being re-used in the
+ * meantime.
+ *
+ * Note that the ShapedWord timeout is much larger than the font timeout,
+ * so that in the case of a short-lived font, we'll discard the gfxFont
+ * completely, with all its words, and avoid the cost of aging the words
+ * individually. That only happens with longer-lived fonts.
  */
 class THEBES_API gfxFontCache : public nsExpirationTracker<gfxFont,3> {
 public:
-    enum { TIMEOUT_SECONDS = 10 };
-    gfxFontCache()
-        : nsExpirationTracker<gfxFont,3>(TIMEOUT_SECONDS*1000) { mFonts.Init(); }
-    ~gfxFontCache() {
-        // Expire everything that has a zero refcount, so we don't leak them.
-        AgeAllGenerations();
-        // All fonts should be gone.
-        NS_WARN_IF_FALSE(mFonts.Count() == 0,
-                         "Fonts still alive while shutting down gfxFontCache");
-        // Note that we have to delete everything through the expiration
-        // tracker, since there might be fonts not in the hashtable but in
-        // the tracker.
-    }
+    enum {
+        FONT_TIMEOUT_SECONDS = 10,
+        SHAPED_WORD_TIMEOUT_SECONDS = 60
+    };
+
+    gfxFontCache();
+    ~gfxFontCache();
 
     /*
      * Get the global gfxFontCache.  You must call Init() before
      * calling this method --- the result will not be null.
      */
     static gfxFontCache* GetCache() {
         return gGlobalCache;
     }
 
     static nsresult Init();
     // It's OK to call this even if Init() has not been called.
     static void Shutdown();
 
     // Look up a font in the cache. Returns an addrefed pointer, or null
     // if there's nothing matching in the cache
     already_AddRefed<gfxFont> Lookup(const gfxFontEntry *aFontEntry,
-                                     const gfxFontStyle *aFontGroup);
+                                     const gfxFontStyle *aStyle);
     // We created a new font (presumably because Lookup returned null);
     // put it in the cache. The font's refcount should be nonzero. It is
     // allowable to add a new font even if there is one already in the
     // cache with the same key; we'll forget about the old one.
     void AddNew(gfxFont *aFont);
 
     // The font's refcount has gone to zero; give ownership of it to
     // the cache. We delete it if it's not acquired again after a certain
@@ -755,16 +761,129 @@ protected:
             return NS_PTR_TO_INT32(aKey->mFontEntry) ^ aKey->mStyle->Hash();
         }
         enum { ALLOW_MEMMOVE = true };
 
         gfxFont* mFont;
     };
 
     nsTHashtable<HashEntry> mFonts;
+
+    static PLDHashOperator AgeCachedWordsForFont(HashEntry* aHashEntry, void*);
+    static void WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache);
+    nsCOMPtr<nsITimer>      mWordCacheExpirationTimer;
+};
+
+class THEBES_API gfxTextRunFactory {
+    NS_INLINE_DECL_REFCOUNTING(gfxTextRunFactory)
+
+public:
+    // Flags in the mask 0xFFFF0000 are reserved for textrun clients
+    // Flags in the mask 0x0000F000 are reserved for per-platform fonts
+    // Flags in the mask 0x00000FFF are set by the textrun creator.
+    enum {
+        CACHE_TEXT_FLAGS    = 0xF0000000,
+        USER_TEXT_FLAGS     = 0x0FFF0000,
+        PLATFORM_TEXT_FLAGS = 0x0000F000,
+        TEXTRUN_TEXT_FLAGS  = 0x00000FFF,
+        SETTABLE_FLAGS      = CACHE_TEXT_FLAGS | USER_TEXT_FLAGS,
+
+        /**
+         * When set, the text string pointer used to create the text run
+         * is guaranteed to be available during the lifetime of the text run.
+         */
+        TEXT_IS_PERSISTENT           = 0x0001,
+        /**
+         * When set, the text is known to be all-ASCII (< 128).
+         */
+        TEXT_IS_ASCII                = 0x0002,
+        /**
+         * When set, the text is RTL.
+         */
+        TEXT_IS_RTL                  = 0x0004,
+        /**
+         * When set, spacing is enabled and the textrun needs to call GetSpacing
+         * on the spacing provider.
+         */
+        TEXT_ENABLE_SPACING          = 0x0008,
+        /**
+         * When set, GetHyphenationBreaks may return true for some character
+         * positions, otherwise it will always return false for all characters.
+         */
+        TEXT_ENABLE_HYPHEN_BREAKS    = 0x0010,
+        /**
+         * When set, the text has no characters above 255 and it is stored
+         * in the textrun in 8-bit format.
+         */
+        TEXT_IS_8BIT                 = 0x0020,
+        /**
+         * When set, the RunMetrics::mBoundingBox field will be initialized
+         * properly based on glyph extents, in particular, glyph extents that
+         * overflow the standard font-box (the box defined by the ascent, descent
+         * and advance width of the glyph). When not set, it may just be the
+         * standard font-box even if glyphs overflow.
+         */
+        TEXT_NEED_BOUNDING_BOX       = 0x0040,
+        /**
+         * When set, optional ligatures are disabled. Ligatures that are
+         * required for legible text should still be enabled.
+         */
+        TEXT_DISABLE_OPTIONAL_LIGATURES = 0x0080,
+        /**
+         * When set, the textrun should favour speed of construction over
+         * quality. This may involve disabling ligatures and/or kerning or
+         * other effects.
+         */
+        TEXT_OPTIMIZE_SPEED          = 0x0100,
+        /**
+         * For internal use by the memory reporter when accounting for
+         * storage used by textruns.
+         * Because the reporter may visit each textrun multiple times while
+         * walking the frame trees and textrun cache, it needs to mark
+         * textruns that have been seen so as to avoid multiple-accounting.
+         */
+        TEXT_RUN_SIZE_ACCOUNTED      = 0x0200,
+
+        /**
+         * nsTextFrameThebes sets these, but they're defined here rather than
+         * in nsTextFrameUtils.h because ShapedWord creation/caching also needs
+         * to check the _INCOMING flag
+         */
+        TEXT_TRAILING_ARABICCHAR = 0x20000000,
+        /**
+         * When set, the previous character for this textrun was an Arabic
+         * character.  This is used for the context detection necessary for
+         * bidi.numeral implementation.
+         */
+        TEXT_INCOMING_ARABICCHAR = 0x40000000,
+
+        TEXT_UNUSED_FLAGS = 0x90000000
+    };
+
+    /**
+     * This record contains all the parameters needed to initialize a textrun.
+     */
+    struct Parameters {
+        // A reference context suggesting where the textrun will be rendered
+        gfxContext   *mContext;
+        // Pointer to arbitrary user data (which should outlive the textrun)
+        void         *mUserData;
+        // A description of which characters have been stripped from the original
+        // DOM string to produce the characters in the textrun. May be null
+        // if that information is not relevant.
+        gfxSkipChars *mSkipChars;
+        // A list of where linebreaks are currently placed in the textrun. May
+        // be null if mInitialBreakCount is zero.
+        PRUint32     *mInitialBreaks;
+        PRUint32      mInitialBreakCount;
+        // The ratio to use to convert device pixels to application layout units
+        PRUint32      mAppUnitsPerDevUnit;
+    };
+
+    virtual ~gfxTextRunFactory() {}
 };
 
 /**
  * This stores glyph bounds information for a particular gfxFont, at
  * a particular appunits-per-dev-pixel ratio (because the compressed glyph
  * width array is stored in appunits).
  * 
  * We store a hashtable from glyph IDs to float bounding rects. For the
@@ -902,22 +1021,19 @@ public:
     gfxFontShaper(gfxFont *aFont)
         : mFont(aFont)
     {
         NS_ASSERTION(aFont, "shaper requires a valid font!");
     }
 
     virtual ~gfxFontShaper() { }
 
-    virtual bool InitTextRun(gfxContext *aContext,
-                               gfxTextRun *aTextRun,
-                               const PRUnichar *aString,
-                               PRUint32 aRunStart,
-                               PRUint32 aRunLength,
-                               PRInt32 aRunScript) = 0;
+    virtual bool ShapeWord(gfxContext *aContext,
+                           gfxShapedWord *aShapedWord,
+                           const PRUnichar *aText) = 0;
 
     gfxFont *GetFont() const { return mFont; }
 
 protected:
     // the font this shaper is working with
     gfxFont * mFont;
 };
 
@@ -1247,26 +1363,140 @@ public:
             return 0;
         }
         return mFontEntry->GetUVSGlyph(aCh, aVS); 
     }
 
     // call the (virtual) InitTextRun method to do glyph generation/shaping,
     // limiting the length of text passed by processing the run in multiple
     // segments if necessary
+    template<typename T>
     bool SplitAndInitTextRun(gfxContext *aContext,
-                               gfxTextRun *aTextRun,
-                               const PRUnichar *aString,
-                               PRUint32 aRunStart,
-                               PRUint32 aRunLength,
-                               PRInt32 aRunScript);
+                             gfxTextRun *aTextRun,
+                             const T *aString,
+                             PRUint32 aRunStart,
+                             PRUint32 aRunLength,
+                             PRInt32 aRunScript);
+
+    // Get a ShapedWord representing the given text (either 8- or 16-bit)
+    // for use in setting up a gfxTextRun.
+    template<typename T>
+    gfxShapedWord* GetShapedWord(gfxContext *aContext,
+                                 const T *aText,
+                                 PRUint32 aLength,
+                                 PRUint32 aHash,
+                                 PRInt32 aRunScript,
+                                 PRInt32 aAppUnitsPerDevUnit,
+                                 PRUint32 aFlags);
+
+    // Ensure the ShapedWord cache is initialized. This MUST be called before
+    // any attempt to use GetShapedWord().
+    void InitWordCache() {
+        if (!mWordCache.IsInitialized()) {
+            mWordCache.Init();
+        }
+    }
+
+    // Called by the gfxFontCache timer to increment the age of all the words,
+    // so that they'll expire after a sufficient period of non-use
+    void AgeCachedWords() {
+        if (mWordCache.IsInitialized()) {
+            (void)mWordCache.EnumerateEntries(AgeCacheEntry, this);
+        }
+    }
 
 protected:
+    // Call the appropriate shaper to generate glyphs for aText and store
+    // them into aShapedWord.
+    // The length of the text is aShapedWord->Length().
+    virtual bool ShapeWord(gfxContext *aContext,
+                           gfxShapedWord *aShapedWord,
+                           const PRUnichar *aText,
+                           bool aPreferPlatformShaping = false);
+
     nsRefPtr<gfxFontEntry> mFontEntry;
 
+    struct CacheHashKey {
+        union {
+            const PRUint8   *mSingle;
+            const PRUnichar *mDouble;
+        }                mText;
+        PRUint32         mLength;
+        PRUint32         mFlags;
+        PRInt32          mScript;
+        PRInt32          mAppUnitsPerDevUnit;
+        PLDHashNumber    mHashKey;
+        bool             mTextIs8Bit;
+
+        CacheHashKey(const PRUint8 *aText, PRUint32 aLength,
+                     PRUint32 aStringHash,
+                     PRInt32 aScriptCode, PRInt32 aAppUnitsPerDevUnit,
+                     PRUint32 aFlags)
+            : mLength(aLength),
+              mFlags(aFlags),
+              mScript(aScriptCode),
+              mAppUnitsPerDevUnit(aAppUnitsPerDevUnit),
+              mHashKey(aStringHash + aScriptCode +
+                  aAppUnitsPerDevUnit * 0x100 + aFlags * 0x10000),
+              mTextIs8Bit(true)
+        {
+            NS_ASSERTION(aFlags & gfxTextRunFactory::TEXT_IS_8BIT,
+                         "8-bit flag should have been set");
+            mText.mSingle = aText;
+        }
+
+        CacheHashKey(const PRUnichar *aText, PRUint32 aLength,
+                     PRUint32 aStringHash,
+                     PRInt32 aScriptCode, PRInt32 aAppUnitsPerDevUnit,
+                     PRUint32 aFlags)
+            : mLength(aLength),
+              mFlags(aFlags),
+              mScript(aScriptCode),
+              mAppUnitsPerDevUnit(aAppUnitsPerDevUnit),
+              mHashKey(aStringHash + aScriptCode +
+                  aAppUnitsPerDevUnit * 0x100 + aFlags * 0x10000),
+              mTextIs8Bit(false)
+        {
+            // We can NOT assert that TEXT_IS_8BIT is false in aFlags here,
+            // because this might be an 8bit-only word from a 16-bit textrun,
+            // in which case the text we're passed is still in 16-bit form,
+            // and we'll have to use an 8-to-16bit comparison in KeyEquals.
+            mText.mDouble = aText;
+        }
+    };
+
+    class CacheHashEntry : public PLDHashEntryHdr {
+    public:
+        typedef const CacheHashKey &KeyType;
+        typedef const CacheHashKey *KeyTypePointer;
+
+        // When constructing a new entry in the hashtable, the caller of Put()
+        // will fill us in.
+        CacheHashEntry(KeyTypePointer aKey) { }
+        CacheHashEntry(const CacheHashEntry& toCopy) { NS_ERROR("Should not be called"); }
+        ~CacheHashEntry() { }
+
+        bool KeyEquals(const KeyTypePointer aKey) const;
+
+        static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+
+        static PLDHashNumber HashKey(const KeyTypePointer aKey) {
+            return aKey->mHashKey;
+        }
+
+        enum { ALLOW_MEMMOVE = true };
+
+        nsAutoPtr<gfxShapedWord> mShapedWord;
+    };
+
+    nsTHashtable<CacheHashEntry> mWordCache;
+
+    static PLDHashOperator AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData);
+    static const PRUint32  kShapedWordCacheMaxAge = 3;
+
     bool                       mIsValid;
 
     // use synthetic bolding for environments where this is not supported
     // by the platform
     bool                       mApplySyntheticBold;
 
     nsExpirationState          mExpirationState;
     gfxFontStyle               mStyle;
@@ -1309,125 +1539,619 @@ protected:
     // Helper to calculate various derived metrics from the results of
     // InitMetricsFromSfntTables or equivalent platform code
     void CalculateDerivedMetrics(Metrics& aMetrics);
 
     // some fonts have bad metrics, this method sanitize them.
     // if this font has bad underline offset, aIsBadUnderlineFont should be true.
     void SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont);
 
-    // Default simply calls m[Platform|HarfBuzz]Shaper->InitTextRun().
-    // Override if the font class wants to give special handling
-    // to shaper failure.
-    // Returns false if shaping failed (though currently we
-    // don't have any good way to handle that situation).
-    virtual bool InitTextRun(gfxContext *aContext,
-                               gfxTextRun *aTextRun,
-                               const PRUnichar *aString,
-                               PRUint32 aRunStart,
-                               PRUint32 aRunLength,
-                               PRInt32 aRunScript,
-                               bool aPreferPlatformShaping = false);
+    // Bug 674909. When synthetic bolding text by drawing twice, need to
+    // render using a pixel offset in device pixels, otherwise text
+    // doesn't appear bolded, it appears as if a bad text shadow exists
+    // when a non-identity transform exists.  Use an offset factor so that
+    // the second draw occurs at a constant offset in device pixels.
+    // This helper calculates the scale factor we need to apply to the
+    // synthetic-bold offset.
+    static double CalcXScale(gfxContext *aContext);
 };
 
 // proportion of ascent used for x-height, if unable to read value from font
 #define DEFAULT_XHEIGHT_FACTOR 0.56f
 
-class THEBES_API gfxTextRunFactory {
-    NS_INLINE_DECL_REFCOUNTING(gfxTextRunFactory)
-
+/*
+ * gfxShapedWord stores a list of zero or more glyphs for each character. For each
+ * glyph we store the glyph ID, the advance, and possibly an xoffset and yoffset.
+ * The idea is that a string is rendered by a loop that draws each glyph
+ * at its designated offset from the current point, then advances the current
+ * point by the glyph's advance in the direction of the textrun (LTR or RTL).
+ * Each glyph advance is always rounded to the nearest appunit; this ensures
+ * consistent results when dividing the text in a textrun into multiple text
+ * frames (frame boundaries are always aligned to appunits). We optimize
+ * for the case where a character has a single glyph and zero xoffset and yoffset,
+ * and the glyph ID and advance are in a reasonable range so we can pack all
+ * necessary data into 32 bits.
+ *
+ * This glyph data is copied into gfxTextRuns as needed from the cache of
+ * ShapedWords associated with each gfxFont instance.
+ *
+ * gfxTextRun methods that measure or draw substrings will associate all the
+ * glyphs in a cluster with the first character of the cluster; if that character
+ * is in the substring, the glyphs will be measured or drawn, otherwise they
+ * won't.
+ */
+class gfxShapedWord
+{
 public:
-    // Flags in the mask 0xFFFF0000 are reserved for textrun clients
-    // Flags in the mask 0x0000F000 are reserved for per-platform fonts
-    // Flags in the mask 0x00000FFF are set by the textrun creator.
-    enum {
-        CACHE_TEXT_FLAGS    = 0xF0000000,
-        USER_TEXT_FLAGS     = 0x0FFF0000,
-        PLATFORM_TEXT_FLAGS = 0x0000F000,
-        TEXTRUN_TEXT_FLAGS  = 0x00000FFF,
-        SETTABLE_FLAGS      = CACHE_TEXT_FLAGS | USER_TEXT_FLAGS,
-
-        /**
-         * When set, the text string pointer used to create the text run
-         * is guaranteed to be available during the lifetime of the text run.
-         */
-        TEXT_IS_PERSISTENT           = 0x0001,
-        /**
-         * When set, the text is known to be all-ASCII (< 128).
-         */
-        TEXT_IS_ASCII                = 0x0002,
-        /**
-         * When set, the text is RTL.
-         */
-        TEXT_IS_RTL                  = 0x0004,
-        /**
-         * When set, spacing is enabled and the textrun needs to call GetSpacing
-         * on the spacing provider.
-         */
-        TEXT_ENABLE_SPACING          = 0x0008,
+    static const PRUint32 kMaxLength = 0x7fff;
+
+    // Create a ShapedWord that can hold glyphs for aLength characters,
+    // with mCharacterGlyphs sized appropriately.
+    //
+    // Returns null on allocation failure (does NOT use infallible alloc)
+    // so caller must check for success.
+    //
+    // This does NOT perform shaping, so the returned word contains no
+    // glyph data; the caller must call gfxFont::Shape() with appropriate
+    // parameters to set up the glyphs.
+    static gfxShapedWord* Create(const PRUint8 *aText, PRUint32 aLength,
+                                 PRInt32 aRunScript,
+                                 PRInt32 aAppUnitsPerDevUnit,
+                                 PRUint32 aFlags) {
+        NS_ASSERTION(aLength <= kMaxLength, "excessive length for gfxShapedWord!");
+
+        // Compute size needed including the mCharacterGlyphs array
+        // and a copy of the original text
+        PRUint32 size =
+            offsetof(gfxShapedWord, mCharacterGlyphs) +
+            aLength * (sizeof(CompressedGlyph) + sizeof(PRUint8));
+        void *storage = moz_malloc(size);
+        if (!storage) {
+            return nsnull;
+        }
+
+        // Construct in the pre-allocated storage, using placement new
+        return new (storage) gfxShapedWord(aText, aLength, aRunScript,
+                                           aAppUnitsPerDevUnit, aFlags);
+    }
+
+    static gfxShapedWord* Create(const PRUnichar *aText, PRUint32 aLength,
+                                 PRInt32 aRunScript,
+                                 PRInt32 aAppUnitsPerDevUnit,
+                                 PRUint32 aFlags) {
+        NS_ASSERTION(aLength <= kMaxLength, "excessive length for gfxShapedWord!");
+
+        // In the 16-bit version of Create, if the TEXT_IS_8BIT flag is set,
+        // then we convert the text to an 8-bit version and call the 8-bit
+        // Create function instead.
+        if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
+            nsCAutoString narrowText;
+            LossyAppendUTF16toASCII(nsDependentSubstring(aText, aLength),
+                                    narrowText);
+            return Create((const PRUint8*)(narrowText.BeginReading()),
+                          aLength, aRunScript, aAppUnitsPerDevUnit, aFlags);
+        }
+
+        PRUint32 size =
+            offsetof(gfxShapedWord, mCharacterGlyphs) +
+            aLength * (sizeof(CompressedGlyph) + sizeof(PRUnichar));
+        void *storage = moz_malloc(size);
+        if (!storage) {
+            return nsnull;
+        }
+
+        return new (storage) gfxShapedWord(aText, aLength, aRunScript,
+                                           aAppUnitsPerDevUnit, aFlags);
+    }
+
+    // Override operator delete to properly free the object that was
+    // allocated via moz_malloc.
+    void operator delete(void* p) {
+        moz_free(p);
+    }
+
+    /**
+     * This class records the information associated with a character in the
+     * input string. It's optimized for the case where there is one glyph
+     * representing that character alone.
+     * 
+     * A character can have zero or more associated glyphs. Each glyph
+     * has an advance width and an x and y offset.
+     * A character may be the start of a cluster.
+     * A character may be the start of a ligature group.
+     * A character can be "missing", indicating that the system is unable
+     * to render the character.
+     * 
+     * All characters in a ligature group conceptually share all the glyphs
+     * associated with the characters in a group.
+     */
+    class CompressedGlyph {
+    public:
+        CompressedGlyph() { mValue = 0; }
+
+        enum {
+            // Indicates that a cluster and ligature group starts at this
+            // character; this character has a single glyph with a reasonable
+            // advance and zero offsets. A "reasonable" advance
+            // is one that fits in the available bits (currently 12) (specified
+            // in appunits).
+            FLAG_IS_SIMPLE_GLYPH  = 0x80000000U,
+
+            // Indicates whether a linebreak is allowed before this character;
+            // this is a two-bit field that holds a FLAG_BREAK_TYPE_xxx value
+            // indicating the kind of linebreak (if any) allowed here.
+            FLAGS_CAN_BREAK_BEFORE = 0x60000000U,
+
+            FLAGS_CAN_BREAK_SHIFT = 29,
+            FLAG_BREAK_TYPE_NONE   = 0,
+            FLAG_BREAK_TYPE_NORMAL = 1,
+            FLAG_BREAK_TYPE_HYPHEN = 2,
+
+            FLAG_CHAR_IS_SPACE     = 0x10000000U,
+
+            // The advance is stored in appunits
+            ADVANCE_MASK  = 0x0FFF0000U,
+            ADVANCE_SHIFT = 16,
+
+            GLYPH_MASK = 0x0000FFFFU,
+
+            // Non-simple glyphs may or may not have glyph data in the
+            // corresponding mDetailedGlyphs entry. They have the following
+            // flag bits:
+
+            // When NOT set, indicates that this character corresponds to a
+            // missing glyph and should be skipped (or possibly, render the character
+            // Unicode value in some special way). If there are glyphs,
+            // the mGlyphID is actually the UTF16 character code. The bit is
+            // inverted so we can memset the array to zero to indicate all missing.
+            FLAG_NOT_MISSING              = 0x01,
+            FLAG_NOT_CLUSTER_START        = 0x02,
+            FLAG_NOT_LIGATURE_GROUP_START = 0x04,
+
+            FLAG_CHAR_IS_TAB              = 0x08,
+            FLAG_CHAR_IS_NEWLINE          = 0x10,
+            FLAG_CHAR_IS_LOW_SURROGATE    = 0x20,
+            
+            GLYPH_COUNT_MASK = 0x00FFFF00U,
+            GLYPH_COUNT_SHIFT = 8
+        };
+
+        // "Simple glyphs" have a simple glyph ID, simple advance and their
+        // x and y offsets are zero. Also the glyph extents do not overflow
+        // the font-box defined by the font ascent, descent and glyph advance width.
+        // These case is optimized to avoid storing DetailedGlyphs.
+
+        // Returns true if the glyph ID aGlyph fits into the compressed representation
+        static bool IsSimpleGlyphID(PRUint32 aGlyph) {
+            return (aGlyph & GLYPH_MASK) == aGlyph;
+        }
+        // Returns true if the advance aAdvance fits into the compressed representation.
+        // aAdvance is in appunits.
+        static bool IsSimpleAdvance(PRUint32 aAdvance) {
+            return (aAdvance & (ADVANCE_MASK >> ADVANCE_SHIFT)) == aAdvance;
+        }
+
+        bool IsSimpleGlyph() const { return (mValue & FLAG_IS_SIMPLE_GLYPH) != 0; }
+        PRUint32 GetSimpleAdvance() const { return (mValue & ADVANCE_MASK) >> ADVANCE_SHIFT; }
+        PRUint32 GetSimpleGlyph() const { return mValue & GLYPH_MASK; }
+
+        bool IsMissing() const { return (mValue & (FLAG_NOT_MISSING|FLAG_IS_SIMPLE_GLYPH)) == 0; }
+        bool IsClusterStart() const {
+            return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_CLUSTER_START);
+        }
+        bool IsLigatureGroupStart() const {
+            return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_LIGATURE_GROUP_START);
+        }
+        bool IsLigatureContinuation() const {
+            return (mValue & FLAG_IS_SIMPLE_GLYPH) == 0 &&
+                (mValue & (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING)) ==
+                    (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING);
+        }
+
+        // Return true if the original character was a normal (breakable,
+        // trimmable) space (U+0020). Not true for other characters that
+        // may happen to map to the space glyph (U+00A0).
+        bool CharIsSpace() const {
+            return (mValue & FLAG_CHAR_IS_SPACE) != 0;
+        }
+
+        bool CharIsTab() const {
+            return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_TAB) != 0;
+        }
+        bool CharIsNewline() const {
+            return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_NEWLINE) != 0;
+        }
+        bool CharIsLowSurrogate() const {
+            return !IsSimpleGlyph() && (mValue & FLAG_CHAR_IS_LOW_SURROGATE) != 0;
+        }
+
+        void SetClusterStart(bool aIsClusterStart) {
+            NS_ASSERTION(!IsSimpleGlyph(),
+                         "can't call SetClusterStart on simple glyphs");
+            if (aIsClusterStart) {
+                mValue &= ~FLAG_NOT_CLUSTER_START;
+            } else {
+                mValue |= FLAG_NOT_CLUSTER_START;
+            }
+        }
+
+        PRUint8 CanBreakBefore() const {
+            return (mValue & FLAGS_CAN_BREAK_BEFORE) >> FLAGS_CAN_BREAK_SHIFT;
+        }
+        // Returns FLAGS_CAN_BREAK_BEFORE if the setting changed, 0 otherwise
+        PRUint32 SetCanBreakBefore(PRUint8 aCanBreakBefore) {
+            NS_ASSERTION(aCanBreakBefore <= 2,
+                         "Bogus break-before value!");
+            PRUint32 breakMask = (PRUint32(aCanBreakBefore) << FLAGS_CAN_BREAK_SHIFT);
+            PRUint32 toggle = breakMask ^ (mValue & FLAGS_CAN_BREAK_BEFORE);
+            mValue ^= toggle;
+            return toggle;
+        }
+
+        CompressedGlyph& SetSimpleGlyph(PRUint32 aAdvanceAppUnits, PRUint32 aGlyph) {
+            NS_ASSERTION(IsSimpleAdvance(aAdvanceAppUnits), "Advance overflow");
+            NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow");
+            mValue = (mValue & FLAGS_CAN_BREAK_BEFORE) |
+                FLAG_IS_SIMPLE_GLYPH |
+                (aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph;
+            return *this;
+        }
+        CompressedGlyph& SetComplex(bool aClusterStart, bool aLigatureStart,
+                PRUint32 aGlyphCount) {
+            mValue = (mValue & FLAGS_CAN_BREAK_BEFORE) |
+                FLAG_NOT_MISSING |
+                (aClusterStart ? 0 : FLAG_NOT_CLUSTER_START) |
+                (aLigatureStart ? 0 : FLAG_NOT_LIGATURE_GROUP_START) |
+                (aGlyphCount << GLYPH_COUNT_SHIFT);
+            return *this;
+        }
         /**
-         * When set, GetHyphenationBreaks may return true for some character
-         * positions, otherwise it will always return false for all characters.
-         */
-        TEXT_ENABLE_HYPHEN_BREAKS    = 0x0010,
-        /**
-         * When set, the text has no characters above 255 and it is stored
-         * in the textrun in 8-bit format.
-         */
-        TEXT_IS_8BIT                 = 0x0020,
-        /**
-         * When set, the RunMetrics::mBoundingBox field will be initialized
-         * properly based on glyph extents, in particular, glyph extents that
-         * overflow the standard font-box (the box defined by the ascent, descent
-         * and advance width of the glyph). When not set, it may just be the
-         * standard font-box even if glyphs overflow.
+         * Missing glyphs are treated as ligature group starts; don't mess with
+         * the cluster-start flag (see bugs 618870 and 619286).
          */
-        TEXT_NEED_BOUNDING_BOX       = 0x0040,
-        /**
-         * When set, optional ligatures are disabled. Ligatures that are
-         * required for legible text should still be enabled.
-         */
-        TEXT_DISABLE_OPTIONAL_LIGATURES = 0x0080,
-        /**
-         * When set, the textrun should favour speed of construction over
-         * quality. This may involve disabling ligatures and/or kerning or
-         * other effects.
-         */
-        TEXT_OPTIMIZE_SPEED          = 0x0100,
-        /**
-         * For internal use by the memory reporter when accounting for
-         * storage used by textruns.
-         * Because the reporter may visit each textrun multiple times while
-         * walking the frame trees and textrun cache, it needs to mark
-         * textruns that have been seen so as to avoid multiple-accounting.
-         */
-        TEXT_RUN_SIZE_ACCOUNTED      = 0x0200
+        CompressedGlyph& SetMissing(PRUint32 aGlyphCount) {
+            mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_NOT_CLUSTER_START)) |
+                (aGlyphCount << GLYPH_COUNT_SHIFT);
+            return *this;
+        }
+        PRUint32 GetGlyphCount() const {
+            NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
+            return (mValue & GLYPH_COUNT_MASK) >> GLYPH_COUNT_SHIFT;
+        }
+
+        void SetIsSpace() {
+            mValue |= FLAG_CHAR_IS_SPACE;
+        }
+        void SetIsTab() {
+            NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
+            mValue |= FLAG_CHAR_IS_TAB;
+        }
+        void SetIsNewline() {
+            NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
+            mValue |= FLAG_CHAR_IS_NEWLINE;
+        }
+        void SetIsLowSurrogate() {
+            NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
+            mValue |= FLAG_CHAR_IS_LOW_SURROGATE;
+        }
+
+    private:
+        PRUint32 mValue;
     };
 
     /**
-     * This record contains all the parameters needed to initialize a textrun.
+     * When the glyphs for a character don't fit into a CompressedGlyph record
+     * in SimpleGlyph format, we use an array of DetailedGlyphs instead.
      */
-    struct Parameters {
-        // A reference context suggesting where the textrun will be rendered
-        gfxContext   *mContext;
-        // Pointer to arbitrary user data (which should outlive the textrun)
-        void         *mUserData;
-        // A description of which characters have been stripped from the original
-        // DOM string to produce the characters in the textrun. May be null
-        // if that information is not relevant.
-        gfxSkipChars *mSkipChars;
-        // A list of where linebreaks are currently placed in the textrun. May
-        // be null if mInitialBreakCount is zero.
-        PRUint32     *mInitialBreaks;
-        PRUint32      mInitialBreakCount;
-        // The ratio to use to convert device pixels to application layout units
-        PRUint32      mAppUnitsPerDevUnit;
+    struct DetailedGlyph {
+        /** The glyphID, or the Unicode character
+         * if this is a missing glyph */
+        PRUint32 mGlyphID;
+        /** The advance, x-offset and y-offset of the glyph, in appunits
+         *  mAdvance is in the text direction (RTL or LTR)
+         *  mXOffset is always from left to right
+         *  mYOffset is always from top to bottom */   
+        PRInt32  mAdvance;
+        float    mXOffset, mYOffset;
     };
 
-    virtual ~gfxTextRunFactory() {}
+    bool IsClusterStart(PRUint32 aPos) {
+        NS_ASSERTION(aPos < Length(), "aPos out of range");
+        return mCharacterGlyphs[aPos].IsClusterStart();
+    }
+
+    bool IsLigatureGroupStart(PRUint32 aPos) {
+        NS_ASSERTION(aPos < Length(), "aPos out of range");
+        return mCharacterGlyphs[aPos].IsLigatureGroupStart();
+    }
+
+    PRUint32 Length() const {
+        return mLength;
+    }
+
+    const PRUint8* Text8Bit() const {
+        NS_ASSERTION(TextIs8Bit(), "invalid use of Text8Bit()");
+        return reinterpret_cast<const PRUint8*>(&mCharacterGlyphs[Length()]);
+    }
+
+    const PRUnichar* TextUnicode() const {
+        NS_ASSERTION(!TextIs8Bit(), "invalid use of TextUnicode()");
+        return reinterpret_cast<const PRUnichar*>(&mCharacterGlyphs[Length()]);
+    }
+
+    PRUnichar GetCharAt(PRUint32 aOffset) const {
+        NS_ASSERTION(aOffset < Length(), "aOffset out of range");
+        return TextIs8Bit() ?
+            PRUnichar(Text8Bit()[aOffset]) : TextUnicode()[aOffset];
+    }
+
+    PRUint32 Flags() const {
+        return mFlags;
+    }
+
+    bool IsRightToLeft() const {
+        return (Flags() & gfxTextRunFactory::TEXT_IS_RTL) != 0;
+    }
+
+    float GetDirection() const {
+        return IsRightToLeft() ? -1.0 : 1.0;
+    }
+
+    bool DisableLigatures() const {
+        return (Flags() & gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) != 0;
+    }
+
+    bool TextIs8Bit() const {
+        return (Flags() & gfxTextRunFactory::TEXT_IS_8BIT) != 0;
+    }
+
+    PRInt32 Script() const {
+        return mScript;
+    }
+
+    PRInt32 AppUnitsPerDevUnit() const {
+        return mAppUnitsPerDevUnit;
+    }
+
+    void ResetAge() {
+        mAgeCounter = 0;
+    }
+    PRUint32 IncrementAge() {
+        return ++mAgeCounter;
+    }
+
+    void SetSimpleGlyph(PRUint32 aCharIndex, CompressedGlyph aGlyph) {
+        NS_ASSERTION(aGlyph.IsSimpleGlyph(), "Should be a simple glyph here");
+        NS_ASSERTION(mCharacterGlyphs, "mCharacterGlyphs pointer is null!");
+        mCharacterGlyphs[aCharIndex] = aGlyph;
+    }
+
+    void SetGlyphs(PRUint32 aCharIndex, CompressedGlyph aGlyph,
+                   const DetailedGlyph *aGlyphs);
+
+    void SetMissingGlyph(PRUint32 aIndex, PRUint32 aChar, gfxFont *aFont);
+
+    void SetIsSpace(PRUint32 aIndex) {
+        mCharacterGlyphs[aIndex].SetIsSpace();
+    }
+
+    void SetIsLowSurrogate(PRUint32 aIndex) {
+        SetGlyphs(aIndex, CompressedGlyph().SetComplex(false, false, 0), nsnull);
+        mCharacterGlyphs[aIndex].SetIsLowSurrogate();
+    }
+
+    bool FilterIfIgnorable(PRUint32 aIndex);
+
+    const CompressedGlyph *GetCharacterGlyphs() const {
+        return &mCharacterGlyphs[0];
+    }
+
+    bool HasDetailedGlyphs() const {
+        return mDetailedGlyphs != nsnull;
+    }
+
+    // NOTE that this must not be called for a character offset that does
+    // not have any DetailedGlyph records; callers must have verified that
+    // mCharacterGlyphs[aCharIndex].GetGlyphCount() is greater than zero.
+    DetailedGlyph *GetDetailedGlyphs(PRUint32 aCharIndex) const {
+        NS_ASSERTION(HasDetailedGlyphs() &&
+                     !mCharacterGlyphs[aCharIndex].IsSimpleGlyph() &&
+                     mCharacterGlyphs[aCharIndex].GetGlyphCount() > 0,
+                     "invalid use of GetDetailedGlyphs; check the caller!");
+        return mDetailedGlyphs->Get(aCharIndex);
+    }
+
+    void AdjustAdvancesForSyntheticBold(float aSynBoldOffset);
+
+    // this is a public static method in order to make it available
+    // for gfxTextRun to use directly on its own CompressedGlyph array,
+    // in addition to the use within ShapedWord
+    static void
+    SetupClusterBoundaries(CompressedGlyph *aGlyphs,
+                           const PRUnichar *aString, PRUint32 aLength);
+
+private:
+    // so that gfxTextRun can share our DetailedGlyphStore class
+    friend class gfxTextRun;
+
+    // Construct storage for a ShapedWord, ready to receive glyph data
+    gfxShapedWord(const PRUint8 *aText, PRUint32 aLength,
+                  PRInt32 aRunScript, PRInt32 aAppUnitsPerDevUnit,
+                  PRUint32 aFlags)
+        : mLength(aLength)
+        , mFlags(aFlags | gfxTextRunFactory::TEXT_IS_8BIT)
+        , mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
+        , mScript(aRunScript)
+        , mAgeCounter(0)
+    {
+        memset(mCharacterGlyphs, 0, aLength * sizeof(CompressedGlyph));
+        PRUint8 *text = reinterpret_cast<PRUint8*>(&mCharacterGlyphs[aLength]);
+        memcpy(text, aText, aLength * sizeof(PRUint8));
+    }
+
+    gfxShapedWord(const PRUnichar *aText, PRUint32 aLength,
+                  PRInt32 aRunScript, PRInt32 aAppUnitsPerDevUnit,
+                  PRUint32 aFlags)
+        : mLength(aLength)
+        , mFlags(aFlags)
+        , mAppUnitsPerDevUnit(aAppUnitsPerDevUnit)
+        , mScript(aRunScript)
+        , mAgeCounter(0)
+    {
+        memset(mCharacterGlyphs, 0, aLength * sizeof(CompressedGlyph));
+        PRUnichar *text = reinterpret_cast<PRUnichar*>(&mCharacterGlyphs[aLength]);
+        memcpy(text, aText, aLength * sizeof(PRUnichar));
+        SetupClusterBoundaries(&mCharacterGlyphs[0], aText, aLength);
+    }
+
+    // Allocate aCount DetailedGlyphs for the given index
+    DetailedGlyph *AllocateDetailedGlyphs(PRUint32 aCharIndex,
+                                          PRUint32 aCount);
+
+    // For characters whose glyph data does not fit the "simple" glyph criteria
+    // in CompressedGlyph, we use a sorted array to store the association
+    // between the source character offset and an index into an array 
+    // DetailedGlyphs. The CompressedGlyph record includes a count of
+    // the number of DetailedGlyph records that belong to the character,
+    // starting at the given index.
+    class DetailedGlyphStore {
+    public:
+        DetailedGlyphStore()
+            : mLastUsed(0)
+        { }
+
+        // This is optimized for the most common calling patterns:
+        // we rarely need random access to the records, access is most commonly
+        // sequential through the textRun, so we record the last-used index
+        // and check whether the caller wants the same record again, or the
+        // next; if not, it's most likely we're starting over from the start
+        // of the run, so we check the first entry before resorting to binary
+        // search as a last resort.
+        // NOTE that this must not be called for a character offset that does
+        // not have any DetailedGlyph records; callers must have verified that
+        // mCharacterGlyphs[aOffset].GetGlyphCount() is greater than zero
+        // before calling this, otherwise the assertions here will fire (in a
+        // debug build), and we'll probably crash.
+        DetailedGlyph* Get(PRUint32 aOffset) {
+            NS_ASSERTION(mOffsetToIndex.Length() > 0,
+                         "no detailed glyph records!");
+            DetailedGlyph* details = mDetails.Elements();
+            // check common cases (fwd iteration, initial entry, etc) first
+            if (mLastUsed < mOffsetToIndex.Length() - 1 &&
+                aOffset == mOffsetToIndex[mLastUsed + 1].mOffset) {
+                ++mLastUsed;
+            } else if (aOffset == mOffsetToIndex[0].mOffset) {
+                mLastUsed = 0;
+            } else if (aOffset == mOffsetToIndex[mLastUsed].mOffset) {
+                // do nothing
+            } else if (mLastUsed > 0 &&
+                       aOffset == mOffsetToIndex[mLastUsed - 1].mOffset) {
+                --mLastUsed;
+            } else {
+                mLastUsed =
+                    mOffsetToIndex.BinaryIndexOf(aOffset, CompareToOffset());
+            }
+            NS_ASSERTION(mLastUsed != nsTArray<DGRec>::NoIndex,
+                         "detailed glyph record missing!");
+            return details + mOffsetToIndex[mLastUsed].mIndex;
+        }
+
+        DetailedGlyph* Allocate(PRUint32 aOffset, PRUint32 aCount) {
+            PRUint32 detailIndex = mDetails.Length();
+            DetailedGlyph *details = mDetails.AppendElements(aCount);
+            if (!details) {
+                return nsnull;
+            }
+            // We normally set up glyph records sequentially, so the common case
+            // here is to append new records to the mOffsetToIndex array;
+            // test for that before falling back to the InsertElementSorted
+            // method.
+            if (mOffsetToIndex.Length() == 0 ||
+                aOffset > mOffsetToIndex[mOffsetToIndex.Length() - 1].mOffset) {
+                if (!mOffsetToIndex.AppendElement(DGRec(aOffset, detailIndex))) {
+                    return nsnull;
+                }
+            } else {
+                if (!mOffsetToIndex.InsertElementSorted(DGRec(aOffset, detailIndex),
+                                                        CompareRecordOffsets())) {
+                    return nsnull;
+                }
+            }
+            return details;
+        }
+
+        size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) {
+            return aMallocSizeOf(this, sizeof(DetailedGlyphStore)) +
+                mDetails.SizeOfExcludingThis(aMallocSizeOf) +
+                mOffsetToIndex.SizeOfExcludingThis(aMallocSizeOf);
+        }
+
+    private:
+        struct DGRec {
+            DGRec(const PRUint32& aOffset, const PRUint32& aIndex)
+                : mOffset(aOffset), mIndex(aIndex) { }
+            PRUint32 mOffset; // source character offset in the textrun
+            PRUint32 mIndex;  // index where this char's DetailedGlyphs begin
+        };
+
+        struct CompareToOffset {
+            bool Equals(const DGRec& a, const PRUint32& b) const {
+                return a.mOffset == b;
+            }
+            bool LessThan(const DGRec& a, const PRUint32& b) const {
+                return a.mOffset < b;
+            }
+        };
+
+        struct CompareRecordOffsets {
+            bool Equals(const DGRec& a, const DGRec& b) const {
+                return a.mOffset == b.mOffset;
+            }
+            bool LessThan(const DGRec& a, const DGRec& b) const {
+                return a.mOffset < b.mOffset;
+            }
+        };
+
+        // Concatenated array of all the DetailedGlyph records needed for the
+        // textRun; individual character offsets are associated with indexes
+        // into this array via the mOffsetToIndex table.
+        nsTArray<DetailedGlyph>     mDetails;
+
+        // For each character offset that needs DetailedGlyphs, we record the
+        // index in mDetails where the list of glyphs begins. This array is
+        // sorted by mOffset.
+        nsTArray<DGRec>             mOffsetToIndex;
+
+        // Records the most recently used index into mOffsetToIndex, so that
+        // we can support sequential access more quickly than just doing
+        // a binary search each time.
+        nsTArray<DGRec>::index_type mLastUsed;
+    };
+
+    nsAutoPtr<DetailedGlyphStore>   mDetailedGlyphs;
+
+    // Number of PRUnichar characters and CompressedGlyph glyph records;
+    // note that gfx font code will never attempt to create a ShapedWord
+    // with a huge number of characters, so we could limit this to 16 bits
+    // to minimize memory usage for large numbers of cached words.
+    PRUint32                        mLength;
+
+    PRUint32                        mFlags;
+
+    PRInt32                         mAppUnitsPerDevUnit;
+    PRInt32                         mScript;
+
+    PRUint32                        mAgeCounter;
+
+    // The mCharacterGlyphs array is actually a variable-size member;
+    // when the ShapedWord is created, its size will be increased as necessary
+    // to allow the proper number of glyphs to be stored.
+    // The original text, in either 8-bit or 16-bit form, will be stored
+    // immediately following the CompressedGlyphs.
+    CompressedGlyph                 mCharacterGlyphs[1];
 };
 
 /**
  * gfxTextRun is an abstraction for drawing and measuring substrings of a run
  * of text. It stores runs of positioned glyph data, each run having a single
  * gfxFont. The glyphs are associated with a string of source text, and the
  * gfxTextRun APIs take parameters that are offsets into that source text.
  * 
@@ -1438,36 +2162,31 @@ public:
  * lazily by methods that need it, it is not cached. Line breaks are stored
  * persistently (insofar as they affect the shaping of glyphs; gfxTextRun does
  * not actually do anything to explicitly account for line breaks). Initially
  * there are no line breaks. The textrun can record line breaks before or after
  * any given cluster. (Line breaks specified inside clusters are ignored.)
  * 
  * It is important that zero-length substrings are handled correctly. This will
  * be on the test!
- * 
- * gfxTextRun stores a list of zero or more glyphs for each character. For each
- * glyph we store the glyph ID, the advance, and possibly an xoffset and yoffset.
- * The idea is that a string is rendered by a loop that draws each glyph
- * at its designated offset from the current point, then advances the current
- * point by the glyph's advance in the direction of the textrun (LTR or RTL).
- * Each glyph advance is always rounded to the nearest appunit; this ensures
- * consistent results when dividing the text in a textrun into multiple text
- * frames (frame boundaries are always aligned to appunits). We optimize
- * for the case where a character has a single glyph and zero xoffset and yoffset,
- * and the glyph ID and advance are in a reasonable range so we can pack all
- * necessary data into 32 bits.
- * 
- * gfxTextRun methods that measure or draw substrings will associate all the
- * glyphs in a cluster with the first character of the cluster; if that character
- * is in the substring, the glyphs will be measured or drawn, otherwise they
- * won't.
  */
 class THEBES_API gfxTextRun {
 public:
+    // we use the same glyph storage as gfxShapedWord, to facilitate copying
+    // glyph data from shaped words into text runs as needed
+    typedef gfxShapedWord::CompressedGlyph    CompressedGlyph;
+    typedef gfxShapedWord::DetailedGlyph      DetailedGlyph;
+    typedef gfxShapedWord::DetailedGlyphStore DetailedGlyphStore;
+
+    // Override operator delete to properly free the object that was
+    // allocated via moz_malloc.
+    void operator delete(void* p) {
+        moz_free(p);
+    }
+
     virtual ~gfxTextRun();
 
     typedef gfxFont::RunMetrics Metrics;
 
     // Public textrun API for general use
 
     bool IsClusterStart(PRUint32 aPos) {
         NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
@@ -1483,16 +2202,33 @@ public:
             CompressedGlyph::FLAG_BREAK_TYPE_NORMAL;
     }
     bool CanHyphenateBefore(PRUint32 aPos) {
         NS_ASSERTION(aPos < mCharacterCount, "aPos out of range");
         return mCharacterGlyphs[aPos].CanBreakBefore() ==
             CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN;
     }
 
+    bool CharIsSpace(PRUint32 aPos) {
+        NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
+        return mCharacterGlyphs[aPos].CharIsSpace();
+    }
+    bool CharIsTab(PRUint32 aPos) {
+        NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
+        return mCharacterGlyphs[aPos].CharIsTab();
+    }
+    bool CharIsNewline(PRUint32 aPos) {
+        NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
+        return mCharacterGlyphs[aPos].CharIsNewline();
+    }
+    bool CharIsLowSurrogate(PRUint32 aPos) {
+        NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
+        return mCharacterGlyphs[aPos].CharIsLowSurrogate();
+    }
+
     PRUint32 GetLength() { return mCharacterCount; }
 
     // All PRUint32 aStart, PRUint32 aLength ranges below are restricted to
     // grapheme cluster boundaries! All offsets are in terms of the string
     // passed into MakeTextRun.
     
     // All coordinates are in layout/app units
 
@@ -1758,191 +2494,23 @@ public:
     void ClearFlagBits(PRUint32 aFlags) {
       NS_ASSERTION(!(aFlags & ~gfxTextRunFactory::SETTABLE_FLAGS),
                    "Only user flags should be mutable");
       mFlags &= ~aFlags;
     }
     const gfxSkipChars& GetSkipChars() const { return mSkipChars; }
     PRUint32 GetAppUnitsPerDevUnit() const { return mAppUnitsPerDevUnit; }
     gfxFontGroup *GetFontGroup() const { return mFontGroup; }
-    const PRUint8 *GetText8Bit() const
-    { return (mFlags & gfxTextRunFactory::TEXT_IS_8BIT) ? mText.mSingle : nsnull; }
-    const PRUnichar *GetTextUnicode() const
-    { return (mFlags & gfxTextRunFactory::TEXT_IS_8BIT) ? nsnull : mText.mDouble; }
-    const void *GetTextAt(PRUint32 aIndex) {
-        return (mFlags & gfxTextRunFactory::TEXT_IS_8BIT)
-            ? static_cast<const void *>(mText.mSingle + aIndex)
-            : static_cast<const void *>(mText.mDouble + aIndex);
-    }
-    const PRUnichar GetChar(PRUint32 i) const
-    { return (mFlags & gfxTextRunFactory::TEXT_IS_8BIT) ? mText.mSingle[i] : mText.mDouble[i]; }
-    PRUint32 GetHashCode() const { return mHashCode; }
-    void SetHashCode(PRUint32 aHash) { mHashCode = aHash; }
+
 
     // Call this, don't call "new gfxTextRun" directly. This does custom
     // allocation and initialization
     static gfxTextRun *Create(const gfxTextRunFactory::Parameters *aParams,
         const void *aText, PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags);
 
-    /**
-     * This class records the information associated with a character in the
-     * input string. It's optimized for the case where there is one glyph
-     * representing that character alone.
-     * 
-     * A character can have zero or more associated glyphs. Each glyph
-     * has an advance width and an x and y offset.
-     * A character may be the start of a cluster.
-     * A character may be the start of a ligature group.
-     * A character can be "missing", indicating that the system is unable
-     * to render the character.
-     * 
-     * All characters in a ligature group conceptually share all the glyphs
-     * associated with the characters in a group.
-     */
-    class CompressedGlyph {
-    public:
-        CompressedGlyph() { mValue = 0; }
-
-        enum {
-            // Indicates that a cluster and ligature group starts at this
-            // character; this character has a single glyph with a reasonable
-            // advance and zero offsets. A "reasonable" advance
-            // is one that fits in the available bits (currently 13) (specified
-            // in appunits).
-            FLAG_IS_SIMPLE_GLYPH  = 0x80000000U,
-
-            // Indicates whether a linebreak is allowed before this character;
-            // this is a two-bit field that holds a FLAG_BREAK_TYPE_xxx value
-            // indicating the kind of linebreak (if any) allowed here.
-            FLAGS_CAN_BREAK_BEFORE = 0x60000000U,
-
-            FLAGS_CAN_BREAK_SHIFT = 29,
-            FLAG_BREAK_TYPE_NONE   = 0,
-            FLAG_BREAK_TYPE_NORMAL = 1,
-            FLAG_BREAK_TYPE_HYPHEN = 2,
-
-            // The advance is stored in appunits
-            ADVANCE_MASK  = 0x1FFF0000U,
-            ADVANCE_SHIFT = 16,
-
-            GLYPH_MASK = 0x0000FFFFU,
-
-            // Non-simple glyphs may or may not have glyph data in the
-            // corresponding mDetailedGlyphs entry. They have the following
-            // flag bits:
-
-            // When NOT set, indicates that this character corresponds to a
-            // missing glyph and should be skipped (or possibly, render the character
-            // Unicode value in some special way). If there are glyphs,
-            // the mGlyphID is actually the UTF16 character code. The bit is
-            // inverted so we can memset the array to zero to indicate all missing.
-            FLAG_NOT_MISSING              = 0x01,
-            FLAG_NOT_CLUSTER_START        = 0x02,
-            FLAG_NOT_LIGATURE_GROUP_START = 0x04,
-            
-            GLYPH_COUNT_MASK = 0x00FFFF00U,
-            GLYPH_COUNT_SHIFT = 8
-        };
-
-        // "Simple glyphs" have a simple glyph ID, simple advance and their
-        // x and y offsets are zero. Also the glyph extents do not overflow
-        // the font-box defined by the font ascent, descent and glyph advance width.
-        // These case is optimized to avoid storing DetailedGlyphs.
-
-        // Returns true if the glyph ID aGlyph fits into the compressed representation
-        static bool IsSimpleGlyphID(PRUint32 aGlyph) {
-            return (aGlyph & GLYPH_MASK) == aGlyph;
-        }
-        // Returns true if the advance aAdvance fits into the compressed representation.
-        // aAdvance is in appunits.
-        static bool IsSimpleAdvance(PRUint32 aAdvance) {
-            return (aAdvance & (ADVANCE_MASK >> ADVANCE_SHIFT)) == aAdvance;
-        }
-
-        bool IsSimpleGlyph() const { return (mValue & FLAG_IS_SIMPLE_GLYPH) != 0; }
-        PRUint32 GetSimpleAdvance() const { return (mValue & ADVANCE_MASK) >> ADVANCE_SHIFT; }
-        PRUint32 GetSimpleGlyph() const { return mValue & GLYPH_MASK; }
-
-        bool IsMissing() const { return (mValue & (FLAG_NOT_MISSING|FLAG_IS_SIMPLE_GLYPH)) == 0; }
-        bool IsClusterStart() const {
-            return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_CLUSTER_START);
-        }
-        bool IsLigatureGroupStart() const {
-            return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_LIGATURE_GROUP_START);
-        }
-        bool IsLigatureContinuation() const {
-            return (mValue & FLAG_IS_SIMPLE_GLYPH) == 0 &&
-                (mValue & (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING)) ==
-                    (FLAG_NOT_LIGATURE_GROUP_START | FLAG_NOT_MISSING);
-        }
-
-        PRUint8 CanBreakBefore() const {
-            return (mValue & FLAGS_CAN_BREAK_BEFORE) >> FLAGS_CAN_BREAK_SHIFT;
-        }
-        // Returns FLAGS_CAN_BREAK_BEFORE if the setting changed, 0 otherwise
-        PRUint32 SetCanBreakBefore(PRUint8 aCanBreakBefore) {
-            NS_ASSERTION(aCanBreakBefore <= 2,
-                         "Bogus break-before value!");
-            PRUint32 breakMask = (PRUint32(aCanBreakBefore) << FLAGS_CAN_BREAK_SHIFT);
-            PRUint32 toggle = breakMask ^ (mValue & FLAGS_CAN_BREAK_BEFORE);
-            mValue ^= toggle;
-            return toggle;
-        }
-
-        CompressedGlyph& SetSimpleGlyph(PRUint32 aAdvanceAppUnits, PRUint32 aGlyph) {
-            NS_ASSERTION(IsSimpleAdvance(aAdvanceAppUnits), "Advance overflow");
-            NS_ASSERTION(IsSimpleGlyphID(aGlyph), "Glyph overflow");
-            mValue = (mValue & FLAGS_CAN_BREAK_BEFORE) |
-                FLAG_IS_SIMPLE_GLYPH |
-                (aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph;
-            return *this;
-        }
-        CompressedGlyph& SetComplex(bool aClusterStart, bool aLigatureStart,
-                PRUint32 aGlyphCount) {
-            mValue = (mValue & FLAGS_CAN_BREAK_BEFORE) |
-                FLAG_NOT_MISSING |
-                (aClusterStart ? 0 : FLAG_NOT_CLUSTER_START) |
-                (aLigatureStart ? 0 : FLAG_NOT_LIGATURE_GROUP_START) |
-                (aGlyphCount << GLYPH_COUNT_SHIFT);
-            return *this;
-        }
-        /**
-         * Missing glyphs are treated as ligature group starts; don't mess with
-         * the cluster-start flag (see bugs 618870 and 619286).
-         */
-        CompressedGlyph& SetMissing(PRUint32 aGlyphCount) {
-            mValue = (mValue & (FLAGS_CAN_BREAK_BEFORE | FLAG_NOT_CLUSTER_START)) |
-                (aGlyphCount << GLYPH_COUNT_SHIFT);
-            return *this;
-        }
-        PRUint32 GetGlyphCount() const {
-            NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph");
-            return (mValue & GLYPH_COUNT_MASK) >> GLYPH_COUNT_SHIFT;
-        }
-
-    private:
-        PRUint32 mValue;
-    };
-
-    /**
-     * When the glyphs for a character don't fit into a CompressedGlyph record
-     * in SimpleGlyph format, we use an array of DetailedGlyphs instead.
-     */
-    struct DetailedGlyph {
-        /** The glyphID, or the Unicode character
-         * if this is a missing glyph */
-        PRUint32 mGlyphID;
-        /** The advance, x-offset and y-offset of the glyph, in appunits
-         *  mAdvance is in the text direction (RTL or LTR)
-         *  mXOffset is always from left to right
-         *  mYOffset is always from top to bottom */   
-        PRInt32  mAdvance;
-        float    mXOffset, mYOffset;
-    };
-
     // The text is divided into GlyphRuns as necessary
     struct GlyphRun {
         nsRefPtr<gfxFont> mFont;   // never null
         PRUint32          mCharacterOffset; // into original UTF16 string
         PRUint8           mMatchType;
     };
 
     class THEBES_API GlyphRunIterator {
@@ -2001,47 +2569,90 @@ public:
     nsresult AddGlyphRun(gfxFont *aFont, PRUint8 aMatchType,
                          PRUint32 aStartCharIndex, bool aForceNewRun);
     void ResetGlyphRuns() { mGlyphRuns.Clear(); }
     void SortGlyphRuns();
     void SanitizeGlyphRuns();
 
     // Call the following glyph-setters during initialization or during reshaping
     // only. It is OK to overwrite existing data for a character.
+    void SetSimpleGlyph(PRUint32 aCharIndex, CompressedGlyph aGlyph) {
+        NS_ASSERTION(aGlyph.IsSimpleGlyph(), "Should be a simple glyph here");
+        mCharacterGlyphs[aCharIndex] = aGlyph;
+    }
     /**
      * Set the glyph data for a character. aGlyphs may be null if aGlyph is a
      * simple glyph or has no associated glyphs. If non-null the data is copied,
      * the caller retains ownership.
      */
-    void SetSimpleGlyph(PRUint32 aCharIndex, CompressedGlyph aGlyph) {
-        NS_ASSERTION(aGlyph.IsSimpleGlyph(), "Should be a simple glyph here");
-        if (mCharacterGlyphs) {
-            mCharacterGlyphs[aCharIndex] = aGlyph;
-        }
-    }
     void SetGlyphs(PRUint32 aCharIndex, CompressedGlyph aGlyph,
                    const DetailedGlyph *aGlyphs);
     void SetMissingGlyph(PRUint32 aCharIndex, PRUint32 aUnicodeChar);
     void SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIndex);
 
-    // If the character at aIndex is default-ignorable, set the glyph
-    // to be invisible-missing and return TRUE, else return FALSE
-    bool FilterIfIgnorable(PRUint32 aIndex);
+    // Set the glyph data for the given character index to the font's
+    // space glyph, IF this can be done as a "simple" glyph record
+    // (not requiring a DetailedGlyph entry). This avoids the need to call
+    // the font shaper and go through the shaped-word cache for most spaces.
+    //
+    // The parameter aSpaceChar is the original character code for which
+    // this space glyph is being used; if this is U+0020, we need to record
+    // that it could be trimmed at a run edge, whereas other kinds of space
+    // (currently just U+00A0) would not be trimmable/breakable.
+    //
+    // Returns true if it was able to set simple glyph data for the space;
+    // if it returns false, the caller needs to fall back to some other
+    // means to create the necessary (detailed) glyph data.
+    bool SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext,
+                               PRUint32 aCharIndex, PRUnichar aSpaceChar);
+
+    // Record the positions of specific characters that layout may need to
+    // detect in the textrun, even though it doesn't have an explicit copy
+    // of the original text. These are recorded using flag bits in the
+    // CompressedGlyph record; if necessary, we convert "simple" glyph records
+    // to "complex" ones as the Tab and Newline flags are not present in
+    // simple CompressedGlyph records.
+    void SetIsTab(PRUint32 aIndex) {
+        CompressedGlyph *g = &mCharacterGlyphs[aIndex];
+        if (g->IsSimpleGlyph()) {
+            DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
+            details->mGlyphID = g->GetSimpleGlyph();
+            details->mAdvance = g->GetSimpleAdvance();
+            details->mXOffset = details->mYOffset = 0;
+            SetGlyphs(aIndex, CompressedGlyph().SetComplex(true, true, 1), details);
+        }
+        g->SetIsTab();
+    }
+    void SetIsNewline(PRUint32 aIndex) {
+        CompressedGlyph *g = &mCharacterGlyphs[aIndex];
+        if (g->IsSimpleGlyph()) {
+            DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
+            details->mGlyphID = g->GetSimpleGlyph();
+            details->mAdvance = g->GetSimpleAdvance();
+            details->mXOffset = details->mYOffset = 0;
+            SetGlyphs(aIndex, CompressedGlyph().SetComplex(true, true, 1), details);
+        }
+        g->SetIsNewline();
+    }
+    void SetIsLowSurrogate(PRUint32 aIndex) {
+        SetGlyphs(aIndex, CompressedGlyph().SetComplex(false, false, 0), nsnull);
+        mCharacterGlyphs[aIndex].SetIsLowSurrogate();
+    }
 
     /**
      * Prefetch all the glyph extents needed to ensure that Measure calls
      * on this textrun not requesting tight boundingBoxes will succeed. Note
      * that some glyph extents might not be fetched due to OOM or other
      * errors.
      */
     void FetchGlyphExtents(gfxContext *aRefContext);
 
     // API for access to the raw glyph data, needed by gfxFont::Draw
     // and gfxFont::GetBoundingBox
-    const CompressedGlyph *GetCharacterGlyphs() { return mCharacterGlyphs; }
+    CompressedGlyph *GetCharacterGlyphs() { return mCharacterGlyphs; }
 
     // NOTE that this must not be called for a character offset that does
     // not have any DetailedGlyph records; callers must have verified that
     // mCharacterGlyphs[aCharIndex].GetGlyphCount() is greater than zero.
     DetailedGlyph *GetDetailedGlyphs(PRUint32 aCharIndex) {
         NS_ASSERTION(mDetailedGlyphs != nsnull &&
                      !mCharacterGlyphs[aCharIndex].IsSimpleGlyph() &&
                      mCharacterGlyphs[aCharIndex].GetGlyphCount() > 0,
@@ -2053,20 +2664,24 @@ public:
     PRUint32 CountMissingGlyphs();
     const GlyphRun *GetGlyphRuns(PRUint32 *aNumGlyphRuns) {
         *aNumGlyphRuns = mGlyphRuns.Length();
         return mGlyphRuns.Elements();
     }
     // Returns the index of the GlyphRun containing the given offset.
     // Returns mGlyphRuns.Length() when aOffset is mCharacterCount.
     PRUint32 FindFirstGlyphRunContaining(PRUint32 aOffset);
+
+    // Copy glyph data from a ShapedWord into this textrun.
+    void CopyGlyphDataFrom(const gfxShapedWord *aSource, PRUint32 aStart);
+
     // Copy glyph data for a range of characters from aSource to this
     // textrun.
-    virtual void CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart,
-                                   PRUint32 aLength, PRUint32 aDest);
+    void CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart,
+                           PRUint32 aLength, PRUint32 aDest);
 
     nsExpirationState *GetExpirationState() { return &mExpirationState; }
 
     struct LigatureData {
         // textrun offsets of the start and end of the containing ligature
         PRUint32 mLigatureStart;
         PRUint32 mLigatureEnd;
         // appunits advance to the start of the ligature part within the ligature;
@@ -2076,19 +2691,16 @@ public:
         // when the part is at the start of the ligature, and after-spacing
         // when the part is as the end of the ligature
         gfxFloat mPartWidth;
         
         bool mClipBeforePart;
         bool mClipAfterPart;
     };
     
-    // user font set generation when text run was created
-    PRUint64 GetUserFontSetGeneration() { return mUserFontSetGeneration; }
-
     // return storage used by this run, for memory reporter;
     // nsTransformedTextRun needs to override this as it holds additional data
     virtual NS_MUST_OVERRIDE size_t
         SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf);
     virtual NS_MUST_OVERRIDE size_t
         SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf);
 
     // Get the size, if it hasn't already been gotten, marking as it goes.
@@ -2099,49 +2711,41 @@ public:
         mFlags |= gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
         return SizeOfIncludingThis(aMallocSizeOf);
     }
     void ResetSizeOfAccountingFlags() {
         mFlags &= ~gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
     }
 
 #ifdef DEBUG
-    // number of entries referencing this textrun in the gfxTextRunWordCache
-    PRUint32 mCachedWords;
-    // generation of gfxTextRunWordCache that refers to this textrun;
-    // if the cache gets cleared, then mCachedWords is no longer meaningful
-    PRUint32 mCacheGeneration;
-    
     void Dump(FILE* aOutput);
 #endif
 
-    // post-process glyph advances to deal with synthetic bolding
-    void AdjustAdvancesForSyntheticBold(gfxContext *aContext,
-                                        PRUint32 aStart,
-                                        PRUint32 aLength);
-
 protected:
     /**
-     * Initializes the textrun to blank.
-     * @param aGlyphStorage preallocated array of CompressedGlyph[aLength]
-     * for the textrun to use; if aText is not persistent, then it has also
-     * been appended to this array, so it must NOT be freed separately.
+     * Create a textrun, and set its mCharacterGlyphs to point immediately
+     * after the base object; this is ONLY used in conjunction with placement
+     * new, after allocating a block large enough for the glyph records to
+     * follow the base textrun object.
      */
     gfxTextRun(const gfxTextRunFactory::Parameters *aParams, const void *aText,
-               PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags,
-               CompressedGlyph *aGlyphStorage);
+               PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags);
 
     /**
      * Helper for the Create() factory method to allocate the required
-     * glyph storage, and copy the text (modifying the aText parameter)
-     * if it is not flagged as persistent.
+     * glyph storage for a textrun object with the basic size aSize,
+     * plus room for aLength glyph records.
      */
-    static CompressedGlyph* AllocateStorage(const void*& aText,
-                                            PRUint32 aLength,
-                                            PRUint32 aFlags);
+    static void* AllocateStorageForTextRun(size_t aSize, PRUint32 aLength);
+
+    // All our glyph data is in logical order, not visual.
+    // Space for mCharacterGlyphs is allocated fused with the textrun object,
+    // and then the constructor sets the pointer to the beginning of this
+    // storage area. Thus, this pointer must NOT be freed!
+    CompressedGlyph  *mCharacterGlyphs;
 
 private:
     // **** general helpers **** 
 
     // Allocate aCount DetailedGlyphs for the given index
     DetailedGlyph *AllocateDetailedGlyphs(PRUint32 aCharIndex, PRUint32 aCount);
 
     // Get the total advance for a range of glyphs.
@@ -2189,166 +2793,29 @@ private:
                                  Metrics *aMetrics);
 
     // **** drawing helper ****
     void DrawGlyphs(gfxFont *aFont, gfxContext *aContext, bool aDrawToPath,
                     gfxPoint *aPt, PRUint32 aStart, PRUint32 aEnd,
                     PropertyProvider *aProvider,
                     PRUint32 aSpacingStart, PRUint32 aSpacingEnd);
 
-    // All our glyph data is in logical order, not visual.
-    // mCharacterGlyphs is allocated by the factory that creates the textrun,
-    // to avoid the possibility of failure during the constructor;
-    // however, ownership passes to the textrun during construction and so
-    // it must be deleted in the destructor.
-    CompressedGlyph*                               mCharacterGlyphs;
-
-    // For characters whose glyph data does not fit the "simple" glyph criteria
-    // in CompressedGlyph, we use a sorted array to store the association
-    // between the source character offset and an index into an array 
-    // DetailedGlyphs. The CompressedGlyph record includes a count of
-    // the number of DetailedGlyph records that belong to the character,
-    // starting at the given index.
-    class DetailedGlyphStore {
-    public:
-        DetailedGlyphStore()
-            : mLastUsed(0)
-        { }
-
-        // This is optimized for the most common calling patterns:
-        // we rarely need random access to the records, access is most commonly
-        // sequential through the textRun, so we record the last-used index
-        // and check whether the caller wants the same record again, or the
-        // next; if not, it's most likely we're starting over from the start
-        // of the run, so we check the first entry before resorting to binary
-        // search as a last resort.
-        // NOTE that this must not be called for a character offset that does
-        // not have any DetailedGlyph records; callers must have verified that
-        // mCharacterGlyphs[aOffset].GetGlyphCount() is greater than zero
-        // before calling this, otherwise the assertions here will fire (in a
-        // debug build), and we'll probably crash.
-        DetailedGlyph* Get(PRUint32 aOffset) {
-            NS_ASSERTION(mOffsetToIndex.Length() > 0,
-                         "no detailed glyph records!");
-            DetailedGlyph* details = mDetails.Elements();
-            // check common cases (fwd iteration, initial entry, etc) first
-            if (mLastUsed < mOffsetToIndex.Length() - 1 &&
-                aOffset == mOffsetToIndex[mLastUsed + 1].mOffset) {
-                ++mLastUsed;
-            } else if (aOffset == mOffsetToIndex[0].mOffset) {
-                mLastUsed = 0;
-            } else if (aOffset == mOffsetToIndex[mLastUsed].mOffset) {
-                // do nothing
-            } else if (mLastUsed > 0 &&
-                       aOffset == mOffsetToIndex[mLastUsed - 1].mOffset) {
-                --mLastUsed;
-            } else {
-                mLastUsed =
-                    mOffsetToIndex.BinaryIndexOf(aOffset, CompareToOffset());
-            }
-            NS_ASSERTION(mLastUsed != nsTArray<DGRec>::NoIndex,
-                         "detailed glyph record missing!");
-            return details + mOffsetToIndex[mLastUsed].mIndex;
-        }
-
-        DetailedGlyph* Allocate(PRUint32 aOffset, PRUint32 aCount) {
-            PRUint32 detailIndex = mDetails.Length();
-            DetailedGlyph *details = mDetails.AppendElements(aCount);
-            if (!details) {
-                return nsnull;
-            }
-            // We normally set up glyph records sequentially, so the common case
-            // here is to append new records to the mOffsetToIndex array;
-            // test for that before falling back to the InsertElementSorted
-            // method.
-            if (mOffsetToIndex.Length() == 0 ||
-                aOffset > mOffsetToIndex[mOffsetToIndex.Length() - 1].mOffset) {
-                if (!mOffsetToIndex.AppendElement(DGRec(aOffset, detailIndex))) {
-                    return nsnull;
-                }
-            } else {
-                if (!mOffsetToIndex.InsertElementSorted(DGRec(aOffset, detailIndex),
-                                                        CompareRecordOffsets())) {
-                    return nsnull;
-                }
-            }
-            return details;
-        }
-
-        size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) {
-            return aMallocSizeOf(this, sizeof(DetailedGlyphStore)) +
-                mDetails.SizeOfExcludingThis(aMallocSizeOf) +
-                mOffsetToIndex.SizeOfExcludingThis(aMallocSizeOf);
-        }
-
-    private:
-        struct DGRec {
-            DGRec(const PRUint32& aOffset, const PRUint32& aIndex)
-                : mOffset(aOffset), mIndex(aIndex) { }
-            PRUint32 mOffset; // source character offset in the textrun
-            PRUint32 mIndex;  // index where this char's DetailedGlyphs begin
-        };
-
-        struct CompareToOffset {
-            bool Equals(const DGRec& a, const PRUint32& b) const {
-                return a.mOffset == b;
-            }
-            bool LessThan(const DGRec& a, const PRUint32& b) const {
-                return a.mOffset < b;
-            }
-        };
-
-        struct CompareRecordOffsets {
-            bool Equals(const DGRec& a, const DGRec& b) const {
-                return a.mOffset == b.mOffset;
-            }
-            bool LessThan(const DGRec& a, const DGRec& b) const {
-                return a.mOffset < b.mOffset;
-            }
-        };
-
-        // Concatenated array of all the DetailedGlyph records needed for the
-        // textRun; individual character offsets are associated with indexes
-        // into this array via the mOffsetToIndex table.
-        nsTArray<DetailedGlyph>     mDetails;
-
-        // For each character offset that needs DetailedGlyphs, we record the
-        // index in mDetails where the list of glyphs begins. This array is
-        // sorted by mOffset.
-        nsTArray<DGRec>             mOffsetToIndex;
-
-        // Records the most recently used index into mOffsetToIndex, so that
-        // we can support sequential access more quickly than just doing
-        // a binary search each time.
-        nsTArray<DGRec>::index_type mLastUsed;
-    };
-
     nsAutoPtr<DetailedGlyphStore>   mDetailedGlyphs;
 
     // XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*,
     // for smaller size especially in the super-common one-glyphrun case
     nsAutoTArray<GlyphRun,1>        mGlyphRuns;
-    // When TEXT_IS_8BIT is set, we use mSingle, otherwise we use mDouble.
-    // When TEXT_IS_PERSISTENT is set, we don't own the text, otherwise we
-    // own the text. When we own the text, it's allocated fused with the
-    // mCharacterGlyphs array, and therefore need not be explicitly deleted.
-    // This text is not null-terminated.
-    union {
-        const PRUint8   *mSingle;
-        const PRUnichar *mDouble;
-    } mText;
+
     void             *mUserData;
     gfxFontGroup     *mFontGroup; // addrefed
     gfxSkipChars      mSkipChars;
     nsExpirationState mExpirationState;
     PRUint32          mAppUnitsPerDevUnit;
     PRUint32          mFlags;
     PRUint32          mCharacterCount;
-    PRUint32          mHashCode;
-    PRUint64          mUserFontSetGeneration; // user font set generation when text run created
 
     bool              mSkipDrawing; // true if the font group we used had a user font
                                     // download that's in progress, so we should hide text
                                     // until the download completes (or timeout fires)
 };
 
 class THEBES_API gfxFontGroup : public gfxTextRunFactory {
 public:
@@ -2380,31 +2847,21 @@ public:
             mStyle.Equals(other.mStyle);
     }
 
     const gfxFontStyle *GetStyle() const { return &mStyle; }
 
     virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle);
 
     /**
-     * The listed characters should not be passed in to MakeTextRun and should
-     * be treated as invisible and zero-width.
+     * The listed characters should be treated as invisible and zero-width
+     * when creating textruns.
      */
+    static bool IsInvalidChar(PRUint8 ch);
     static bool IsInvalidChar(PRUnichar ch);
-    
-    /**
-     * Make a textrun for an empty string. This is fast; if you call it,
-     * don't bother caching the result.
-     */
-    gfxTextRun *MakeEmptyTextRun(const Parameters *aParams, PRUint32 aFlags);
-    /**
-     * Make a textrun for a single ASCII space. This is fast; if you call it,
-     * don't bother caching the result.
-     */
-    gfxTextRun *MakeSpaceTextRun(const Parameters *aParams, PRUint32 aFlags);
 
     /**
      * Make a textrun for a given string.
      * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
      * textrun will copy it.
      * This calls FetchGlyphExtents on the textrun.
      */
     virtual gfxTextRun *MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
@@ -2413,16 +2870,32 @@ public:
      * Make a textrun for a given string.
      * If aText is not persistent (aFlags & TEXT_IS_PERSISTENT), the
      * textrun will copy it.
      * This calls FetchGlyphExtents on the textrun.
      */
     virtual gfxTextRun *MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
                                     const Parameters *aParams, PRUint32 aFlags);
 
+    /**
+     * Textrun creation helper for clients that don't want to pass
+     * a full Parameters record.
+     */
+    template<typename T>
+    gfxTextRun *MakeTextRun(const T *aString, PRUint32 aLength,
+                            gfxContext *aRefContext,
+                            PRUint32 aAppUnitsPerDevUnit,
+                            PRUint32 aFlags)
+    {
+        gfxTextRunFactory::Parameters params = {
+            aRefContext, nsnull, nsnull, nsnull, 0, aAppUnitsPerDevUnit
+        };
+        return MakeTextRun(aString, aLength, &params, aFlags);
+    }
+
     /* helper function for splitting font families on commas and
      * calling a function for each family to fill the mFonts array
      */
     typedef bool (*FontCreationCallback) (const nsAString& aName,
                                             const nsACString& aGenericName,
                                             bool aUseFontSet,
                                             void *closure);
     bool ForEachFont(const nsAString& aFamilies,
@@ -2456,18 +2929,19 @@ public:
                         gfxFont *aPrevMatchedFont,
                         PRUint8 *aMatchType);
 
     // search through pref fonts for a character, return nsnull if no matching pref font
     virtual already_AddRefed<gfxFont> WhichPrefFontSupportsChar(PRUint32 aCh);
 
     virtual already_AddRefed<gfxFont> WhichSystemFontSupportsChar(PRUint32 aCh);
 
+    template<typename T>
     void ComputeRanges(nsTArray<gfxTextRange>& mRanges,
-                       const PRUnichar *aString, PRUint32 begin, PRUint32 end,
+                       const T *aString, PRUint32 aLength,
                        PRInt32 aRunScript);
 
     gfxUserFontSet* GetUserFontSet();
 
     // With downloadable fonts, the composition of the font group can change as fonts are downloaded
     // for each change in state of the user font set, the generation value is bumped to avoid picking up
     // previously created text runs in the text run word cache.  For font groups based on stylesheets
     // with no @font-face rule, this always returns 0.
@@ -2496,41 +2970,51 @@ protected:
     eFontPrefLang           mLastPrefLang;       // lang group for last pref font
     eFontPrefLang           mPageLang;
     bool                    mLastPrefFirstFont;  // is this the first font in the list of pref fonts for this lang group?
 
     bool                    mSkipDrawing; // hide text while waiting for a font
                                           // download to complete (or fallback
                                           // timer to fire)
 
+    /**
+     * Textrun creation short-cuts for special cases where we don't need to
+     * call a font shaper to generate glyphs.
+     */
+    gfxTextRun *MakeEmptyTextRun(const Parameters *aParams, PRUint32 aFlags);
+    gfxTextRun *MakeSpaceTextRun(const Parameters *aParams, PRUint32 aFlags);
+    gfxTextRun *MakeBlankTextRun(const void* aText, PRUint32 aLength,
+                                 const Parameters *aParams, PRUint32 aFlags);
+
     // Used for construction/destruction.  Not intended to change the font set
     // as invalidation of font lists and caches is not considered.
     void SetUserFontSet(gfxUserFontSet *aUserFontSet);
 
     // Initialize the list of fonts
     void BuildFontList();
 
     // Init this font group's font metrics. If there no bad fonts, you don't need to call this.
     // But if there are one or more bad fonts which have bad underline offset,
     // you should call this with the *first* bad font.
     void InitMetricsForBadFont(gfxFont* aBadFont);
 
     // Set up the textrun glyphs for an entire text run:
     // find script runs, and then call InitScriptRun for each
+    template<typename T>
     void InitTextRun(gfxContext *aContext,
                      gfxTextRun *aTextRun,
-                     const PRUnichar *aString,
+                     const T *aString,
                      PRUint32 aLength);
 
     // InitTextRun helper to handle a single script run, by finding font ranges
     // and calling each font's InitTextRun() as appropriate
+    template<typename T>
     void InitScriptRun(gfxContext *aContext,
                        gfxTextRun *aTextRun,
-                       const PRUnichar *aString,
-                       PRUint32 aTotalLength,
+                       const T *aString,
                        PRUint32 aScriptRunStart,
                        PRUint32 aScriptRunEnd,
                        PRInt32 aRunScript);
 
     /* If aResolveGeneric is true, then CSS/Gecko generic family names are
      * replaced with preferred fonts.
      *
      * If aResolveFontName is true then fc() is called only for existing fonts
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -114,48 +114,43 @@ gfxGDIFont::CreatePlatformShaper()
 gfxFont*
 gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
 {
     return new gfxGDIFont(static_cast<GDIFontEntry*>(mFontEntry.get()),
                           &mStyle, mNeedsBold, anAAOption);
 }
 
 static bool
-UseUniscribe(gfxTextRun *aTextRun,
-             const PRUnichar *aString,
-             PRUint32 aRunStart,
-             PRUint32 aRunLength)
+UseUniscribe(gfxShapedWord *aShapedWord,
+             const PRUnichar *aString)
 {
-    PRUint32 flags = aTextRun->GetFlags();
+    PRUint32 flags = aShapedWord->Flags();
     bool useGDI;
 
     bool isXP = (gfxWindowsPlatform::WindowsOSVersion() 
                        < gfxWindowsPlatform::kWindowsVista);
 
     // bug 561304 - Uniscribe bug produces bad positioning at certain
     // font sizes on XP, so default to GDI on XP using logic of 3.6
 
     useGDI = isXP &&
              (flags &
                (gfxTextRunFactory::TEXT_OPTIMIZE_SPEED | 
                 gfxTextRunFactory::TEXT_IS_RTL)
              ) == gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
 
     return !useGDI ||
-        ScriptIsComplex(aString + aRunStart, aRunLength, SIC_COMPLEX) == S_OK;
+        ScriptIsComplex(aString, aShapedWord->Length(), SIC_COMPLEX) == S_OK;
 }
 
 bool
-gfxGDIFont::InitTextRun(gfxContext *aContext,
-                        gfxTextRun *aTextRun,
-                        const PRUnichar *aString,
-                        PRUint32 aRunStart,
-                        PRUint32 aRunLength,
-                        PRInt32 aRunScript,
-                        bool aPreferPlatformShaping)
+gfxGDIFont::ShapeWord(gfxContext *aContext,
+                      gfxShapedWord *aShapedWord,
+                      const PRUnichar *aString,
+                      bool aPreferPlatformShaping)
 {
     if (!mMetrics) {
         Initialize();
     }
     if (!mIsValid) {
         NS_WARNING("invalid font! expect incorrect text rendering");
         return false;
     }
@@ -163,81 +158,66 @@ gfxGDIFont::InitTextRun(gfxContext *aCon
     bool ok = false;
 
     // ensure the cairo font is set up, so there's no risk it'll fall back to
     // creating a "toy" font internally (see bug 544617)
     SetupCairoFont(aContext);
 
 #ifdef MOZ_GRAPHITE
     if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
-        ok = mGraphiteShaper->InitTextRun(aContext, aTextRun, aString,
-                                          aRunStart, aRunLength, 
-                                          aRunScript);
+        ok = mGraphiteShaper->ShapeWord(aContext, aShapedWord, aString);
     }
 #endif
 
     if (!ok && mHarfBuzzShaper) {
-        if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aRunScript)) {
-            ok = mHarfBuzzShaper->InitTextRun(aContext, aTextRun, aString,
-                                              aRunStart, aRunLength, 
-                                              aRunScript);
+        if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aShapedWord->Script())) {
+            ok = mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aString);
         }
     }
 
     if (!ok) {
         GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry());
         bool preferUniscribe =
             (!fe->IsTrueType() || fe->IsSymbolFont()) && !fe->mForceGDI;
 
-        if (preferUniscribe ||
-            UseUniscribe(aTextRun, aString, aRunStart, aRunLength))
-        {
+        if (preferUniscribe || UseUniscribe(aShapedWord, aString)) {
             // first try Uniscribe
             if (!mUniscribeShaper) {
                 mUniscribeShaper = new gfxUniscribeShaper(this);
             }
 
-            ok = mUniscribeShaper->InitTextRun(aContext, aTextRun, aString,
-                                               aRunStart, aRunLength, 
-                                               aRunScript);
+            ok = mUniscribeShaper->ShapeWord(aContext, aShapedWord, aString);
             if (ok) {
                 return true;
             }
 
             // fallback to GDI shaping
             if (!mPlatformShaper) {
                 CreatePlatformShaper();
             }
 
-            ok = mPlatformShaper->InitTextRun(aContext, aTextRun, aString,
-                                              aRunStart, aRunLength, 
-                                              aRunScript);
+            ok = mPlatformShaper->ShapeWord(aContext, aShapedWord, aString);
         } else {
             // first use GDI
             if (!mPlatformShaper) {
                 CreatePlatformShaper();
             }
 
-            ok = mPlatformShaper->InitTextRun(aContext, aTextRun, aString,
-                                              aRunStart, aRunLength, 
-                                              aRunScript);
-
+            ok = mPlatformShaper->ShapeWord(aContext, aShapedWord, aString);
             if (ok) {
                 return true;
             }
 
             // try Uniscribe if GDI failed
             if (!mUniscribeShaper) {
                 mUniscribeShaper = new gfxUniscribeShaper(this);
             }
 
             // use Uniscribe shaping
-            ok = mUniscribeShaper->InitTextRun(aContext, aTextRun, aString,
-                                               aRunStart, aRunLength, 
-                                               aRunScript);
+            ok = mUniscribeShaper->ShapeWord(aContext, aShapedWord, aString);
         }
 
 #if DEBUG
         if (!ok) {
             NS_ConvertUTF16toUTF8 name(GetName());
             char msg[256];
 
             sprintf(msg, 
--- a/gfx/thebes/gfxGDIFont.h
+++ b/gfx/thebes/gfxGDIFont.h
@@ -87,23 +87,20 @@ public:
 
     // get hinted glyph width in pixels as 16.16 fixed-point value
     virtual PRInt32 GetGlyphWidth(gfxContext *aCtx, PRUint16 aGID);
 
 protected:
     virtual void CreatePlatformShaper();
 
     /* override to check for uniscribe failure and fall back to GDI */
-    virtual bool InitTextRun(gfxContext *aContext,
-                               gfxTextRun *aTextRun,
-                               const PRUnichar *aString,
-                               PRUint32 aRunStart,
-                               PRUint32 aRunLength,
-                               PRInt32 aRunScript,
-                               bool aPreferPlatformShaping = false);
+    virtual bool ShapeWord(gfxContext *aContext,
+                           gfxShapedWord *aShapedWord,
+                           const PRUnichar *aString,
+                           bool aPreferPlatformShaping = false);
 
     void Initialize(); // creates metrics and Cairo fonts
 
     void FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize);
 
     // mPlatformShaper is used for the GDI shaper, mUniscribeShaper
     // for the Uniscribe version if needed
     nsAutoPtr<gfxFontShaper>   mUniscribeShaper;
--- a/gfx/thebes/gfxGDIShaper.cpp
+++ b/gfx/thebes/gfxGDIShaper.cpp
@@ -45,86 +45,84 @@
 
 /**********************************************************************
  *
  * class gfxGDIShaper
  *
  **********************************************************************/
 
 bool
-gfxGDIShaper::InitTextRun(gfxContext *aContext,
-                          gfxTextRun *aTextRun,
-                          const PRUnichar *aString,
-                          PRUint32 aRunStart,
-                          PRUint32 aRunLength,
-                          PRInt32 aRunScript)
+gfxGDIShaper::ShapeWord(gfxContext *aContext,
+                        gfxShapedWord *aShapedWord,
+                        const PRUnichar *aString)
 {
     DCFromContext dc(aContext);
     AutoSelectFont selectFont(dc, static_cast<gfxGDIFont*>(mFont)->GetHFONT());
 
+    PRUint32 length = aShapedWord->Length();
     nsAutoTArray<WORD,500> glyphArray;
-    if (!glyphArray.SetLength(aRunLength)) {
+    if (!glyphArray.SetLength(length)) {
         return false;
     }
     WORD *glyphs = glyphArray.Elements();
 
-    DWORD ret = ::GetGlyphIndicesW(dc, aString + aRunStart, aRunLength,
+    DWORD ret = ::GetGlyphIndicesW(dc, aString, length,
                                    glyphs, GGI_MARK_NONEXISTING_GLYPHS);
     if (ret == GDI_ERROR) {
         return false;
     }
 
-    for (int k = 0; k < aRunLength; k++) {
+    for (int k = 0; k < length; k++) {
         if (glyphs[k] == 0xFFFF)
             return false;
     }
  
     SIZE size;
     nsAutoTArray<int,500> partialWidthArray;
-    if (!partialWidthArray.SetLength(aRunLength)) {
+    if (!partialWidthArray.SetLength(length)) {
         return false;
     }
 
     BOOL success = ::GetTextExtentExPointI(dc,
                                            glyphs,
-                                           aRunLength,
+                                           length,
                                            INT_MAX,
                                            NULL,
                                            partialWidthArray.Elements(),
                                            &size);
     if (!success) {
         return false;
     }
 
     gfxTextRun::CompressedGlyph g;
     PRUint32 i;
     PRInt32 lastWidth = 0;
-    PRUint32 appUnitsPerDevPixel = aTextRun->GetAppUnitsPerDevUnit();
-    for (i = 0; i < aRunLength; ++i) {
-        PRUint32 offset = aRunStart + i;
+    PRUint32 appUnitsPerDevPixel = aShapedWord->AppUnitsPerDevUnit();
+    for (i = 0; i < length; ++i) {
+        PRUint32 offset = i;
         PRInt32 advancePixels = partialWidthArray[i] - lastWidth;
         lastWidth = partialWidthArray[i];
-        PRInt32 advanceAppUnits = advancePixels*appUnitsPerDevPixel;
+        PRInt32 advanceAppUnits = advancePixels * appUnitsPerDevPixel;
         WCHAR glyph = glyphs[i];
-        NS_ASSERTION(!gfxFontGroup::IsInvalidChar(aTextRun->GetChar(offset)),
+        NS_ASSERTION(!gfxFontGroup::IsInvalidChar(aShapedWord->GetCharAt(offset)),
                      "Invalid character detected!");
-        bool atClusterStart = aTextRun->IsClusterStart(offset);
+        bool atClusterStart = aShapedWord->IsClusterStart(offset);
         if (advanceAppUnits >= 0 &&
-            gfxTextRun::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) &&
-            gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph) &&
+            gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) &&
+            gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(glyph) &&
             atClusterStart)
         {
-            aTextRun->SetSimpleGlyph(offset,
-                                     g.SetSimpleGlyph(advanceAppUnits, glyph));
+            aShapedWord->SetSimpleGlyph(offset,
+                                        g.SetSimpleGlyph(advanceAppUnits, glyph));
         } else {
-            gfxTextRun::DetailedGlyph details;
+            gfxShapedWord::DetailedGlyph details;
             details.mGlyphID = glyph;
             details.mAdvance = advanceAppUnits;
             details.mXOffset = 0;
             details.mYOffset = 0;
-            aTextRun->SetGlyphs(offset,
-                                g.SetComplex(atClusterStart, true, 1),
-                                &details);
+            aShapedWord->SetGlyphs(offset,
+                                   g.SetComplex(atClusterStart, true, 1),
+                                   &details);
         }
     }
 
     return true;
 }
--- a/gfx/thebes/gfxGDIShaper.h
+++ b/gfx/thebes/gfxGDIShaper.h
@@ -49,17 +49,14 @@ public:
         MOZ_COUNT_CTOR(gfxGDIShaper);
     }
 
     virtual ~gfxGDIShaper()
     {
         MOZ_COUNT_DTOR(gfxGDIShaper);
     }
 
-    virtual bool InitTextRun(gfxContext *aContext,
-                               gfxTextRun *aTextRun,
-                               const PRUnichar *aString,
-                               PRUint32 aRunStart,
-                               PRUint32 aRunLength,
-                               PRInt32 aRunScript);
+    virtual bool ShapeWord(gfxContext *aContext,
+                           gfxShapedWord *aShapedWord,
+                           const PRUnichar *aString);
 };
 
 #endif /* GFX_GDISHAPER_H */
--- a/gfx/thebes/gfxGraphiteShaper.cpp
+++ b/gfx/thebes/gfxGraphiteShaper.cpp
@@ -156,22 +156,19 @@ MakeGraphiteLangTag(PRUint32 aTag)
     while ((grLangTag & mask) == ' ') {
         grLangTag &= ~mask;
         mask <<= 8;
     }
     return grLangTag;
 }
 
 bool
-gfxGraphiteShaper::InitTextRun(gfxContext *aContext,
-                               gfxTextRun *aTextRun,
-                               const PRUnichar *aString,
-                               PRUint32 aRunStart,
-                               PRUint32 aRunLength,
-                               PRInt32 aRunScript)
+gfxGraphiteShaper::ShapeWord(gfxContext      *aContext,
+                             gfxShapedWord   *aShapedWord,
+                             const PRUnichar *aText)
 {
     // some font back-ends require this in order to get proper hinted metrics
     mFont->SetupCairoFont(aContext);
 
     mCallbackData.mContext = aContext;
 
     if (!mGrFont) {
         mGrFace = gr_make_face(&mCallbackData, GrGetTable, gr_face_default);
@@ -185,73 +182,61 @@ gfxGraphiteShaper::InitTextRun(gfxContex
             gr_make_font(mFont->GetAdjustedSize(), mGrFace);
         if (!mGrFont) {
             gr_face_destroy(mGrFace);
             mGrFace = nsnull;
             return false;
         }
     }
 
-    const gfxFontStyle *style = aTextRun->GetFontGroup()->GetStyle();
+    gfxFontEntry *entry = mFont->GetFontEntry();
+    const gfxFontStyle *style = mFont->GetStyle();
     PRUint32 grLang = 0;
     if (style->languageOverride) {
         grLang = MakeGraphiteLangTag(style->languageOverride);
-    } else if (mFont->GetFontEntry()->mLanguageOverride) {
-        grLang = MakeGraphiteLangTag(mFont->GetFontEntry()->mLanguageOverride);
+    } else if (entry->mLanguageOverride) {
+        grLang = MakeGraphiteLangTag(entry->mLanguageOverride);
     } else {
         nsCAutoString langString;
         style->language->ToUTF8String(langString);
         grLang = GetGraphiteTagForLang(langString);
     }
     gr_feature_val *grFeatures = gr_face_featureval_for_lang(mGrFace, grLang);
 
-    bool disableLigatures =
-        (aTextRun->GetFlags() &
-         gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) != 0;
-    if (disableLigatures) {
+    if (aShapedWord->DisableLigatures()) {
         const gr_feature_ref* fref =
             gr_face_find_fref(mGrFace, TRUETYPE_TAG('l','i','g','a'));
         if (fref) {
             gr_fref_set_feature_value(fref, 0, grFeatures);
         }
     }
 
     const nsTArray<gfxFontFeature> *features = &style->featureSettings;
     if (features->IsEmpty()) {
-        features = &mFont->GetFontEntry()->mFeatureSettings;
+        features = &entry->mFeatureSettings;
     }
     for (PRUint32 i = 0; i < features->Length(); ++i) {
         const gr_feature_ref* fref =
             gr_face_find_fref(mGrFace, (*features)[i].mTag);
         if (fref) {
             gr_fref_set_feature_value(fref, (*features)[i].mValue, grFeatures);
         }
     }
 
-    const PRUnichar *textStart = aString + aRunStart;
-    const PRUnichar *textEnd = textStart + aRunLength;
-    const void *pError;
-    size_t nChars = gr_count_unicode_characters(gr_utf16,
-                                                textStart, textEnd,
-                                                &pError);
-    if (pError != nsnull) {
-        return false;
-    }
-
     gr_segment *seg = gr_make_seg(mGrFont, mGrFace, 0, grFeatures,
-                                  gr_utf16, textStart, nChars,
-                                  aTextRun->IsRightToLeft());
+                                  gr_utf16, aText, aShapedWord->Length(),
+                                  aShapedWord->IsRightToLeft());
     if (features) {
         gr_featureval_destroy(grFeatures);
     }
     if (!seg) {
         return false;
     }
 
-    nsresult rv = SetGlyphsFromSegment(aTextRun, aRunStart, aRunLength, seg);
+    nsresult rv = SetGlyphsFromSegment(aShapedWord, seg);
 
     gr_seg_destroy(seg);
 
     return NS_SUCCEEDED(rv);
 }
 
 #define SMALL_GLYPH_RUN 256 // avoid heap allocation of per-glyph data arrays
                             // for short (typical) runs up to this length
@@ -260,33 +245,31 @@ struct Cluster {
     PRUint32 baseChar;
     PRUint32 baseGlyph;
     PRUint32 nChars;
     PRUint32 nGlyphs;
     Cluster() : baseChar(0), baseGlyph(0), nChars(0), nGlyphs(0) { }
 };
 
 nsresult
-gfxGraphiteShaper::SetGlyphsFromSegment(gfxTextRun *aTextRun,
-                                        PRUint32 aRunStart,
-                                        PRUint32 aRunLength,
+gfxGraphiteShaper::SetGlyphsFromSegment(gfxShapedWord *aShapedWord,
                                         gr_segment *aSegment)
 {
-    PRInt32 dev2appUnits = aTextRun->GetAppUnitsPerDevUnit();
-    bool rtl = aTextRun->IsRightToLeft();
+    PRInt32 dev2appUnits = aShapedWord->AppUnitsPerDevUnit();
+    bool rtl = aShapedWord->IsRightToLeft();
 
     PRUint32 glyphCount = gr_seg_n_slots(aSegment);
 
     // identify clusters; graphite may have reordered/expanded/ligated glyphs.
     nsAutoTArray<Cluster,SMALL_GLYPH_RUN> clusters;
     nsAutoTArray<PRUint16,SMALL_GLYPH_RUN> gids;
     nsAutoTArray<float,SMALL_GLYPH_RUN> xLocs;
     nsAutoTArray<float,SMALL_GLYPH_RUN> yLocs;
 
-    if (!clusters.SetLength(aRunLength) ||
+    if (!clusters.SetLength(aShapedWord->Length()) ||
         !gids.SetLength(glyphCount) ||
         !xLocs.SetLength(glyphCount) ||
         !yLocs.SetLength(glyphCount))
     {
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // walk through the glyph slots and check which original character
@@ -312,27 +295,27 @@ gfxGraphiteShaper::SetGlyphsFromSegment(
             --cIndex;
         }
 
         // if there's a gap between the current cluster's base character and
         // this glyph's, extend the cluster to include the intervening chars
         if (gr_slot_can_insert_before(slot) && clusters[cIndex].nChars &&
             before >= clusters[cIndex].baseChar + clusters[cIndex].nChars)
         {
-            NS_ASSERTION(cIndex < aRunLength - 1, "cIndex at end of run");
+            NS_ASSERTION(cIndex < aShapedWord->Length() - 1, "cIndex at end of word");
             Cluster& c = clusters[cIndex + 1];
             c.baseChar = clusters[cIndex].baseChar + clusters[cIndex].nChars;
             c.nChars = before - c.baseChar;
             c.baseGlyph = gIndex;
             c.nGlyphs = 0;
             ++cIndex;
         }
 
         // increment cluster's glyph count to include current slot
-        NS_ASSERTION(cIndex < aRunLength, "cIndex beyond valid run length");
+        NS_ASSERTION(cIndex < aShapedWord->Length(), "cIndex beyond word length");
         ++clusters[cIndex].nGlyphs;
 
         // extend cluster if necessary to reach the glyph's "after" index
         if (clusters[cIndex].baseChar + clusters[cIndex].nChars < after + 1) {
             clusters[cIndex].nChars = after + 1 - clusters[cIndex].baseChar;
         }
     }
 
@@ -353,65 +336,64 @@ gfxGraphiteShaper::SetGlyphsFromSegment(
             } else {
                 adv = xLocs[clusters[i+1].baseGlyph] - xLocs[c.baseGlyph];
             }
         }
 
         // Check for default-ignorable char that didn't get filtered, combined,
         // etc by the shaping process, and skip it.
         PRUint32 offs = gr_cinfo_base(gr_seg_cinfo(aSegment, c.baseChar));
-        NS_ASSERTION(offs >= c.baseChar && offs < aRunLength,
+        NS_ASSERTION(offs >= c.baseChar && offs < aShapedWord->Length(),
                      "unexpected offset");
         if (c.nGlyphs == 1 && c.nChars == 1 &&
-            aTextRun->FilterIfIgnorable(aRunStart + offs))
+            aShapedWord->FilterIfIgnorable(offs))
         {
             continue;
         }
 
         PRUint32 appAdvance = adv * dev2appUnits;
         if (c.nGlyphs == 1 &&
-            gfxTextRun::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
-            gfxTextRun::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
+            gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
+            gfxShapedWord::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
             yLocs[c.baseGlyph] == 0)
         {
-            gfxTextRun::CompressedGlyph g;
-            aTextRun->SetSimpleGlyph(aRunStart + offs,
-                                     g.SetSimpleGlyph(appAdvance,
-                                                      gids[c.baseGlyph]));
+            gfxShapedWord::CompressedGlyph g;
+            aShapedWord->SetSimpleGlyph(offs,
+                                        g.SetSimpleGlyph(appAdvance,
+                                                         gids[c.baseGlyph]));
         } else {
             // not a one-to-one mapping with simple metrics: use DetailedGlyph
-            nsAutoTArray<gfxTextRun::DetailedGlyph,8> details;
+            nsAutoTArray<gfxShapedWord::DetailedGlyph,8> details;
             float clusterLoc;
             for (PRUint32 j = c.baseGlyph; j < c.baseGlyph + c.nGlyphs; ++j) {
-                gfxTextRun::DetailedGlyph* d = details.AppendElement();
+                gfxShapedWord::DetailedGlyph* d = details.AppendElement();
                 d->mGlyphID = gids[j];
                 d->mYOffset = -yLocs[j] * dev2appUnits;
                 if (j == c.baseGlyph) {
                     d->mXOffset = 0;
                     d->mAdvance = appAdvance;
                     clusterLoc = xLocs[j];
                 } else {
                     d->mXOffset = (xLocs[j] - clusterLoc - adv) * dev2appUnits;
                     d->mAdvance = 0;
                 }
             }
-            gfxTextRun::CompressedGlyph g;
-            g.SetComplex(aTextRun->IsClusterStart(aRunStart + offs),
+            gfxShapedWord::CompressedGlyph g;
+            g.SetComplex(aShapedWord->IsClusterStart(offs),
                          true, details.Length());
-            aTextRun->SetGlyphs(aRunStart + offs, g, details.Elements());
+            aShapedWord->SetGlyphs(offs, g, details.Elements());
         }
 
         for (PRUint32 j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) {
             offs = gr_cinfo_base(gr_seg_cinfo(aSegment, j));
-            NS_ASSERTION(offs >= j && offs < aRunLength,
+            NS_ASSERTION(offs >= j && offs < aShapedWord->Length(),
                          "unexpected offset");
-            gfxTextRun::CompressedGlyph g;
-            g.SetComplex(aTextRun->IsClusterStart(aRunStart + offs),
-                         false, 0);
-            aTextRun->SetGlyphs(aRunStart + offs, g, nsnull);
+            gfxShapedWord::CompressedGlyph g;
+            g.SetComplex(aShapedWord->IsClusterStart(offs), false, 0);
+            aShapedWord->SetGlyphs(offs, g, nsnull);
         }
     }
 
     return NS_OK;
 }
 
 // for language tag validation - include list of tags from the IANA registry
 #include "gfxLanguageTagList.cpp"
--- a/gfx/thebes/gfxGraphiteShaper.h
+++ b/gfx/thebes/gfxGraphiteShaper.h
@@ -47,22 +47,19 @@ class gr_face;
 class gr_font;
 class gr_segment;
 
 class gfxGraphiteShaper : public gfxFontShaper {
 public:
     gfxGraphiteShaper(gfxFont *aFont);
     virtual ~gfxGraphiteShaper();
 
-    virtual bool InitTextRun(gfxContext *aContext,
-                               gfxTextRun *aTextRun,
-                               const PRUnichar *aString,
-                               PRUint32 aRunStart,
-                               PRUint32 aRunLength,
-                               PRInt32 aRunScript);
+    virtual bool ShapeWord(gfxContext *aContext,
+                           gfxShapedWord *aShapedWord,
+                           const PRUnichar *aText);
 
     const void* GetTable(PRUint32 aTag, size_t *aLength);
 
     static void Shutdown();
 
     struct CallbackData {
         gfxFont           *mFont;
         gfxGraphiteShaper *mShaper;
@@ -71,19 +68,17 @@ public:
 
     struct TableRec {
         hb_blob_t  *mBlob;
         const void *mData;
         PRUint32    mLength;
     };
 
 protected:
-    nsresult SetGlyphsFromSegment(gfxTextRun *aTextRun,
-                                  PRUint32 aRunStart,
-                                  PRUint32 aRunLength,
+    nsresult SetGlyphsFromSegment(gfxShapedWord *aShapedWord,
                                   gr_segment *aSegment);
 
     gr_face *mGrFace;
     gr_font *mGrFont;
 
     CallbackData mCallbackData;
 
     nsDataHashtable<nsUint32HashKey,TableRec> mTables;
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -708,22 +708,19 @@ HBGetEastAsianWidth(hb_codepoint_t aCh)
 /*
  * gfxFontShaper override to initialize the text run using HarfBuzz
  */
 
 static hb_font_funcs_t * sHBFontFuncs = nsnull;
 static hb_unicode_funcs_t * sHBUnicodeFuncs = nsnull;
 
 bool
-gfxHarfBuzzShaper::InitTextRun(gfxContext *aContext,
-                               gfxTextRun *aTextRun,
-                               const PRUnichar *aString,
-                               PRUint32 aRunStart,
-                               PRUint32 aRunLength,
-                               PRInt32 aRunScript)
+gfxHarfBuzzShaper::ShapeWord(gfxContext      *aContext,
+                             gfxShapedWord   *aShapedWord,
+                             const PRUnichar *aText)
 {
     // some font back-ends require this in order to get proper hinted metrics
     mFont->SetupCairoFont(aContext);
 
     if (!mHBFace) {
 
         mUseFontGlyphWidths = mFont->ProvidesGlyphWidths();
 
@@ -813,92 +810,87 @@ gfxHarfBuzzShaper::InitTextRun(gfxContex
 
     FontCallbackData fcd(this, aContext);
     hb_font_t *font = hb_font_create();
     hb_font_set_funcs(font, sHBFontFuncs, nsnull, &fcd);
     hb_font_set_ppem(font, mFont->GetAdjustedSize(), mFont->GetAdjustedSize());
     PRUint32 scale = FloatToFixed(mFont->GetAdjustedSize()); // 16.16 fixed-point
     hb_font_set_scale(font, scale, scale);
 
-    // aRunStart and aRunLength define the section of the textRun and of
-    // aString that is to be drawn with this particular font
-
-    bool disableLigatures =
-        (aTextRun->GetFlags() &
-         gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) != 0;
-
     nsAutoTArray<hb_feature_t,20> features;
 
     // Ligature features are enabled by default in the generic shaper,
     // so we explicitly turn them off if necessary (for letter-spacing)
-    if (disableLigatures) {
+    if (aShapedWord->DisableLigatures()) {
         hb_feature_t ligaOff = { HB_TAG('l','i','g','a'), 0, 0, UINT_MAX };
         hb_feature_t cligOff = { HB_TAG('c','l','i','g'), 0, 0, UINT_MAX };
         features.AppendElement(ligaOff);
         features.AppendElement(cligOff);
     }
 
     // css features need to be merged with the existing ones, if any
-    const gfxFontStyle *style = aTextRun->GetFontGroup()->GetStyle();
+    gfxFontEntry *entry = mFont->GetFontEntry();
+    const gfxFontStyle *style = mFont->GetStyle();
     const nsTArray<gfxFontFeature> *cssFeatures = &style->featureSettings;
     if (cssFeatures->IsEmpty()) {
-        cssFeatures = &mFont->GetFontEntry()->mFeatureSettings;
+        cssFeatures = &entry->mFeatureSettings;
     }
     for (PRUint32 i = 0; i < cssFeatures->Length(); ++i) {
         PRUint32 j;
         for (j = 0; j < features.Length(); ++j) {
             if (cssFeatures->ElementAt(i).mTag == features[j].tag) {
                 features[j].value = cssFeatures->ElementAt(i).mValue;
                 break;
             }
         }