merge backout
authorJonathan Kew <jfkthame@gmail.com>
Wed, 04 May 2011 15:31:57 +0100
changeset 68956 9fa7473f00b8526c5c5211d53a1e1e830d30ccdb
parent 68954 79497dd8d244531a144b96251c5a30fad233e040 (diff)
parent 68955 0779fb4183b93dbb6fa61be801020ac809aba16e (current diff)
child 68958 0179ee37bb48a77210bd74fc9e9059da144ed584
push idunknown
push userunknown
push dateunknown
milestone6.0a1
merge backout
intl/Makefile.in
intl/locales/Makefile.in
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2683,16 +2683,21 @@ function BrowserOnClick(event) {
       }
       else if (ot == errorDoc.getElementById('ignoreWarningButton')) {
         // Allow users to override and continue through to the site,
         // but add a notify bar as a reminder, so that they don't lose
         // track after, e.g., tab switching.
         gBrowser.loadURIWithFlags(content.location.href,
                                   nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
                                   null, null, null);
+
+        Services.perms.add(makeURI(content.location.href), "safe-browsing",
+                           Ci.nsIPermissionManager.ALLOW_ACTION,
+                           Ci.nsIPermissionManager.EXPIRE_SESSION);
+
         let buttons = [{
           label: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.label"),
           accessKey: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.accessKey"),
           callback: function() { getMeOutOfHere(); }
         }];
 
         let title;
         if (isMalware) {
--- a/content/base/public/nsLineBreaker.h
+++ b/content/base/public/nsLineBreaker.h
@@ -38,30 +38,35 @@
 #ifndef NSLINEBREAKER_H_
 #define NSLINEBREAKER_H_
 
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsILineBreaker.h"
 
 class nsIAtom;
+class nsHyphenator;
 
 /**
  * A receiver of line break data.
  */
 class nsILineBreakSink {
 public:
   /**
    * Sets the break data for a substring of the associated text chunk.
    * One or more of these calls will be performed; the union of all substrings
    * will cover the entire text chunk. Substrings may overlap (i.e., we may
    * set the break-before state of a character more than once).
    * @param aBreakBefore the break-before states for the characters in the substring.
+   * These are enum values from gfxTextRun::CompressedGlyph:
+   *    FLAG_BREAK_TYPE_NONE     - no linebreak is allowed here
+   *    FLAG_BREAK_TYPE_NORMAL   - a normal (whitespace) linebreak
+   *    FLAG_BREAK_TYPE_HYPHEN   - a hyphenation point
    */
-  virtual void SetBreaks(PRUint32 aStart, PRUint32 aLength, PRPackedBool* aBreakBefore) = 0;
+  virtual void SetBreaks(PRUint32 aStart, PRUint32 aLength, PRUint8* aBreakBefore) = 0;
   
   /**
    * Indicates which characters should be capitalized. Only called if
    * BREAK_NEED_CAPITALIZATION was requested.
    */
   virtual void SetCapitalization(PRUint32 aStart, PRUint32 aLength, PRPackedBool* aCapitalize) = 0;
 };
 
@@ -148,17 +153,22 @@ public:
      * at the end of textruns in case context is needed for following breakable
      * text.
      */
     BREAK_SKIP_SETTING_NO_BREAKS = 0x04,
     /**
      * We need to be notified of characters that should be capitalized
      * (as in text-transform:capitalize) in this chunk of text.
      */
-    BREAK_NEED_CAPITALIZATION = 0x08
+    BREAK_NEED_CAPITALIZATION = 0x08,
+    /**
+     * Auto-hyphenation is enabled, so we need to get a hyphenator
+     * (if available) and use it to find breakpoints.
+     */
+    BREAK_USE_AUTO_HYPHENATION = 0x10
   };
 
   /**
    * Append "invisible whitespace". This acts like whitespace, but there is
    * no actual text associated with it. Only the BREAK_SUPPRESS_INSIDE flag
    * is relevant here.
    */
   nsresult AppendInvisibleWhitespace(PRUint32 aFlags);
@@ -209,19 +219,28 @@ private:
   // State for the nonwhitespace "word" that started in previous text and hasn't
   // finished yet.
 
   // When the current word ends, this computes the linebreak opportunities
   // *inside* the word (excluding either end) and sets them through the
   // appropriate sink(s). Then we clear the current word state.
   nsresult FlushCurrentWord();
 
+  void UpdateCurrentWordLangGroup(nsIAtom *aLangGroup);
+
+  void FindHyphenationPoints(nsHyphenator *aHyphenator,
+                             const PRUnichar *aTextStart,
+                             const PRUnichar *aTextLimit,
+                             PRPackedBool *aBreakState);
+
   nsAutoTArray<PRUnichar,100> mCurrentWord;
   // All the items that contribute to mCurrentWord
   nsAutoTArray<TextItem,2>    mTextItems;
+  nsIAtom*                    mCurrentWordLangGroup;
+  PRPackedBool                mCurrentWordContainsMixedLang;
   PRPackedBool                mCurrentWordContainsComplexChar;
 
   // True if the previous character was breakable whitespace
   PRPackedBool                mAfterBreakableSpace;
   // True if a break must be allowed at the current position because
   // a run of breakable whitespace ends here
   PRPackedBool                mBreakHere;
 };
--- a/content/base/src/nsLineBreaker.cpp
+++ b/content/base/src/nsLineBreaker.cpp
@@ -34,19 +34,24 @@
  * 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 "nsLineBreaker.h"
 #include "nsContentUtils.h"
 #include "nsILineBreaker.h"
+#include "gfxFont.h" // for the gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_* values
+#include "nsHyphenationManager.h"
+#include "nsHyphenator.h"
 
 nsLineBreaker::nsLineBreaker()
-  : mCurrentWordContainsComplexChar(PR_FALSE),
+  : mCurrentWordLangGroup(nsnull),
+    mCurrentWordContainsMixedLang(PR_FALSE),
+    mCurrentWordContainsComplexChar(PR_FALSE),
     mAfterBreakableSpace(PR_FALSE), mBreakHere(PR_FALSE)
 {
 }
 
 nsLineBreaker::~nsLineBreaker()
 {
   NS_ASSERTION(mCurrentWord.Length() == 0, "Should have Reset() before destruction!");
 }
@@ -69,43 +74,65 @@ SetupCapitalization(const PRUnichar* aWo
     }
   }
 }
 
 nsresult
 nsLineBreaker::FlushCurrentWord()
 {
   PRUint32 length = mCurrentWord.Length();
-  nsAutoTArray<PRPackedBool,4000> breakState;
+  nsAutoTArray<PRUint8,4000> breakState;
   if (!breakState.AppendElements(length))
     return NS_ERROR_OUT_OF_MEMORY;
   
   nsTArray<PRPackedBool> capitalizationState;
 
   if (!mCurrentWordContainsComplexChar) {
     // Just set everything internal to "no break"!
-    memset(breakState.Elements(), PR_FALSE, length*sizeof(PRPackedBool));
+    memset(breakState.Elements(),
+           gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE,
+           length*sizeof(PRUint8));
   } else {
     nsContentUtils::LineBreaker()->
       GetJISx4051Breaks(mCurrentWord.Elements(), length, breakState.Elements());
   }
 
+  PRBool autoHyphenate = mCurrentWordLangGroup &&
+    !mCurrentWordContainsMixedLang;
   PRUint32 i;
+  for (i = 0; autoHyphenate && i < mTextItems.Length(); ++i) {
+    TextItem* ti = &mTextItems[i];
+    if (!(ti->mFlags & BREAK_USE_AUTO_HYPHENATION)) {
+      autoHyphenate = PR_FALSE;
+    }
+  }
+  if (autoHyphenate) {
+    nsRefPtr<nsHyphenator> hyphenator =
+      nsHyphenationManager::Instance()->GetHyphenator(mCurrentWordLangGroup);
+    if (hyphenator) {
+      FindHyphenationPoints(hyphenator,
+                            mCurrentWord.Elements(),
+                            mCurrentWord.Elements() + length,
+                            breakState.Elements());
+    }
+  }
+
   PRUint32 offset = 0;
   for (i = 0; i < mTextItems.Length(); ++i) {
     TextItem* ti = &mTextItems[i];
     NS_ASSERTION(ti->mLength > 0, "Zero length word contribution?");
 
     if ((ti->mFlags & BREAK_SUPPRESS_INITIAL) && ti->mSinkOffset == 0) {
-      breakState[offset] = PR_FALSE;
+      breakState[offset] = gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE;
     }
     if (ti->mFlags & BREAK_SUPPRESS_INSIDE) {
       PRUint32 exclude = ti->mSinkOffset == 0 ? 1 : 0;
-      memset(breakState.Elements() + offset + exclude, PR_FALSE,
-             (ti->mLength - exclude)*sizeof(PRPackedBool));
+      memset(breakState.Elements() + offset + exclude,
+             gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE,
+             (ti->mLength - exclude)*sizeof(PRUint8));
     }
 
     // Don't set the break state for the first character of the word, because
     // it was already set correctly earlier and we don't know what the true
     // value should be.
     PRUint32 skipSet = i == 0 ? 1 : 0;
     if (ti->mSink) {
       ti->mSink->SetBreaks(ti->mSinkOffset + skipSet, ti->mLength - skipSet,
@@ -125,16 +152,18 @@ nsLineBreaker::FlushCurrentWord()
     }
     
     offset += ti->mLength;
   }
 
   mCurrentWord.Clear();
   mTextItems.Clear();
   mCurrentWordContainsComplexChar = PR_FALSE;
+  mCurrentWordContainsMixedLang = PR_FALSE;
+  mCurrentWordLangGroup = nsnull;
   return NS_OK;
 }
 
 nsresult
 nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32 aLength,
                           PRUint32 aFlags, nsILineBreakSink* aSink)
 {
   NS_ASSERTION(aLength > 0, "Appending empty text...");
@@ -145,33 +174,34 @@ nsLineBreaker::AppendText(nsIAtom* aLang
   if (mCurrentWord.Length() > 0) {
     NS_ASSERTION(!mAfterBreakableSpace && !mBreakHere, "These should not be set");
 
     while (offset < aLength && !IsSpace(aText[offset])) {
       mCurrentWord.AppendElement(aText[offset]);
       if (!mCurrentWordContainsComplexChar && IsComplexChar(aText[offset])) {
         mCurrentWordContainsComplexChar = PR_TRUE;
       }
+      UpdateCurrentWordLangGroup(aLangGroup);
       ++offset;
     }
 
     if (offset > 0) {
       mTextItems.AppendElement(TextItem(aSink, 0, offset, aFlags));
     }
 
     if (offset == aLength)
       return NS_OK;
 
     // We encountered whitespace, so we're done with this word
     nsresult rv = FlushCurrentWord();
     if (NS_FAILED(rv))
       return rv;
   }
 
-  nsAutoTArray<PRPackedBool,4000> breakState;
+  nsAutoTArray<PRUint8,4000> breakState;
   if (aSink) {
     if (!breakState.AppendElements(aLength))
       return NS_ERROR_OUT_OF_MEMORY;
   }
   
   nsTArray<PRPackedBool> capitalizationState;
   if (aSink && (aFlags & BREAK_NEED_CAPITALIZATION)) {
     if (!capitalizationState.AppendElements(aLength))
@@ -193,37 +223,52 @@ nsLineBreaker::AppendText(nsIAtom* aLang
       --offset;
       if (IsSpace(aText[offset]))
         break;
     }
   }
   PRUint32 wordStart = offset;
   PRBool wordHasComplexChar = PR_FALSE;
 
+  nsRefPtr<nsHyphenator> hyphenator;
+  if ((aFlags & BREAK_USE_AUTO_HYPHENATION) && !(aFlags & BREAK_SUPPRESS_INSIDE)) {
+    hyphenator = nsHyphenationManager::Instance()->GetHyphenator(aLangGroup);
+  }
+
   for (;;) {
     PRUnichar ch = aText[offset];
     PRBool isSpace = IsSpace(ch);
     PRBool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE);
 
     if (aSink) {
-      breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace);
+      breakState[offset] =
+        mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) ?
+          gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL :
+          gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE;
     }
     mBreakHere = PR_FALSE;
     mAfterBreakableSpace = isBreakableSpace;
 
     if (isSpace) {
       if (offset > wordStart && aSink) {
-        if (wordHasComplexChar && !(aFlags & BREAK_SUPPRESS_INSIDE)) {
-          // Save current start-of-word state because GetJISx4051Breaks will
-          // set it to false
-          PRPackedBool currentStart = breakState[wordStart];
-          nsContentUtils::LineBreaker()->
-            GetJISx4051Breaks(aText + wordStart, offset - wordStart,
-                              breakState.Elements() + wordStart);
-          breakState[wordStart] = currentStart;
+        if (!(aFlags & BREAK_SUPPRESS_INSIDE)) {
+          if (wordHasComplexChar) {
+            // Save current start-of-word state because GetJISx4051Breaks will
+            // set it to false
+            PRUint8 currentStart = breakState[wordStart];
+            nsContentUtils::LineBreaker()->
+              GetJISx4051Breaks(aText + wordStart, offset - wordStart,
+                                breakState.Elements() + wordStart);
+            breakState[wordStart] = currentStart;
+          }
+          if (hyphenator) {
+            FindHyphenationPoints(hyphenator,
+                                  aText + wordStart, aText + offset,
+                                  breakState.Elements() + wordStart);
+          }
         }
         if (aFlags & BREAK_NEED_CAPITALIZATION) {
           SetupCapitalization(aText + wordStart, offset - wordStart,
                               capitalizationState.Elements() + wordStart);
         }
       }
       wordHasComplexChar = PR_FALSE;
       ++offset;
@@ -241,43 +286,62 @@ nsLineBreaker::AppendText(nsIAtom* aLang
         PRUint32 len = offset - wordStart;
         PRUnichar* elems = mCurrentWord.AppendElements(len);
         if (!elems)
           return NS_ERROR_OUT_OF_MEMORY;
         memcpy(elems, aText + wordStart, sizeof(PRUnichar)*len);
         mTextItems.AppendElement(TextItem(aSink, wordStart, len, aFlags));
         // Ensure that the break-before for this word is written out
         offset = wordStart + 1;
+        UpdateCurrentWordLangGroup(aLangGroup);
         break;
       }
     }
   }
 
   if (!noBreaksNeeded) {
     // aSink must not be null
     aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
     if (aFlags & BREAK_NEED_CAPITALIZATION) {
       aSink->SetCapitalization(start, offset - start,
                                capitalizationState.Elements() + start);
     }
   }
   return NS_OK;
 }
 
+void
+nsLineBreaker::FindHyphenationPoints(nsHyphenator *aHyphenator,
+                                     const PRUnichar *aTextStart,
+                                     const PRUnichar *aTextLimit,
+                                     PRPackedBool *aBreakState)
+{
+  nsDependentSubstring string(aTextStart, aTextLimit);
+  nsAutoTArray<PRPackedBool,200> hyphens;
+  if (NS_SUCCEEDED(aHyphenator->Hyphenate(string, hyphens))) {
+    for (PRUint32 i = 0; i + 1 < string.Length(); ++i) {
+      if (hyphens[i]) {
+        aBreakState[i + 1] =
+          gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN;
+      }
+    }
+  }
+}
+
 nsresult
 nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aLength,
                           PRUint32 aFlags, nsILineBreakSink* aSink)
 {
   NS_ASSERTION(aLength > 0, "Appending empty text...");
 
-  if (aFlags & BREAK_NEED_CAPITALIZATION) {
-    // Defer to the Unicode path if capitalization is required
+  if (aFlags & (BREAK_NEED_CAPITALIZATION | BREAK_USE_AUTO_HYPHENATION)) {
+    // Defer to the Unicode path if capitalization or hyphenation is required
     nsAutoString str;
-    CopyASCIItoUTF16(nsDependentCString(reinterpret_cast<const char*>(aText), aLength),
-                     str);
+    const char* cp = reinterpret_cast<const char*>(aText);
+    CopyASCIItoUTF16(nsDependentCSubstring(cp, cp + aLength), str);
     return AppendText(aLangGroup, str.get(), aLength, aFlags, aSink);
   }
 
   PRUint32 offset = 0;
 
   // Continue the current word
   if (mCurrentWord.Length() > 0) {
     NS_ASSERTION(!mAfterBreakableSpace && !mBreakHere, "These should not be set");
@@ -301,17 +365,17 @@ nsLineBreaker::AppendText(nsIAtom* aLang
     }
 
     // We encountered whitespace, so we're done with this word
     nsresult rv = FlushCurrentWord();
     if (NS_FAILED(rv))
       return rv;
   }
 
-  nsAutoTArray<PRPackedBool,4000> breakState;
+  nsAutoTArray<PRUint8,4000> breakState;
   if (aSink) {
     if (!breakState.AppendElements(aLength))
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
   PRUint32 start = offset;
   PRBool noBreaksNeeded = !aSink ||
     (aFlags == (BREAK_SUPPRESS_INITIAL | BREAK_SUPPRESS_INSIDE | BREAK_SKIP_SETTING_NO_BREAKS) &&
@@ -332,17 +396,20 @@ nsLineBreaker::AppendText(nsIAtom* aLang
   PRBool wordHasComplexChar = PR_FALSE;
 
   for (;;) {
     PRUint8 ch = aText[offset];
     PRBool isSpace = IsSpace(ch);
     PRBool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE);
 
     if (aSink) {
-      breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace);
+      breakState[offset] =
+        mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) ?
+          gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL :
+          gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE;
     }
     mBreakHere = PR_FALSE;
     mAfterBreakableSpace = isBreakableSpace;
 
     if (isSpace) {
       if (offset > wordStart && wordHasComplexChar) {
         if (aSink && !(aFlags & BREAK_SUPPRESS_INSIDE)) {
           // Save current start-of-word state because GetJISx4051Breaks will
@@ -385,16 +452,26 @@ nsLineBreaker::AppendText(nsIAtom* aLang
   }
 
   if (!noBreaksNeeded) {
     aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
   }
   return NS_OK;
 }
 
+void
+nsLineBreaker::UpdateCurrentWordLangGroup(nsIAtom *aLangGroup)
+{
+  if (mCurrentWordLangGroup && mCurrentWordLangGroup != aLangGroup) {
+    mCurrentWordContainsMixedLang = PR_TRUE;
+  } else {
+    mCurrentWordLangGroup = aLangGroup;
+  }
+}
+
 nsresult
 nsLineBreaker::AppendInvisibleWhitespace(PRUint32 aFlags)
 {
   nsresult rv = FlushCurrentWord();
   if (NS_FAILED(rv))
     return rv;
 
   PRBool isBreakableSpace = !(aFlags & BREAK_SUPPRESS_INSIDE);
--- a/gfx/src/nsFontMetrics.cpp
+++ b/gfx/src/nsFontMetrics.cpp
@@ -35,16 +35,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** 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 {
 public:
     AutoTextRun(nsFontMetrics* aMetrics, nsRenderingContext* aRC,
                 const char* aString, PRInt32 aLength)
@@ -75,16 +76,20 @@ private:
 };
 
 class StubPropertyProvider : public gfxTextRun::PropertyProvider {
 public:
     virtual void GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength,
                                       PRPackedBool* aBreakBefore) {
         NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
     }
+    virtual PRInt8 GetHyphensOption() {
+        NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
+        return NS_STYLE_HYPHENS_NONE;
+    }
     virtual gfxFloat GetHyphenWidth() {
         NS_ERROR("This shouldn't be called because we never enable hyphens");
         return 0;
     }
     virtual void GetSpacing(PRUint32 aStart, PRUint32 aLength,
                             Spacing* aSpacing) {
         NS_ERROR("This shouldn't be called because we never enable spacing");
     }
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -60,16 +60,17 @@
 #include "gfxUserFontSet.h"
 #include "gfxPlatformFontList.h"
 #include "gfxScriptItemizer.h"
 #include "gfxUnicodeProperties.h"
 #include "nsMathUtils.h"
 #include "nsBidiUtils.h"
 #include "nsUnicodeRange.h"
 #include "nsCompressedCharMap.h"
+#include "nsStyleConsts.h"
 
 #include "cairo.h"
 #include "gfxFontTest.h"
 
 #include "harfbuzz/hb-blob.h"
 
 #include "nsCRT.h"
 
@@ -3115,32 +3116,32 @@ gfxTextRun::~gfxTextRun()
     delete [] mCharacterGlyphs;
 
     NS_RELEASE(mFontGroup);
     MOZ_COUNT_DTOR(gfxTextRun);
 }
 
 PRBool
 gfxTextRun::SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
-                                   PRPackedBool *aBreakBefore,
+                                   PRUint8 *aBreakBefore,
                                    gfxContext *aRefContext)
 {
     NS_ASSERTION(aStart + aLength <= mCharacterCount, "Overflow");
 
     if (!mCharacterGlyphs)
         return PR_TRUE;
     PRUint32 changed = 0;
     PRUint32 i;
     for (i = 0; i < aLength; ++i) {
-        PRBool canBreak = aBreakBefore[i];
+        PRUint8 canBreak = aBreakBefore[i];
         if (canBreak && !mCharacterGlyphs[aStart + 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 = PR_FALSE;
+            canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE;
         }
         changed |= mCharacterGlyphs[aStart + i].SetCanBreakBefore(canBreak);
     }
     return changed != 0;
 }
 
 gfxTextRun::LigatureData
 gfxTextRun::ComputeLigatureData(PRUint32 aPartStart, PRUint32 aPartEnd,
@@ -3717,17 +3718,19 @@ gfxTextRun::BreakAndMeasureText(PRUint32
     PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
     PRBool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0;
     if (haveSpacing) {
         GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
                            spacingBuffer);
     }
     PRPackedBool hyphenBuffer[MEASUREMENT_BUFFER_SIZE];
     PRBool haveHyphenation = aProvider &&
-                             (mFlags & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0;
+        (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_AUTO ||
+         (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_MANUAL &&
+          (mFlags & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0));
     if (haveHyphenation) {
         aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
                                         hyphenBuffer);
     }
 
     gfxFloat width = 0;
     gfxFloat advance = 0;
     // The number of space characters that can be trimmed
@@ -3761,17 +3764,17 @@ gfxTextRun::BreakAndMeasureText(PRUint32
             }
         }
 
         // There can't be a word-wrap break opportunity at the beginning of the
         // line: if the width is too small for even one character to fit, it 
         // could be the first and last break opportunity on the line, and that
         // would trigger an infinite loop.
         if (!aSuppressInitialBreak || i > aStart) {
-            PRBool lineBreakHere = mCharacterGlyphs[i].CanBreakBefore();
+            PRBool lineBreakHere = mCharacterGlyphs[i].CanBreakBefore() == 1;
             PRBool hyphenation = haveHyphenation && hyphenBuffer[i - bufferStart];
             PRBool wordWrapping = aCanWordWrap && *aBreakPriority <= eWordWrapBreak;
 
             if (lineBreakHere || hyphenation || wordWrapping) {
                 gfxFloat hyphenatedAdvance = advance;
                 if (!lineBreakHere && !wordWrapping) {
                     hyphenatedAdvance += aProvider->GetHyphenWidth();
                 }
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -1402,17 +1402,23 @@ public:
         return mCharacterGlyphs[aPos].IsClusterStart();
     }
     PRBool IsLigatureGroupStart(PRUint32 aPos) {
         NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
         return mCharacterGlyphs[aPos].IsLigatureGroupStart();
     }
     PRBool CanBreakLineBefore(PRUint32 aPos) {
         NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
-        return mCharacterGlyphs[aPos].CanBreakBefore();
+        return mCharacterGlyphs[aPos].CanBreakBefore() ==
+            CompressedGlyph::FLAG_BREAK_TYPE_NORMAL;
+    }
+    PRBool CanHyphenateBefore(PRUint32 aPos) {
+        NS_ASSERTION(0 <= aPos && aPos < mCharacterCount, "aPos out of range");
+        return mCharacterGlyphs[aPos].CanBreakBefore() ==
+            CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN;
     }
 
     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.
     
@@ -1426,17 +1432,17 @@ public:
      * This can change glyphs and/or geometry! Some textruns' shapes
      * depend on potential line breaks (e.g., title-case-converting textruns).
      * This function is virtual so that those textruns can reshape themselves.
      * 
      * @return true if this changed the linebreaks, false if the new line
      * breaks are the same as the old
      */
     virtual PRBool SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
-                                          PRPackedBool *aBreakBefore,
+                                          PRUint8 *aBreakBefore,
                                           gfxContext *aRefContext);
 
     /**
      * Layout provides PropertyProvider objects. These allow detection of
      * potential line break points and computation of spacing. We pass the data
      * this way to allow lazy data acquisition; for example BreakAndMeasureText
      * will want to only ask for properties of text it's actually looking at.
      * 
@@ -1446,16 +1452,21 @@ public:
      */
     class PropertyProvider {
     public:
         // Detect hyphenation break opportunities in the given range; breaks
         // not at cluster boundaries will be ignored.
         virtual void GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength,
                                           PRPackedBool *aBreakBefore) = 0;
 
+        // Returns the provider's hyphenation setting, so callers can decide
+        // whether it is necessary to call GetHyphenationBreaks.
+        // Result is an NS_STYLE_HYPHENS_* value.
+        virtual PRInt8 GetHyphensOption() = 0;
+
         // Returns the extra width that will be consumed by a hyphen. This should
         // be constant for a given textrun.
         virtual gfxFloat GetHyphenWidth() = 0;
 
         typedef gfxFont::Spacing Spacing;
 
         /**
          * Get the spacing around the indicated characters. Spacing must be zero
@@ -1717,24 +1728,32 @@ public:
     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 14) (specified
+            // is one that fits in the available bits (currently 13) (specified
             // in appunits).
             FLAG_IS_SIMPLE_GLYPH  = 0x80000000U,
-            // Indicates that a linebreak is allowed before this character
-            FLAG_CAN_BREAK_BEFORE = 0x40000000U,
+
+            // 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  = 0x3FFF0000U,
+            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:
 
@@ -1778,48 +1797,52 @@ public:
             return (mValue & FLAG_IS_SIMPLE_GLYPH) || !(mValue & FLAG_NOT_LIGATURE_GROUP_START);
         }
         PRBool 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);
         }
 
-        PRBool CanBreakBefore() const { return (mValue & FLAG_CAN_BREAK_BEFORE) != 0; }
-        // Returns FLAG_CAN_BREAK_BEFORE if the setting changed, 0 otherwise
-        PRUint32 SetCanBreakBefore(PRBool aCanBreakBefore) {
-            NS_ASSERTION(aCanBreakBefore == PR_FALSE || aCanBreakBefore == PR_TRUE,
+        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 = aCanBreakBefore*FLAG_CAN_BREAK_BEFORE;
-            PRUint32 toggle = breakMask ^ (mValue & FLAG_CAN_BREAK_BEFORE);
+            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 & FLAG_CAN_BREAK_BEFORE) | FLAG_IS_SIMPLE_GLYPH |
+            mValue = (mValue & FLAGS_CAN_BREAK_BEFORE) |
+                FLAG_IS_SIMPLE_GLYPH |
                 (aAdvanceAppUnits << ADVANCE_SHIFT) | aGlyph;
             return *this;
         }
         CompressedGlyph& SetComplex(PRBool aClusterStart, PRBool aLigatureStart,
                 PRUint32 aGlyphCount) {
-            mValue = (mValue & FLAG_CAN_BREAK_BEFORE) | FLAG_NOT_MISSING |
+            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 & (FLAG_CAN_BREAK_BEFORE | FLAG_NOT_CLUSTER_START)) |
+            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;
         }
 
--- a/intl/Makefile.in
+++ b/intl/Makefile.in
@@ -38,16 +38,17 @@
 DEPTH     = ..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 PARALLEL_DIRS = \
+  hyphenation \
   locale \
   lwbrk \
   strres \
   unicharutil \
   $(NULL)
 
 DIRS = \
   uconv \
--- a/intl/build/Makefile.in
+++ b/intl/build/Makefile.in
@@ -74,16 +74,17 @@ LOCAL_INCLUDES = \
 			$(NULL)
 
 SHARED_LIBRARY_LIBS = \
 			../lwbrk/src/$(LIB_PREFIX)lwbrk_s.$(LIB_SUFFIX) \
 			../unicharutil/src/$(LIB_PREFIX)ucharucomp_s.$(LIB_SUFFIX) \
 			../strres/src/$(LIB_PREFIX)strres_s.$(LIB_SUFFIX) \
 			../locale/src/$(LIB_PREFIX)nslocale_s.$(LIB_SUFFIX) \
 			../locale/src/$(LOCALE_DIR)/$(LIB_PREFIX)platlocale_s.$(LIB_SUFFIX) \
+			../hyphenation/src/$(HYPHENATION_DIR)/$(LIB_PREFIX)hyphenation_s.$(LIB_SUFFIX) \
 			$(NULL)
 
 EXTRA_DSO_LDOPTS = \
 	$(MOZ_UNICHARUTIL_LIBS) \
 	$(MOZ_COMPONENT_LIBS) \
 	$(NULL)
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
new file mode 100644
--- /dev/null
+++ b/intl/hyphenation/Makefile.in
@@ -0,0 +1,48 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla Hyphenation Service.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Jonathan Kew <jfkthame@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= hyphenation
+DIRS		= public src
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/intl/hyphenation/public/Makefile.in
@@ -0,0 +1,53 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla Hyphenation Service.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Jonathan Kew <jfkthame@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= hyphenation
+
+EXPORTS		= \
+		nsHyphenationManager.h \
+		nsHyphenator.h \
+		$(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/intl/hyphenation/public/nsHyphenationManager.h
@@ -0,0 +1,74 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Hyphenation Service.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsHyphenationManager_h__
+#define nsHyphenationManager_h__
+
+#include "nsInterfaceHashtable.h"
+#include "nsRefPtrHashtable.h"
+#include "nsHashKeys.h"
+
+class nsHyphenator;
+class nsIAtom;
+
+class nsHyphenationManager
+{
+public:
+  nsHyphenationManager();
+
+  already_AddRefed<nsHyphenator> GetHyphenator(nsIAtom *aLocale);
+
+  static nsHyphenationManager *Instance();
+
+  static void Shutdown();
+
+private:
+  ~nsHyphenationManager();
+
+protected:
+  void LoadPatternList();
+  void LoadPatternListFromDir(nsIFile *aDir);
+  void LoadAliases();
+
+  nsInterfaceHashtable<nsISupportsHashKey,nsIAtom> mHyphAliases;
+  nsInterfaceHashtable<nsISupportsHashKey,nsIFile> mPatternFiles;
+  nsRefPtrHashtable<nsISupportsHashKey,nsHyphenator> mHyphenators;
+
+  static nsHyphenationManager *sInstance;
+};
+
+#endif // nsHyphenationManager_h__
new file mode 100644
--- /dev/null
+++ b/intl/hyphenation/public/nsHyphenator.h
@@ -0,0 +1,66 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Hyphenation Service.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsHyphenator_h__
+#define nsHyphenator_h__
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+class nsIUGenCategory;
+
+class nsHyphenator
+{
+public:
+  nsHyphenator(nsIFile *aFile);
+
+  NS_INLINE_DECL_REFCOUNTING(nsHyphenator)
+
+  PRBool IsValid();
+
+  nsresult Hyphenate(const nsAString& aText, nsTArray<PRPackedBool>& aHyphens);
+
+private:
+  ~nsHyphenator();
+
+protected:
+  void                      *mDict;
+  nsCOMPtr<nsIUGenCategory>  mCategories;
+};
+
+#endif // nsHyphenator_h__
new file mode 100644
--- /dev/null
+++ b/intl/hyphenation/src/Makefile.in
@@ -0,0 +1,56 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla Hyphenation Service.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Jonathan Kew <jfkthame@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH           = ../../..
+topsrcdir       = @top_srcdir@
+srcdir          = @srcdir@
+VPATH           = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE           = hyphenation
+LIBRARY_NAME     = hyphenation_s
+LIBXUL_LIBRARY   = 1
+
+CSRCS            = hyphen.c \
+                   $(NULL)
+
+CPPSRCS          = nsHyphenator.cpp \
+                   nsHyphenationManager.cpp \
+                   $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/intl/hyphenation/src/hnjalloc.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Foundation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2005-2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * Simple replacement for hnjalloc.h from libhyphen-2.x, to use moz_x* memory
+ * allocation functions. Note that the hyphen.c code does *NOT* check for
+ * NULL from memory (re)allocation, so it is essential that we use the
+ * "infallible" moz_x* variants here.
+ */
+
+#include "mozilla/mozalloc.h"
+
+#define hnj_malloc(size)      moz_xmalloc(size)
+#define hnj_realloc(p, size)  moz_xrealloc(p, size)
+#define hnj_free(p)           moz_free(p)
+
new file mode 100644
--- /dev/null
+++ b/intl/hyphenation/src/nsHyphenationManager.cpp
@@ -0,0 +1,256 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Hyphenation Service.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsHyphenationManager.h"
+#include "nsHyphenator.h"
+#include "nsIAtom.h"
+#include "nsIFile.h"
+#include "nsIProperties.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIDirectoryEnumerator.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsUnicharUtils.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+
+#define INTL_HYPHENATIONALIAS_PREFIX "intl.hyphenation-alias."
+
+nsHyphenationManager *nsHyphenationManager::sInstance = nsnull;
+
+nsHyphenationManager*
+nsHyphenationManager::Instance()
+{
+  if (sInstance == nsnull) {
+    sInstance = new nsHyphenationManager();
+  }
+  return sInstance;
+}
+
+void
+nsHyphenationManager::Shutdown()
+{
+  delete sInstance;
+}
+
+nsHyphenationManager::nsHyphenationManager()
+{
+  mHyphAliases.Init();
+  mPatternFiles.Init();
+  mHyphenators.Init();
+  LoadPatternList();
+  LoadAliases();
+}
+
+nsHyphenationManager::~nsHyphenationManager()
+{
+  sInstance = nsnull;
+}
+
+already_AddRefed<nsHyphenator>
+nsHyphenationManager::GetHyphenator(nsIAtom *aLocale)
+{
+  nsRefPtr<nsHyphenator> hyph;
+  mHyphenators.Get(aLocale, getter_AddRefs(hyph));
+  if (hyph) {
+    return hyph.forget();
+  }
+  nsCOMPtr<nsIFile> file = mPatternFiles.Get(aLocale);
+  if (!file) {
+    nsCOMPtr<nsIAtom> alias = mHyphAliases.Get(aLocale);
+    if (alias) {
+      mHyphenators.Get(alias, getter_AddRefs(hyph));
+      if (hyph) {
+        return hyph.forget();
+      }
+      file = mPatternFiles.Get(alias);
+      if (file) {
+        aLocale = alias;
+      }
+    }
+    if (!file) {
+      // In the case of a locale such as "de-DE-1996", we try replacing
+      // successive trailing subtags with "-*" to find fallback patterns,
+      // so "de-DE-1996" -> "de-DE-*" (and then recursively -> "de-*")
+      nsAtomCString localeStr(aLocale);
+      if (StringEndsWith(localeStr, NS_LITERAL_CSTRING("-*"))) {
+        localeStr.Truncate(localeStr.Length() - 2);
+      }
+      PRInt32 i = localeStr.RFindChar('-');
+      if (i > 1) {
+        localeStr.Replace(i, localeStr.Length() - i, "-*");
+        nsCOMPtr<nsIAtom> fuzzyLocale = do_GetAtom(localeStr);
+        return GetHyphenator(fuzzyLocale);
+      } else {
+        return nsnull;
+      }
+    }
+  }
+  hyph = new nsHyphenator(file);
+  if (hyph->IsValid()) {
+    mHyphenators.Put(aLocale, hyph);
+    return hyph.forget();
+  }
+#ifdef DEBUG
+  nsCString msg;
+  file->GetNativePath(msg);
+  msg.Insert("failed to load patterns from ", 0);
+  NS_WARNING(msg.get());
+#endif
+  mPatternFiles.Remove(aLocale);
+  return nsnull;
+}
+
+void
+nsHyphenationManager::LoadPatternList()
+{
+  mPatternFiles.Clear();
+  mHyphenators.Clear();
+  
+  nsresult rv;
+  
+  nsCOMPtr<nsIProperties> dirSvc =
+    do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+  if (!dirSvc) {
+    return;
+  }
+  
+  nsCOMPtr<nsIFile> greDir;
+  rv = dirSvc->Get(NS_GRE_DIR,
+                   NS_GET_IID(nsIFile), getter_AddRefs(greDir));
+  if (NS_SUCCEEDED(rv)) {
+    greDir->AppendNative(NS_LITERAL_CSTRING("hyphenation"));
+    LoadPatternListFromDir(greDir);
+  }
+  
+  nsCOMPtr<nsIFile> appDir;
+  rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
+                   NS_GET_IID(nsIFile), getter_AddRefs(appDir));
+  if (NS_SUCCEEDED(rv)) {
+    appDir->AppendNative(NS_LITERAL_CSTRING("hyphenation"));
+    PRBool equals;
+    if (NS_SUCCEEDED(appDir->Equals(greDir, &equals)) && !equals) {
+      LoadPatternListFromDir(appDir);
+    }
+  }
+}
+
+void
+nsHyphenationManager::LoadPatternListFromDir(nsIFile *aDir)
+{
+  nsresult rv;
+  
+  PRBool check = PR_FALSE;
+  rv = aDir->Exists(&check);
+  if (NS_FAILED(rv) || !check) {
+    return;
+  }
+  
+  rv = aDir->IsDirectory(&check);
+  if (NS_FAILED(rv) || !check) {
+    return;
+  }
+
+  nsCOMPtr<nsISimpleEnumerator> e;
+  rv = aDir->GetDirectoryEntries(getter_AddRefs(e));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+  
+  nsCOMPtr<nsIDirectoryEnumerator> files(do_QueryInterface(e));
+  if (!files) {
+    return;
+  }
+  
+  nsCOMPtr<nsIFile> file;
+  while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file))) && file){
+    nsAutoString dictName;
+    file->GetLeafName(dictName);
+    NS_ConvertUTF16toUTF8 locale(dictName);
+    ToLowerCase(locale);
+    if (!StringEndsWith(locale, NS_LITERAL_CSTRING(".dic"))) {
+      continue;
+    }
+    if (StringBeginsWith(locale, NS_LITERAL_CSTRING("hyph_"))) {
+      locale.Cut(0, 5);
+    }
+    locale.SetLength(locale.Length() - 4); // strip ".dic"
+    for (PRUint32 i = 0; i < locale.Length(); ++i) {
+      if (locale[i] == '_') {
+        locale.Replace(i, 1, '-');
+      }
+    }
+#ifdef DEBUG
+    printf("adding hyphenation patterns for %s: %s\n", locale.get(),
+           NS_ConvertUTF16toUTF8(dictName).get());
+#endif
+    nsCOMPtr<nsIAtom> localeAtom = do_GetAtom(locale);
+    mPatternFiles.Put(localeAtom, file);
+  }
+}
+
+void
+nsHyphenationManager::LoadAliases()
+{
+  nsresult rv;
+  nsCOMPtr<nsIPrefBranch> prefBranch =
+    do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+  PRUint32 prefCount;
+  char **prefNames;
+  rv = prefBranch->GetChildList(INTL_HYPHENATIONALIAS_PREFIX,
+                                &prefCount, &prefNames);
+  if (NS_SUCCEEDED(rv) && prefCount > 0) {
+    for (PRUint32 i = 0; i < prefCount; ++i) {
+      char *prefValue;
+      rv = prefBranch->GetCharPref(prefNames[i], &prefValue);
+      if (NS_SUCCEEDED(rv)) {
+        nsCAutoString alias(prefNames[i]);
+        alias.Cut(0, strlen(INTL_HYPHENATIONALIAS_PREFIX));
+        ToLowerCase(alias);
+        nsCAutoString value(prefValue);
+        ToLowerCase(value);
+        nsCOMPtr<nsIAtom> aliasAtom = do_GetAtom(alias);
+        nsCOMPtr<nsIAtom> valueAtom = do_GetAtom(value);
+        mHyphAliases.Put(aliasAtom, valueAtom);
+        NS_Free(prefValue);
+      }
+    }
+    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, prefNames);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/intl/hyphenation/src/nsHyphenator.cpp
@@ -0,0 +1,147 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Hyphenation Service.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsHyphenator.h"
+#include "nsIFile.h"
+#include "nsUTF8Utils.h"
+#include "nsIUGenCategory.h"
+#include "nsUnicharUtilCIID.h"
+
+#include "hyphen.h"
+
+nsHyphenator::nsHyphenator(nsIFile *aFile)
+  : mDict(nsnull)
+{
+  nsCString path;
+  aFile->GetNativePath(path);
+  mDict = hnj_hyphen_load(path.get());
+#ifdef DEBUG
+  if (mDict) {
+    printf("loaded hyphenation patterns from %s\n", path.get());
+  }
+#endif
+  nsresult rv;
+  mCategories =
+    do_GetService(NS_UNICHARCATEGORY_CONTRACTID, &rv);
+  NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get category service");
+}
+
+nsHyphenator::~nsHyphenator()
+{
+  if (mDict != nsnull) {
+    hnj_hyphen_free((HyphenDict*)mDict);
+    mDict = nsnull;
+  }
+}
+
+PRBool
+nsHyphenator::IsValid()
+{
+  return (mDict != nsnull) && (mCategories != nsnull);
+}
+
+nsresult
+nsHyphenator::Hyphenate(const nsAString& aString,
+                        nsTArray<PRPackedBool>& aHyphens)
+{
+  if (!aHyphens.SetLength(aString.Length())) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  memset(aHyphens.Elements(), PR_FALSE, aHyphens.Length());
+
+  PRBool inWord = PR_FALSE;
+  PRUint32 wordStart = 0, wordLimit = 0;
+  for (PRUint32 i = 0; i < aString.Length(); i++) {
+    PRUnichar ch = aString[i];
+
+    nsIUGenCategory::nsUGenCategory cat = mCategories->Get(ch);
+    if (cat == nsIUGenCategory::kLetter || cat == nsIUGenCategory::kMark) {
+      if (!inWord) {
+        inWord = PR_TRUE;
+        wordStart = i;
+      }
+      wordLimit = i + 1;
+      if (i < aString.Length() - 1) {
+        continue;
+      }
+    }
+
+    if (inWord) {
+      NS_ConvertUTF16toUTF8 utf8(aString.BeginReading() + wordStart,
+                                 wordLimit - wordStart);
+      nsAutoTArray<char,200> utf8hyphens;
+      utf8hyphens.SetLength(utf8.Length() + 5);
+      char **rep = nsnull;
+      int *pos = nsnull;
+      int *cut = nsnull;
+      int err = hnj_hyphen_hyphenate2((HyphenDict*)mDict,
+                                      utf8.BeginReading(), utf8.Length(),
+                                      utf8hyphens.Elements(), nsnull,
+                                      &rep, &pos, &cut);
+      if (!err) {
+        PRUint32 utf16offset = wordStart;
+        const char *cp = utf8.BeginReading();
+        while (cp < utf8.EndReading()) {
+          if (UTF8traits::isASCII(*cp)) { // single-byte utf8 char
+            cp++;
+            utf16offset++;
+          } else if (UTF8traits::is2byte(*cp)) { // 2-byte sequence
+            cp += 2;
+            utf16offset++;
+          } else if (UTF8traits::is3byte(*cp)) { // 3-byte sequence
+            cp += 3;
+            utf16offset++;
+          } else { // must be a 4-byte sequence (no need to check validity,
+                   // as this was just created with NS_ConvertUTF16toUTF8)
+            NS_ASSERTION(UTF8traits::is4byte(*cp), "unexpected utf8 byte");
+            cp += 4;
+            utf16offset += 2;
+          }
+          NS_ASSERTION(cp <= utf8.EndReading(), "incomplete utf8 string?");
+          NS_ASSERTION(utf16offset <= aString.Length(), "length mismatch?");
+          if (utf8hyphens[cp - utf8.BeginReading() - 1] & 0x01) {
+            aHyphens[utf16offset - 1] = PR_TRUE;
+          }
+        }
+      }
+    }
+    
+    inWord = PR_FALSE;
+  }
+
+  return NS_OK;
+}
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -124,16 +124,18 @@
 
 #include "nsCycleCollector.h"
 #include "nsJSEnvironment.h"
 #include "nsContentSink.h"
 #include "nsFrameMessageManager.h"
 #include "nsRefreshDriver.h"
 #include "CanvasImageCache.h"
 
+#include "nsHyphenationManager.h"
+
 extern void NS_ShutdownChainItemPool();
 
 using namespace mozilla;
 
 nsrefcnt nsLayoutStatics::sLayoutStaticRefcnt = 0;
 
 nsresult
 nsLayoutStatics::Initialize()
@@ -377,9 +379,11 @@ nsLayoutStatics::Shutdown()
 
   NS_ShutdownChainItemPool();
 
   nsFrameList::Shutdown();
 
   nsHTMLInputElement::DestroyUploadLastDir();
 
   nsLayoutUtils::Shutdown();
+
+  nsHyphenationManager::Shutdown();
 }
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -860,17 +860,17 @@ public:
   public:
     BreakSink(gfxTextRun* aTextRun, gfxContext* aContext, PRUint32 aOffsetIntoTextRun,
               PRBool aExistingTextRun) :
                 mTextRun(aTextRun), mContext(aContext),
                 mOffsetIntoTextRun(aOffsetIntoTextRun),
                 mChangedBreaks(PR_FALSE), mExistingTextRun(aExistingTextRun) {}
 
     virtual void SetBreaks(PRUint32 aOffset, PRUint32 aLength,
-                           PRPackedBool* aBreakBefore) {
+                           PRUint8* aBreakBefore) {
       if (mTextRun->SetPotentialLineBreaks(aOffset + mOffsetIntoTextRun, aLength,
                                            aBreakBefore, mContext)) {
         mChangedBreaks = PR_TRUE;
         // Be conservative and assume that some breaks have been set
         mTextRun->ClearFlagBits(nsTextFrameUtils::TEXT_NO_BREAKS);
       }
     }
     
@@ -1140,16 +1140,17 @@ BuildTextRuns(gfxContext* aContext, nsTe
     NS_ASSERTION(!aForFrame ||
                  (aLineContainer == FindLineContainer(aForFrame) ||
                   (aLineContainer->GetType() == nsGkAtoms::letterFrame &&
                    aLineContainer->GetStyleDisplay()->IsFloating())),
                  "Wrong line container hint");
   }
 
   nsPresContext* presContext = aLineContainer->PresContext();
+  nsStyleContext* sc = aForFrame->GetStyleContext();
   BuildTextRunsScanner scanner(presContext, aContext, aLineContainer);
 
   nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aLineContainer);
 
   if (!block) {
     NS_ASSERTION(!aLineContainer->GetPrevInFlow() && !aLineContainer->GetNextInFlow(),
                  "Breakable non-block line containers not supported");
     // Just loop through all the children of the linecontainer ... it's really
@@ -2032,16 +2033,19 @@ BuildTextRunsScanner::SetupBreakSinksFor
       flags |= nsLineBreaker::BREAK_SUPPRESS_INSIDE;
     }
     if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_NO_BREAKS) {
       flags |= nsLineBreaker::BREAK_SKIP_SETTING_NO_BREAKS;
     }
     if (textStyle->mTextTransform == NS_STYLE_TEXT_TRANSFORM_CAPITALIZE) {
       flags |= nsLineBreaker::BREAK_NEED_CAPITALIZATION;
     }
+    if (textStyle->mHyphens == NS_STYLE_HYPHENS_AUTO) {
+      flags |= nsLineBreaker::BREAK_USE_AUTO_HYPHENATION;
+    }
 
     if (HasCompressedLeadingWhitespace(startFrame, textStyle,
                                        mappedFlow->GetContentEnd(), iter)) {
       mLineBreaker.AppendInvisibleWhitespace(flags);
     }
 
     if (length > 0) {
       BreakSink* sink = aSuppressSink ? nsnull : (*breakSink).get();
@@ -2451,16 +2455,19 @@ public:
 
   // Call this after construction if you're not going to reflow the text
   void InitializeForDisplay(PRBool aTrimAfter);
 
   virtual void GetSpacing(PRUint32 aStart, PRUint32 aLength, Spacing* aSpacing);
   virtual gfxFloat GetHyphenWidth();
   virtual void GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength,
                                     PRPackedBool* aBreakBefore);
+  virtual PRInt8 GetHyphensOption() {
+    return mTextStyle->mHyphens;
+  }
 
   void GetSpacingInternal(PRUint32 aStart, PRUint32 aLength, Spacing* aSpacing,
                           PRBool aIgnoreTabs);
 
   /**
    * Count the number of justifiable characters in the given DOM range
    */
   PRUint32 ComputeJustifiableCharacters(PRInt32 aOffset, PRInt32 aLength);
@@ -2820,17 +2827,19 @@ PropertyProvider::GetHyphenWidth()
 
 void
 PropertyProvider::GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength,
                                        PRPackedBool* aBreakBefore)
 {
   NS_PRECONDITION(IsInBounds(mStart, mLength, aStart, aLength), "Range out of bounds");
   NS_PRECONDITION(mLength != PR_INT32_MAX, "Can't call this with undefined length");
 
-  if (!mTextStyle->WhiteSpaceCanWrap()) {
+  if (!mTextStyle->WhiteSpaceCanWrap() ||
+      mTextStyle->mHyphens == NS_STYLE_HYPHENS_NONE)
+  {
     memset(aBreakBefore, PR_FALSE, aLength);
     return;
   }
 
   // Iterate through the original-string character runs
   nsSkipCharsRunIterator
     run(mStart, nsSkipCharsRunIterator::LENGTH_UNSKIPPED_ONLY, aLength);
   run.SetSkippedOffset(aStart);
@@ -2856,16 +2865,24 @@ PropertyProvider::GetHyphenationBreaks(P
       memset(aBreakBefore + runOffsetInSubstring, 0, run.GetRunLength());
       // Don't allow hyphen breaks at the start of the line
       aBreakBefore[runOffsetInSubstring] = allowHyphenBreakBeforeNextChar &&
           (!(mFrame->GetStateBits() & TEXT_START_OF_LINE) ||
            run.GetSkippedOffset() > mStart.GetSkippedOffset());
       allowHyphenBreakBeforeNextChar = PR_FALSE;
     }
   }
+
+  if (mTextStyle->mHyphens == NS_STYLE_HYPHENS_AUTO) {
+    for (PRUint32 i = 0; i < aLength; ++i) {
+      if (mTextRun->CanHyphenateBefore(aStart + i)) {
+        aBreakBefore[i] = PR_TRUE;
+      }
+    }
+  }
 }
 
 void
 PropertyProvider::InitializeForDisplay(PRBool aTrimAfter)
 {
   nsTextFrame::TrimmedOffsets trimmed =
     mFrame->GetTrimmedOffsets(mFrag, aTrimAfter);
   mStart.SetOriginalOffset(trimmed.mStart);
@@ -6048,17 +6065,19 @@ nsTextFrame::AddInlineMinWidthForFlow(ns
   // OK since we can't really handle tabs for intrinsic sizing anyway.
   const nsStyleText* textStyle = GetStyleText();
   const nsTextFragment* frag = mContent->GetText();
 
   // If we're hyphenating, the PropertyProvider needs the actual length;
   // otherwise we can just pass PR_INT32_MAX to mean "all the text"
   PRInt32 len = PR_INT32_MAX;
   PRBool hyphenating = frag->GetLength() > 0 &&
-    (mTextRun->GetFlags() & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0;
+    (textStyle->mHyphens == NS_STYLE_HYPHENS_AUTO ||
+     (textStyle->mHyphens == NS_STYLE_HYPHENS_MANUAL &&
+      (mTextRun->GetFlags() & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0));
   if (hyphenating) {
     gfxSkipCharsIterator tmp(iter);
     len = PR_MIN(GetContentOffset() + GetInFlowContentLength(),
                  tmp.ConvertSkippedToOriginal(flowEndInTextRun)) - iter.GetOriginalOffset();
   }
   PropertyProvider provider(mTextRun, textStyle, frag, this,
                             iter, len, nsnull, 0);
 
@@ -6131,17 +6150,17 @@ nsTextFrame::AddInlineMinWidthForFlow(ns
       wordStart = i + 1;
     } else if (i < flowEndInTextRun ||
         (i == mTextRun->GetLength() &&
          (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TRAILING_BREAK))) {
       if (preformattedNewline) {
         aData->ForceBreak(aRenderingContext);
       } else if (hyphBreakBefore && hyphBreakBefore[i - start]) {
         aData->OptionallyBreak(aRenderingContext, provider.GetHyphenWidth());
-      } else {
+      } {
         aData->OptionallyBreak(aRenderingContext);
       }
       wordStart = i;
     }
   }
 
   if (start < flowEndInTextRun) {
     // Check if we have collapsible whitespace at the end
@@ -6355,16 +6374,20 @@ nsTextFrame::ComputeTightBounds(gfxConte
   // paint so that's the one we'll use.
   return RoundOut(metrics.mBoundingBox) + nsPoint(0, mAscent);
 }
 
 static PRBool
 HasSoftHyphenBefore(const nsTextFragment* aFrag, gfxTextRun* aTextRun,
                     PRInt32 aStartOffset, const gfxSkipCharsIterator& aIter)
 {
+  if (aIter.GetSkippedOffset() < aTextRun->GetLength() &&
+      aTextRun->CanHyphenateBefore(aIter.GetSkippedOffset())) {
+    return PR_TRUE;
+  }
   if (!(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_SHY))
     return PR_FALSE;
   gfxSkipCharsIterator iter = aIter;
   while (iter.GetOriginalOffset() > aStartOffset) {
     iter.AdvanceOriginal(-1);
     if (!iter.IsOriginalCharSkipped())
       break;
     if (aFrag->CharAt(iter.GetOriginalOffset()) == CH_SHY)
@@ -6929,16 +6952,17 @@ nsTextFrame::ReflowText(nsLineLayout& aL
   // from this textframe overrides. (Currently in CSS trimmable width can be
   // at most one space so there's no way for trimmable width from a previous
   // frame to accumulate with trimmable width from this frame.)
   if (transformedCharsFit > 0) {
     aLineLayout.SetTrimmableWidth(NSToCoordFloor(trimmableWidth));
     AddStateBits(TEXT_HAS_NONCOLLAPSED_CHARACTERS);
   }
   if (charsFit > 0 && charsFit == length &&
+      textStyle->mHyphens != NS_STYLE_HYPHENS_NONE &&
       HasSoftHyphenBefore(frag, mTextRun, offset, end)) {
     // Record a potential break after final soft hyphen
     aLineLayout.NotifyOptionalBreakPosition(mContent, offset + length,
         textMetrics.mAdvanceWidth + provider.GetHyphenWidth() <= availWidth,
                                            eNormalBreak);
   }
   PRBool breakAfter = forceBreakAfter;
   // length == 0 means either the text is empty or it's all collapsed away
@@ -6961,17 +6985,17 @@ nsTextFrame::ReflowText(nsLineLayout& aL
                                               PR_TRUE, eNormalBreak);
     }
   }
 
   // Compute reflow status
   aStatus = contentLength == maxContentLength
     ? NS_FRAME_COMPLETE : NS_FRAME_NOT_COMPLETE;
 
-  if (charsFit == 0 && length > 0) {
+  if (charsFit == 0 && length > 0 && !usedHyphenation) {
     // Couldn't place any text
     aStatus = NS_INLINE_LINE_BREAK_BEFORE();
   } else if (contentLength > 0 && mContentOffset + contentLength - 1 == newLineOffset) {
     // Ends in \n
     aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
     aLineLayout.SetLineEndsInBR(PR_TRUE);
   } else if (breakAfter) {
     aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-1-notref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body lang="en-us">
+<div style="width: 5em;">
+supercalifragilisticexpialidocious
+</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-1-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body lang="en-us">
+<div style="width: 5em; -moz-hyphens: manual;">
+su&shy;per&shy;cal&shy;ifrag&shy;ilis&shy;tic&shy;ex&shy;pi&shy;ali&shy;do&shy;cious
+</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<!-- simple test for automatic hyphenation -->
+<body lang="en-us">
+<div style="width: 5em; -moz-hyphens: auto;">
+supercalifragilisticexpialidocious
+</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-1a.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<!-- adding random <span>s should not affect hyphenation -->
+<body lang="en-us">
+<div style="width: 5em; -moz-hyphens: auto;">
+super<span>cali</span>frag<span>ili</span>sti<span>cex</span>pialidoc<span>i</span>ous
+</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-2-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body lang="en-us">
+<div style="width: 5em; -moz-hyphens: auto;">
+supercalifragilisticexpialidocious
+<span style="-moz-hyphens:none">supercalifragilisticexpialidocious</span>
+supercalifragilisticexpialidocious
+</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-2.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<!-- mixed languages in a word should inhibit automatic hyphenation -->
+<body lang="en-us">
+<div style="width: 5em; -moz-hyphens: auto;">
+supercalifragilisticexpialidocious
+super<span lang="foo">cali</span>fragilisticexpialidocious
+supercalifragilisticexpialidocious
+</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-3-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body lang="en-us">
+<div style="width: 5em;">
+supercalifragilisticexpialidocious
+</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-3.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<!-- check that -moz-hyphens:none prevents break at &shy; -->
+<body lang="en-us">
+<div style="width: 5em; -moz-hyphens: none;">
+su&shy;per&shy;cal&shy;ifrag&shy;ilis&shy;tic&shy;ex&shy;pi&shy;ali&shy;do&shy;cious
+</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-4-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body lang="x-unknown-language">
+<div style="width: 5em; -moz-hyphens: none;">
+supercalifragilisticexpialidocious
+</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-4.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<!-- check that hyphenation is not applied to unknown language -->
+<body lang="x-unknown-language">
+<div style="width: 5em; -moz-hyphens: auto;">
+supercalifragilisticexpialidocious
+</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-5-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+div {
+  margin:       10px;
+  width:        10px;
+  font-family:  monospace;
+  -moz-hyphens: manual;
+}
+</style>
+</head>
+<!-- test some hyphenations that involve overlapping patterns -->
+<body lang="en-us">
+<div>
+photo
+</div>
+<div>
+pho&shy;to&shy;graph
+</div>
+<div>
+pho&shy;tog&shy;ra&shy;pher
+</div>
+<div>
+pho&shy;to&shy;graph&shy;i&shy;cal
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-5.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+div {
+  margin:       10px;
+  width:        10px;
+  font-family:  monospace;
+  -moz-hyphens: auto;
+}
+</style>
+</head>
+<!-- test some hyphenations that involve overlapping patterns -->
+<body lang="en-us">
+<div>
+photo
+</div>
+<div>
+photograph
+</div>
+<div>
+photographer
+</div>
+<div>
+photographical
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-6-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body lang="en-us">
+<div style="width: 0; -moz-hyphens: manual;">
+hy&shy;<span style="color:red">phen&shy;</span>ation
+</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-6.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<!-- style changes don't break hyphenation -->
+<body lang="en-us">
+<div style="width: 0; -moz-hyphens: auto;">
+hy<span style="color:red">phen</span>ation
+</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-7-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body lang="en-us">
+<div style="width: 0; -moz-hyphens: manual;">
+h<span style="color:red">y&shy;phen&shy;a</span>tion
+</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/auto-hyphenation-7.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<!-- style changes don't break hyphenation -->
+<body lang="en-us">
+<div style="width: 0; -moz-hyphens: auto;">
+h<span style="color:red">yphena</span>tion
+</div>
+</body>
+</html>
+
--- a/layout/reftests/text/reftest.list
+++ b/layout/reftests/text/reftest.list
@@ -81,8 +81,18 @@ fails-if(!winWidget) HTTP(..) == 475092-
 # Random on Windows because behavior depends on Uniscribe version(?)
 random-if(winWidget) HTTP(..) != kerning-01.html kerning-01-notref.html
 # Test for bug 577380, support for AAT layout (on OS X only)
 random-if(!cocoaWidget) == 577380.html 577380-ref.html
 # Test for OpenType Arabic shaping support
 HTTP(..) == arabic-shaping-1.html arabic-shaping-1-ref.html
 # check ligature in Arial Bold on Windows, for bug 644184; may fail on other platforms depending on fonts
 random-if(!winWidget) == arial-bold-lam-alef-1.html arial-bold-lam-alef-1-ref.html
+# Tests for hyphenation with -moz-hyphens
+== auto-hyphenation-1.html auto-hyphenation-1-ref.html
+!= auto-hyphenation-1.html auto-hyphenation-1-notref.html
+== auto-hyphenation-1a.html auto-hyphenation-1-ref.html
+== auto-hyphenation-2.html auto-hyphenation-2-ref.html
+== auto-hyphenation-3.html auto-hyphenation-3-ref.html
+== auto-hyphenation-4.html auto-hyphenation-4-ref.html
+== auto-hyphenation-5.html auto-hyphenation-5-ref.html
+== auto-hyphenation-6.html auto-hyphenation-6-ref.html
+== auto-hyphenation-7.html auto-hyphenation-7-ref.html
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1043,16 +1043,21 @@ pref("intl.locale.matchOS",             
 pref("intl.fallbackCharsetList.ISO-8859-1", "windows-1252");
 pref("font.language.group",                 "chrome://global/locale/intl.properties");
 
 // these locales have right-to-left UI
 pref("intl.uidirection.ar", "rtl");
 pref("intl.uidirection.he", "rtl");
 pref("intl.uidirection.fa", "rtl");
 
+// use en-US hyphenation by default for content tagged with plain lang="en"
+pref("intl.hyphenation-alias.en", "en-us");
+// and for other subtags of en-*, if no specific patterns are available
+pref("intl.hyphenation-alias.en-*", "en-us");
+
 pref("font.mathfont-family", "STIXNonUnicode, STIXSizeOneSym, STIXSize1, STIXGeneral, Standard Symbols L, DejaVu Sans, Cambria Math");
 
 // Some CJK fonts have bad underline offset, their CJK character glyphs are overlapped (or adjoined)  to its underline.
 // These fonts are ignored the underline offset, instead of it, the underline is lowered to bottom of its em descent.
 pref("font.blacklist.underline_offset", "FangSong,Gulim,GulimChe,MingLiU,MingLiU-ExtB,MingLiU_HKSCS,MingLiU-HKSCS-ExtB,MS Gothic,MS Mincho,MS PGothic,MS PMincho,MS UI Gothic,PMingLiU,PMingLiU-ExtB,SimHei,SimSun,SimSun-ExtB,Hei,Kai,Apple LiGothic,Apple LiSung,Osaka");
 
 pref("images.dither", "auto");
 pref("security.directory",              "");
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -49,16 +49,17 @@
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsCRT.h"
 #include "nsDataHashtable.h"
 #include "nsICryptoHash.h"
 #include "nsICryptoHMAC.h"
 #include "nsIDirectoryService.h"
 #include "nsIKeyModule.h"
 #include "nsIObserverService.h"
+#include "nsIPermissionManager.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefBranch2.h"
 #include "nsIPrefService.h"
 #include "nsIProperties.h"
 #include "nsIProxyObjectManager.h"
 #include "nsToolkitCompsCID.h"
 #include "nsIUrlClassifierUtils.h"
 #include "nsUrlClassifierDBService.h"
@@ -4020,16 +4021,27 @@ nsUrlClassifierDBService::LookupURI(nsIU
     *didLookup = PR_TRUE;
   } else {
     // Check if the URI is on a clean host.  If so, we don't need to
     // bother queueing up a lookup, we can just return.
     PRBool clean;
     rv = mWorker->CheckCleanHost(key, &clean);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    if (!clean) {
+      nsCOMPtr<nsIPermissionManager> permissionManager =
+        do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+
+      if (permissionManager) {
+        PRUint32 perm;
+        permissionManager->TestPermission(uri, "safe-browsing", &perm);
+        clean |= (perm == nsIPermissionManager::ALLOW_ACTION);
+      }
+    }
+
     *didLookup = !clean;
     if (clean) {
       return NS_OK;
     }
   }
 
   // Create an nsUrlClassifierLookupCallback object.  This object will
   // take care of confirming partial hash matches if necessary before