[tm] Merge m-c and tracemonkey trees, mainly to fix bug 463879
authorVladimir Vukicevic <vladimir@pobox.com>
Sun, 09 Nov 2008 23:05:30 -0800
changeset 21549 9d3b769f920c94b31326549c0b51733629b44b3e
parent 21548 9d4c8de84579657075eff652b694e188d4b6d5dd (current diff)
parent 21545 e71d103b75aff229fc699447854eb2edfc7cb18e (diff)
child 21550 6350575692265f9fd5fceb90f6f3164b1a6feb20
child 21552 d8718260ba23d98f4608e7c0755b3e9e45d81886
push id3570
push uservladimir@mozilla.com
push dateMon, 10 Nov 2008 07:05:59 +0000
treeherdermozilla-central@9d3b769f920c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs463879
milestone1.9.1b2pre
[tm] Merge m-c and tracemonkey trees, mainly to fix bug 463879
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6850,16 +6850,18 @@ let gPrivateBrowsingUI = {
               .setAttribute("disabled", "true");
     }
   },
 
   onExitPrivateBrowsing: function PBUI_onExitPrivateBrowsing() {
     if (BrowserSearch.searchBar)
       BrowserSearch.searchBar.textbox.reset();
 
+    gFindBar.getElement("findbar-textbox").reset();
+
     let pbMenuItem = document.getElementById("privateBrowsingItem");
     if (pbMenuItem)
       pbMenuItem.removeAttribute("checked");
 
     if (!this._privateBrowsingAutoStarted) {
       // Adjust the window's title
       let docElement = document.documentElement;
 #ifdef XP_MACOSX // see bug 411929 comment 38 for the reason behind this
--- a/browser/components/privatebrowsing/test/browser/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/Makefile.in
@@ -43,16 +43,17 @@ relativesrcdir  = browser/components/pri
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES =  \
 		browser_console_clear.js \
 		browser_privatebrowsing_theming.js \
 		browser_privatebrowsing_searchbar.js \
+		browser_privatebrowsing_findbar.js \
 		$(NULL)
 
 ifeq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
 _BROWSER_TEST_FILES += browser_privatebrowsing_ui.js \
     $(NULL)
 endif
 
 libs:: $(_BROWSER_TEST_FILES)
new file mode 100644
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_findbar.js
@@ -0,0 +1,75 @@
+/* ***** 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 Private Browsing Tests.
+ *
+ * The Initial Developer of the Original Code is
+ * Ehsan Akhgari.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ehsan Akhgari <ehsan.akhgari@gmail.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+// This test makes sure that the find bar is cleared when leaving the
+// private browsing mode.
+
+function test() {
+  // initialization
+  let prefBranch = Cc["@mozilla.org/preferences-service;1"].
+                   getService(Ci.nsIPrefBranch);
+  prefBranch.setBoolPref("browser.privatebrowsing.keep_current_session", true);
+  let pb = Cc["@mozilla.org/privatebrowsing;1"].
+           getService(Ci.nsIPrivateBrowsingService);
+
+  // fill in the find bar with something
+  const kTestSearchString = "privatebrowsing";
+  let findBox = gFindBar.getElement("findbar-textbox");
+  gFindBar.startFind(gFindBar.FIND_NORMAL);
+  for (let i = 0; i < kTestSearchString.length; ++ i)
+    EventUtils.synthesizeKey(kTestSearchString[i], {});
+
+  // enter private browsing mode
+  pb.privateBrowsingEnabled = true;
+
+  is(findBox.value, kTestSearchString,
+    "entering the private browsing mode should not clear the findbar");
+  ok(findBox.editor.transactionManager.numberOfUndoItems > 0,
+    "entering the private browsing mode should not reset the undo list of the findbar control");
+
+  // leave private browsing mode
+  pb.privateBrowsingEnabled = false;
+
+  is(findBox.value, "",
+    "leaving the private browsing mode should clear the findbar");
+  is(findBox.editor.transactionManager.numberOfUndoItems, 0,
+    "leaving the private browsing mode should reset the undo list of the findbar control");
+
+  // cleanup
+  prefBranch.clearUserPref("browser.privatebrowsing.keep_current_session");
+  gFindBar.close();
+}
--- a/browser/components/sessionstore/test/browser/browser_248970_a.js
+++ b/browser/components/sessionstore/test/browser/browser_248970_a.js
@@ -40,16 +40,19 @@ function test() {
 
   // test setup
   waitForExplicitFinish();
 
   // private browsing service
   let pb = Cc["@mozilla.org/privatebrowsing;1"].
            getService(Ci.nsIPrivateBrowsingService);
   gPrefService.setBoolPref("browser.privatebrowsing.keep_current_session", true);
+  let profilePath = Cc["@mozilla.org/file/directory_service;1"].
+                    getService(Ci.nsIProperties).
+                    get("ProfD", Ci.nsIFile);
 
   function getSessionstorejsModificationTime() {
     // directory service
     let file = Cc["@mozilla.org/file/directory_service;1"].
                getService(Ci.nsIProperties).
                get("ProfD", Ci.nsIFile);
 
     // access sessionstore.js
@@ -60,37 +63,43 @@ function test() {
     else
       return -1;
   }
 
   // sessionstore service
   let ss = Cc["@mozilla.org/browser/sessionstore;1"].
            getService(Ci.nsISessionStore);
   let ss_interval = gPrefService.getIntPref("browser.sessionstore.interval");
-  let start_mod_time = getSessionstorejsModificationTime();
+  // Remove the sessionstore.js file before setting the interval to 0
+  let sessionStoreJS = profilePath.clone();
+  sessionStoreJS.append("sessionstore.js");
+  if (sessionStoreJS.exists())
+    sessionStoreJS.remove(false);
+  // Make sure that sessionstore.js can be forced to be created by setting
+  // the interval pref to 0
   gPrefService.setIntPref("browser.sessionstore.interval", 0);
-  isnot(start_mod_time, getSessionstorejsModificationTime(),
-    "sessionstore.js should be modified when setting the interval to 0");
+  // sessionstore.js should be re-created at this point
+  sessionStoreJS = profilePath.clone();
+  sessionStoreJS.append("sessionstore.js");
+  ok(sessionStoreJS.exists(),
+    "sessionstore.js should be re-created after setting the interval to 0");
 
   //////////////////////////////////////////////////////////////////
   // Test (A) : No data recording while in private browsing mode  //
   //////////////////////////////////////////////////////////////////
 
   // public session, add a new tab: (A)
   const testURL_A = "http://example.org/";
   let tab_A = gBrowser.addTab(testURL_A);
 
   tab_A.linkedBrowser.addEventListener("load", function (aEvent) {
     this.removeEventListener("load", arguments.callee, true);
 
     // remove sessionstore.js to make sure it's created again when entering
     // the private browsing mode.
-    let profilePath = Cc["@mozilla.org/file/directory_service;1"].
-                      getService(Ci.nsIProperties).
-                      get("ProfD", Ci.nsIFile);
     let sessionStoreJS = profilePath.clone();
     sessionStoreJS.append("sessionstore.js");
     ok(sessionStoreJS.exists(),
       "sessionstore.js should exist prior to entering the private browsing mode");
     sessionStoreJS.remove(false);
 
     // enter private browsing mode
     pb.privateBrowsingEnabled = true;
--- a/browser/themes/winstripe/browser/jar.mn
+++ b/browser/themes/winstripe/browser/jar.mn
@@ -126,16 +126,18 @@ classic.jar:
         skin/classic/aero/browser/Toolbar.png                        (Toolbar-aero.png)
         skin/classic/aero/browser/Toolbar-small.png                  (Toolbar-small-aero.png)
         skin/classic/aero/browser/Go-arrow.png                       (Go-arrow-aero.png)
         skin/classic/aero/browser/Go-arrow-rtl.png                   (Go-arrow-rtl-aero.png)
 *       skin/classic/aero/browser/searchbar.css                      (searchbar.css)
         skin/classic/aero/browser/Search-glass.png                   (Search-glass-aero.png)
         skin/classic/aero/browser/Search-glass-rtl.png               (Search-glass-rtl-aero.png)
         skin/classic/aero/browser/Search-addengines.png
+        skin/classic/aero/browser/section_collapsed.png
+        skin/classic/aero/browser/section_expanded.png
         skin/classic/aero/browser/setDesktopBackground.css
         skin/classic/aero/browser/menu-back.png                      (menu-back-aero.png)
         skin/classic/aero/browser/menu-forward.png                   (menu-forward-aero.png)
         skin/classic/aero/browser/monitor.png
         skin/classic/aero/browser/monitor_16-10.png
         skin/classic/aero/browser/navbar-textbox-buttons.png         (navbar-textbox-buttons-aero.png)
         skin/classic/aero/browser/urlbar-favicon-glow.png
         skin/classic/aero/browser/feeds/feed-icons-16.png            (feeds/feed-icons-16-aero.png)
--- a/content/media/video/public/nsChannelToPipeListener.h
+++ b/content/media/video/public/nsChannelToPipeListener.h
@@ -62,17 +62,19 @@ class nsChannelToPipeListener : public n
 
   // IRequestObserver
   NS_DECL_NSIREQUESTOBSERVER
 
   // IStreamListener
   NS_DECL_NSISTREAMLISTENER
 
   public:
-  nsChannelToPipeListener(nsMediaDecoder* aDecoder);
+  // If aSeeking is PR_TRUE then this listener was created as part of a
+  // seek request and is expecting a byte range partial result.
+  nsChannelToPipeListener(nsMediaDecoder* aDecoder, PRBool aSeeking = PR_FALSE);
   nsresult Init();
   nsresult GetInputStream(nsIInputStream** aStream);
   void Stop();
   void Cancel();
 
   // Return the download rate in bytes per second. Returns 
   // less than zero if the download has complated.
   double BytesPerSecond() const;
@@ -90,11 +92,14 @@ private:
   PRIntervalTime mIntervalStart;
 
   // Interval when last downloaded bytes occurred. Used in computer
   // bytes per second download rate.
   PRIntervalTime mIntervalEnd;
 
   // Total bytes transferred so far
   PRInt64 mTotalBytes;
+
+  // PR_TRUE if this listener is expecting a byte range request result
+  PRPackedBool mSeeking;
 };
 
 #endif
--- a/content/media/video/public/nsMediaDecoder.h
+++ b/content/media/video/public/nsMediaDecoder.h
@@ -140,16 +140,22 @@ class nsMediaDecoder : public nsIObserve
 
   // Return the size of the video file in bytes. Return 0 if the
   // size is unknown or the stream is infinite.
   virtual PRInt64 GetTotalBytes() = 0;
 
   // Set the size of the video file in bytes.
   virtual void SetTotalBytes(PRInt64 aBytes) = 0;
 
+  // Set a flag indicating whether seeking is supported
+  virtual void SetSeekable(PRBool aSeekable) = 0;
+
+  // Return PR_TRUE if seeking is supported.
+  virtual PRBool GetSeekable() = 0;
+
   // Called when the HTML DOM element is bound.
   virtual void ElementAvailable(nsHTMLMediaElement* anElement);
 
   // Called when the HTML DOM element is unbound.
   virtual void ElementUnavailable();
 
   // Invalidate the frame.
   virtual void Invalidate();
--- a/content/media/video/public/nsOggDecoder.h
+++ b/content/media/video/public/nsOggDecoder.h
@@ -337,16 +337,22 @@ class nsOggDecoder : public nsMediaDecod
 
   // Call from any thread safely. Return PR_TRUE if we are currently
   // seeking in the media resource.
   virtual PRBool IsSeeking() const;
 
   // Get the size of the media file in bytes. Called on the main thread only.
   virtual void SetTotalBytes(PRInt64 aBytes);
 
+  // Set a flag indicating whether seeking is supported
+  virtual void SetSeekable(PRBool aSeekable);
+
+  // Return PR_TRUE if seeking is supported.
+  virtual PRBool GetSeekable();
+
 protected:
   // Change to a new play state. This updates the mState variable and
   // notifies any thread blocking on this objects monitor of the
   // change. Can be called on any thread.
   void ChangeState(PlayState aState);
 
   // Returns the monitor for other threads to synchronise access to
   // state
@@ -445,19 +451,28 @@ private:
   // started this is reset to negative.
   float mRequestedSeekTime;
 
   // Size of the media file in bytes. Set on the first non-byte range
   // HTTP request from nsChannelToPipe Listener. Accessed on the
   // main thread only.
   PRInt64 mContentLength;
 
+  // Duration of the media resource. Set to -1 if unknown.
+  // Set when the Ogg metadata is loaded. Accessed on the main thread
+  // only.
+  PRInt64 mDuration;
+
   // True if we are registered with the observer service for shutdown.
   PRPackedBool mNotifyOnShutdown;
 
+  // True if the media resource is seekable (server supports byte range
+  // requests).
+  PRPackedBool mSeekable;
+
   /******
    * The following member variables can be accessed from any thread.
    ******/
 
   // The state machine object for handling the decoding via
   // oggplay. It is safe to call methods of this object from other
   // threads. Its internal data is synchronised on a monitor. The
   // lifetime of this object is after mPlayState is LOADING and before
--- a/content/media/video/src/nsChannelToPipeListener.cpp
+++ b/content/media/video/src/nsChannelToPipeListener.cpp
@@ -37,21 +37,27 @@
  * ***** END LICENSE BLOCK ***** */
 #include "nsAString.h"
 #include "nsNetUtil.h"
 #include "nsMediaDecoder.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsChannelToPipeListener.h"
 #include "nsICachingChannel.h"
 
-nsChannelToPipeListener::nsChannelToPipeListener(nsMediaDecoder* aDecoder) :
+#define HTTP_OK_CODE 200
+#define HTTP_PARTIAL_RESPONSE_CODE 206
+
+nsChannelToPipeListener::nsChannelToPipeListener(
+    nsMediaDecoder* aDecoder,
+    PRBool aSeeking) :
   mDecoder(aDecoder),
   mIntervalStart(0),
   mIntervalEnd(0),
-  mTotalBytes(0)
+  mTotalBytes(0),
+  mSeeking(aSeeking)
 {
 }
 
 nsresult nsChannelToPipeListener::Init() 
 {
   nsresult rv = NS_NewPipe(getter_AddRefs(mInput), 
                            getter_AddRefs(mOutput),
                            0, 
@@ -93,20 +99,35 @@ nsresult nsChannelToPipeListener::OnStar
   mIntervalStart = PR_IntervalNow();
   mIntervalEnd = mIntervalStart;
   mTotalBytes = 0;
   mDecoder->UpdateBytesDownloaded(mTotalBytes);
   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(aRequest);
   if (hc) {
     PRUint32 responseStatus = 0; 
     hc->GetResponseStatus(&responseStatus);
-    if (responseStatus == 200) {
+    if (mSeeking && responseStatus == HTTP_OK_CODE) {
+      // If we get an OK response but we were seeking,
+      // and therefore expecting a partial response of
+      // HTTP_PARTIAL_RESPONSE_CODE, tell the decoder
+      // we don't support seeking.
+      mDecoder->SetSeekable(PR_FALSE);
+    }
+    else if (!mSeeking && 
+             (responseStatus == HTTP_OK_CODE ||
+              responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
+      // We weren't seeking and got a valid response status,
+      // set the length of the content.
       PRInt32 cl = 0;
       hc->GetContentLength(&cl);
       mDecoder->SetTotalBytes(cl);
+
+      // If we get an HTTP_OK_CODE response to our byte range
+      // request, then we don't support seeking.
+      mDecoder->SetSeekable(responseStatus == HTTP_PARTIAL_RESPONSE_CODE);
     }
   }
 
   nsCOMPtr<nsICachingChannel> cc = do_QueryInterface(aRequest);
   if (cc) {
     PRBool fromCache = PR_FALSE;
     nsresult rv = cc->IsFromCache(&fromCache);
 
--- a/content/media/video/src/nsMediaStream.cpp
+++ b/content/media/video/src/nsMediaStream.cpp
@@ -433,16 +433,26 @@ nsresult nsHttpStreamStrategy::Open(nsIS
 
   nsresult rv = mListener->Init();
   NS_ENSURE_SUCCESS(rv, rv);
   
   if (aStreamListener) {
     *aStreamListener = mListener;
     NS_ADDREF(*aStreamListener);
   } else {
+    // Use a byte range request from the start of the resource.
+    // This enables us to detect if the stream supports byte range
+    // requests, and therefore seeking, early.
+    nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
+    if (hc) {
+      hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"),
+          NS_LITERAL_CSTRING("bytes=0-"),
+          PR_FALSE);
+    }
+ 
     rv = mChannel->AsyncOpen(mListener, nsnull);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   
   rv = mListener->GetInputStream(getter_AddRefs(mPipeInput));
   NS_ENSURE_SUCCESS(rv, rv);
 
   mPosition = 0;
@@ -533,17 +543,17 @@ public:
     nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
     if (hc) {
       nsCAutoString rangeString("bytes=");
       rangeString.AppendInt(mOffset);
       rangeString.Append("-");
       hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE);
     }
 
-    mListener = new nsChannelToPipeListener(mDecoder);
+    mListener = new nsChannelToPipeListener(mDecoder, PR_TRUE);
     NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
 
     mResult = mListener->Init();
     NS_ENSURE_SUCCESS(mResult, mResult);
 
     mResult = mChannel->AsyncOpen(mListener, nsnull);
     NS_ENSURE_SUCCESS(mResult, mResult);
 
--- a/content/media/video/src/nsOggDecoder.cpp
+++ b/content/media/video/src/nsOggDecoder.cpp
@@ -30,16 +30,17 @@
  * 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 <limits>
 #include "prlog.h"
 #include "prmem.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
 #include "nsThreadUtils.h"
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsNetUtil.h"
 #include "nsAudioStream.h"
@@ -262,16 +263,28 @@ public:
   // be locked when calling this method. Returns PR_FALSE if unable to
   // write to the audio device without blocking.
   PRBool PlayAudio(FrameData* aFrame);
 
   // Called from the main thread to get the current frame time. The decoder
   // monitor must be obtained before calling this.
   float GetCurrentTime();
 
+  // Called from the main thread to get the duration. The decoder monitor
+  // must be obtained before calling this. It is in units of milliseconds.
+  PRInt64 GetDuration();
+
+  // Called from the main thread to set the content length of the media
+  // resource. The decoder monitor must be obtained before calling this.
+  void SetContentLength(PRInt64 aLength);
+
+  // Called from the main thread to set whether the media resource can
+  // be seeked. The decoder monitor must be obtained before calling this.
+  void SetSeekable(PRBool aSeekable);
+
   // Get and set the audio volume. The decoder monitor must be
   // obtained before calling this.
   float GetVolume();
   void SetVolume(float aVolume);
 
   // Clear the flag indicating that a playback position change event
   // is currently queued. This is called from the main thread and must
   // be called with the decode monitor held.
@@ -422,16 +435,30 @@ private:
   // time value. Synchronised via decoder monitor.
   float mCurrentFrameTime;
 
   // Volume of playback. 0.0 = muted. 1.0 = full volume. Read/Written
   // from the decode and main threads. Synchronised via decoder
   // monitor.
   float mVolume;
 
+  // Duration of the media resource. It is accessed from the decoder and main
+  // threads. Synchronised via decoder monitor. It is in units of
+  // milliseconds.
+  PRInt64 mDuration;
+
+  // Content Length of the media resource if known. If it is -1 then the
+  // size is unknown. Accessed from the decoder and main threads. Synchronised
+  // via decoder monitor.
+  PRInt64 mContentLength;
+
+  // PR_TRUE if the media resource can be seeked. Accessed from the decoder
+  // and main threads. Synchronised via decoder monitor.
+  PRPackedBool mSeekable;
+
   // PR_TRUE if an event to notify about a change in the playback
   // position has been queued, but not yet run. It is set to PR_FALSE when
   // the event is run. This allows coalescing of these events as they can be
   // produced many times per second. Synchronised via decoder monitor.
   PRPackedBool mPositionChangeQueued;
 };
 
 nsOggDecodeStateMachine::nsOggDecodeStateMachine(nsOggDecoder* aDecoder, nsChannelReader* aReader) :
@@ -450,16 +477,19 @@ nsOggDecodeStateMachine::nsOggDecodeStat
   mReader(aReader),
   mBufferingStart(0),
   mBufferingBytes(0),
   mLastFrameTime(0),
   mState(DECODER_STATE_DECODING_METADATA),
   mSeekTime(0.0),
   mCurrentFrameTime(0.0),
   mVolume(1.0),
+  mDuration(-1),
+  mContentLength(-1),
+  mSeekable(PR_TRUE),
   mPositionChangeQueued(PR_FALSE)
 {
 }
 
 nsOggDecodeStateMachine::~nsOggDecodeStateMachine()
 {
   while (!mDecodedFrames.IsEmpty()) {
     delete mDecodedFrames.Pop();
@@ -768,16 +798,33 @@ void nsOggDecodeStateMachine::SetVolume(
 }
 
 float nsOggDecodeStateMachine::GetCurrentTime()
 {
   //  NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "GetCurrentTime() called without acquiring decoder monitor");
   return mCurrentFrameTime;
 }
 
+PRInt64 nsOggDecodeStateMachine::GetDuration()
+{
+  //  NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "GetDuration() called without acquiring decoder monitor");
+  return mDuration;
+}
+
+void nsOggDecodeStateMachine::SetContentLength(PRInt64 aLength)
+{
+  //  NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "SetContentLength() called without acquiring decoder monitor");
+  mContentLength = aLength;
+}
+
+void nsOggDecodeStateMachine::SetSeekable(PRBool aSeekable)
+{
+   //  NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "SetSeekable() called without acquiring decoder monitor");
+  mSeekable = aSeekable;
+}
 
 void nsOggDecodeStateMachine::Shutdown()
 {
   // oggplay_prepare_for_close cannot be undone. Once called, the
   // mPlayer object cannot decode any more frames. Once we've entered
   // the shutdown state here there's no going back.
   nsAutoMonitor mon(mDecoder->GetMonitor());
   if (mPlayer) {
@@ -1039,30 +1086,51 @@ void nsOggDecodeStateMachine::LoadOggHea
       }
       else if (mAudioTrack == -1 && oggplay_get_track_type(mPlayer, i) == OGGZ_CONTENT_VORBIS) {
         mAudioTrack = i;
         oggplay_set_offset(mPlayer, i, OGGPLAY_AUDIO_OFFSET);
         oggplay_get_audio_samplerate(mPlayer, i, &mAudioRate);
         oggplay_get_audio_channels(mPlayer, i, &mAudioChannels);
         LOG(PR_LOG_DEBUG, ("samplerate: %d, channels: %d", mAudioRate, mAudioChannels));
       }
-      
+ 
       if (oggplay_set_track_active(mPlayer, i) < 0)  {
         LOG(PR_LOG_ERROR, ("Could not set track %d active", i));
       }
     }
-    
+
     if (mVideoTrack == -1) {
       oggplay_set_callback_num_frames(mPlayer, mAudioTrack, OGGPLAY_FRAMES_PER_CALLBACK);
       mCallbackPeriod = 1.0 / (float(mAudioRate) / OGGPLAY_FRAMES_PER_CALLBACK);
     }
     LOG(PR_LOG_DEBUG, ("Callback Period: %f", mCallbackPeriod));
 
     oggplay_use_buffer(mPlayer, OGGPLAY_BUFFER_SIZE);
 
+    // Get the duration from the Ogg file. We only do this if the
+    // content length of the resource is known as we need to seek
+    // to the end of the file to get the last time field. We also
+    // only do this if the resource is seekable.
+    {
+      nsAutoMonitor mon(mDecoder->GetMonitor());
+      if (mState != DECODER_STATE_SHUTDOWN &&
+          mContentLength >= 0 && 
+          mSeekable) {
+        // Don't hold the monitor during the duration
+        // call as it can issue seek requests
+        // and blocks until these are completed.
+        mon.Exit();
+        PRInt64 d = oggplay_get_duration(mPlayer);
+        mon.Enter();
+        mDuration = d;
+      }
+      if (mState == DECODER_STATE_SHUTDOWN)
+        return;
+    }
+
     // Inform the element that we've loaded the Ogg metadata
     nsCOMPtr<nsIRunnable> metadataLoadedEvent = 
       NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, MetadataLoaded); 
     
     NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
   }
 }
 
@@ -1092,30 +1160,32 @@ void nsOggDecoder::SetVolume(float volum
 
   if (mDecodeStateMachine) {
     mDecodeStateMachine->SetVolume(volume);
   }
 }
 
 float nsOggDecoder::GetDuration()
 {
-  // Currently not implemented. Video Spec says to return
-  // NaN if unknown.
-  // TODO: return NaN
-  return 0.0;
+  if (mDuration >= 0) {
+     return static_cast<float>(mDuration) / 1000.0;
+  }
+
+  return std::numeric_limits<float>::quiet_NaN();
 }
 
 nsOggDecoder::nsOggDecoder() :
   nsMediaDecoder(),
   mBytesDownloaded(0),
   mCurrentTime(0.0),
   mInitialVolume(0.0),
   mRequestedSeekTime(-1.0),
-  mContentLength(0),
+  mContentLength(-1),
   mNotifyOnShutdown(PR_FALSE),
+  mSeekable(PR_TRUE),
   mReader(0),
   mMonitor(0),
   mPlayState(PLAY_STATE_PAUSED),
   mNextState(PLAY_STATE_PAUSED)
 {
   MOZ_COUNT_CTOR(nsOggDecoder);
 }
 
@@ -1196,16 +1266,21 @@ nsresult nsOggDecoder::Load(nsIURI* aURI
 
   nsresult rv = mReader->Init(this, mURI, aChannel, aStreamListener);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = NS_NewThread(getter_AddRefs(mDecodeThread));
   NS_ENSURE_SUCCESS(rv, rv);
 
   mDecodeStateMachine = new nsOggDecodeStateMachine(this, mReader);
+  {
+    nsAutoMonitor mon(mMonitor);
+    mDecodeStateMachine->SetContentLength(mContentLength);
+    mDecodeStateMachine->SetSeekable(mSeekable);
+  }
 
   ChangeState(PLAY_STATE_LOADING);
 
   return mDecodeThread->Dispatch(mDecodeStateMachine, NS_DISPATCH_NORMAL);
 }
 
 nsresult nsOggDecoder::Play()
 {
@@ -1297,16 +1372,21 @@ nsIPrincipal* nsOggDecoder::GetCurrentPr
     return nsnull;
   }
 
   return mReader->GetCurrentPrincipal();
 }
 
 void nsOggDecoder::MetadataLoaded()
 {
+  {
+    nsAutoMonitor mon(mMonitor);
+    mDuration = mDecodeStateMachine ? mDecodeStateMachine->GetDuration() : -1;
+  }
+
   if (mElement) {
     mElement->MetadataLoaded();
   }
 }
 
 void nsOggDecoder::FirstFrameLoaded()
 {
   if (mElement) {
@@ -1375,16 +1455,20 @@ PRUint64 nsOggDecoder::GetBytesLoaded()
 PRInt64 nsOggDecoder::GetTotalBytes()
 {
   return mContentLength;
 }
 
 void nsOggDecoder::SetTotalBytes(PRInt64 aBytes)
 {
   mContentLength = aBytes;
+  if (mDecodeStateMachine) {
+    nsAutoMonitor mon(mMonitor);
+    mDecodeStateMachine->SetContentLength(aBytes);
+  } 
 }
 
 void nsOggDecoder::UpdateBytesDownloaded(PRUint64 aBytes)
 {
   mBytesDownloaded = aBytes;
 }
 
 void nsOggDecoder::BufferingStopped()
@@ -1542,8 +1626,23 @@ void nsOggDecoder::PlaybackPositionChang
   // event runs JavaScript that queries the media size, the
   // frame has reflowed and the size updated beforehand.
   Invalidate();
 
   if (mElement && lastTime != mCurrentTime) {
     mElement->DispatchSimpleEvent(NS_LITERAL_STRING("timeupdate"));
   }
 }
+
+void nsOggDecoder::SetSeekable(PRBool aSeekable)
+{
+  mSeekable = aSeekable;
+  if (mDecodeStateMachine) {
+    nsAutoMonitor mon(mMonitor);
+    mDecodeStateMachine->SetSeekable(aSeekable);
+  }
+}
+
+PRBool nsOggDecoder::GetSeekable()
+{
+  return mSeekable;
+}
+
--- a/content/media/video/test/Makefile.in
+++ b/content/media/video/test/Makefile.in
@@ -43,16 +43,17 @@ relativesrcdir  = content/media/video/te
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = 	test_autoplay.html \
                 test_bug461281.html \
                 test_constants.html \
                 test_controls.html \
                 test_currentTime.html \
+                test_duration1.html \
                 test_ended1.html \
                 test_ended2.html \
                 test_networkState.html \
                 test_paused.html \
                 test_readyState.html \
                 test_seek1.html \
                 test_seek2.html \
                 test_seek3.html \
new file mode 100644
--- /dev/null
+++ b/content/media/video/test/test_duration1.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Media test: seek test 1</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<video id='v'
+       src='seek.ogg'
+       onloadedmetadata='return startTest();'></video>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+// Test that getting the duration from a file works
+var completed = false;
+var timeout;
+
+function startTest() {
+  if (completed)
+    return false;
+  var v = document.getElementById('v');
+  ok(Math.round(v.duration*1000) == 3833, "Check duration of video: " + v.duration);
+  completed = true;
+  clearTimeout(timeout);
+  SimpleTest.finish();
+  return false;
+}
+
+timeout = setTimeout(function () {
+		       ok(false, "Test timed out");
+		       SimpleTest.finish();
+		     }, 60000);
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
--- a/content/svg/content/test/Makefile.in
+++ b/content/svg/content/test/Makefile.in
@@ -46,16 +46,17 @@ include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
 		test_bbox.xhtml \
 		bbox-helper.svg \
 		test_dataTypes.html \
 		dataTypes-helper.svg \
 		test_getSubStringLength.xhtml \
 		getSubStringLength-helper.svg \
+		test_pathSeg.xhtml \
 		test_scientific.html \
 		scientific-helper.svg \
 		test_text.html \
 		text-helper.svg \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/svg/content/test/test_pathSeg.xhtml
@@ -0,0 +1,143 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=459953
+-->
+<head>
+  <title>Test for Bug 459953</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=459953">Mozilla Bug 459953</a>
+<p id="display"></p>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+function runTest()
+{
+  var svgns="http://www.w3.org/2000/svg";
+
+  var path1=document.createElementNS(svgns, "path");
+
+  var sseg;
+  
+  var a=10,s=20,d=30,z=9; //Arbitrary numbers for arguments
+  
+  var whatever=true; //This is often so, but here it does not matter
+  
+  sseg=path1.createSVGPathSegMovetoAbs(a, s);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegMovetoRel(a, s);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegLinetoAbs(a, s);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegLinetoRel(a, s);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegLinetoVerticalAbs(a);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegLinetoVerticalRel(a);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegLinetoHorizontalAbs(a);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegLinetoHorizontalRel(a);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegCurvetoCubicAbs(a, s, d, z, a, s);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegCurvetoCubicRel(a, s, d, z, a, s);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegCurvetoCubicSmoothAbs(a, s, d, z);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegCurvetoCubicSmoothRel(a, s, d, z);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegCurvetoQuadraticAbs(a, s, d, z);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegCurvetoQuadraticRel(a, s, d, z);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegCurvetoQuadraticSmoothAbs(a, s);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegCurvetoQuadraticSmoothRel(a, s);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegArcAbs(a, s, d, z, a, whatever, whatever);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegArcRel(a, s, d, z, a, whatever, whatever);
+  path1.pathSegList.appendItem(sseg);
+  sseg=path1.createSVGPathSegClosePath();
+  path1.pathSegList.appendItem(sseg);
+  
+  for(var i=0;i<path1.pathSegList.numberOfItems;i++){
+    var seg=path1.pathSegList.getItem(i);
+    switch(seg.pathSegType){
+    case seg.PATHSEG_MOVETO_ABS:
+      is(seg.pathSegTypeAsLetter, "M", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_MOVETO_REL:
+      is(seg.pathSegTypeAsLetter, "m", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_CLOSEPATH:
+      is(seg.pathSegTypeAsLetter, "z", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_LINETO_ABS:
+      is(seg.pathSegTypeAsLetter, "L", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_LINETO_REL:
+      is(seg.pathSegTypeAsLetter, "l", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_LINETO_VERTICAL_ABS:
+      is(seg.pathSegTypeAsLetter, "V", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_LINETO_VERTICAL_REL:
+      is(seg.pathSegTypeAsLetter, "v", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_LINETO_HORIZONTAL_ABS:
+      is(seg.pathSegTypeAsLetter, "H", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_LINETO_HORIZONTAL_REL:
+      is(seg.pathSegTypeAsLetter, "h", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_CURVETO_CUBIC_ABS:
+      is(seg.pathSegTypeAsLetter, "C", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_CURVETO_CUBIC_REL:
+      is(seg.pathSegTypeAsLetter, "c", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
+      is(seg.pathSegTypeAsLetter, "S", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
+      is(seg.pathSegTypeAsLetter, "s", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_CURVETO_QUADRATIC_ABS:
+      is(seg.pathSegTypeAsLetter, "Q", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_CURVETO_QUADRATIC_REL:
+      is(seg.pathSegTypeAsLetter, "q", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
+      is(seg.pathSegTypeAsLetter, "T", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
+      is(seg.pathSegTypeAsLetter, "t", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_ARC_ABS:
+      is(seg.pathSegTypeAsLetter, "A", "wrong path segment letter");      
+      break;
+    case seg.PATHSEG_ARC_REL:
+      is(seg.pathSegTypeAsLetter, "a", "wrong path segment letter");      
+      break;
+      
+    }
+  }
+  SimpleTest.finish();
+}
+
+window.addEventListener("load", runTest, false);
+]]>
+</script>
+</pre>
+</body>
+</html>
--- a/gfx/cairo/README
+++ b/gfx/cairo/README
@@ -25,15 +25,19 @@ win32-logical-font-scale.patch: set CAIR
 nonfatal-assertions.patch: Make assertions non-fatal
 
 buggy-repeat.patch: Unconditionally turn on buggy-repeat handling to bandaid bug 413583.
 
 tmpfile_wince.patch: Make Windows CE use tmpfile() on windows mobile due to the lack of _open_osfhandle and no fs permissions.
 
 cairo-version-fixes.patch: fix up cairo-version.c/cairo-version.h for in-place builds
 
+win32-ddb-dib.patch: fix for bug 455513; not upstream yet pending feebdack
+
+qpainter-type.patch: add SURFACE_TYPE_QPAINTER to cairo.h
+
 ==== pixman patches ====
 
 endian.patch: include cairo-platform.h for endian macros
 
 ==== disable printing patch ====
 
 disable-printing.patch:  allows us to use NS_PRINTING to disable printing.
--- a/gfx/cairo/cairo/src/cairo-qpainter-surface.cpp
+++ b/gfx/cairo/cairo/src/cairo-qpainter-surface.cpp
@@ -596,16 +596,18 @@ static void
 
 static cairo_status_t
 _cairo_qpainter_surface_clone_similar (void *abstract_surface,
                                        cairo_surface_t *src,
                                        int              src_x,
                                        int              src_y,
                                        int              width,
                                        int              height,
+                                       int              *clone_offset_x,
+                                       int              *clone_offset_y,
                                        cairo_surface_t **clone_out)
 {
     cairo_qpainter_surface_t *qs = (cairo_qpainter_surface_t *) abstract_surface;
     cairo_surface_t *new_surf = NULL;
 
     // For non-image targets, always try to create a QPixmap first
     if (qs->image == NULL && (!_qpixmaps_have_no_alpha || src->content == CAIRO_CONTENT_COLOR))
     {
@@ -638,16 +640,18 @@ static cairo_status_t
 
     _cairo_pattern_fini (&upat.base);
 
     if (status) {
         cairo_surface_destroy (new_surf);
         new_surf = NULL;
     }
 
+    *clone_offset_x = 0;
+    *clone_offset_y = 0;
     *clone_out = new_surf;
     return (cairo_status_t) status;
 }
 
 static cairo_int_status_t
 _cairo_qpainter_surface_get_extents (void *abstract_surface,
                                      cairo_rectangle_int_t *extents)
 {
--- a/gfx/cairo/cairo/src/cairo-win32-private.h
+++ b/gfx/cairo/cairo/src/cairo-win32-private.h
@@ -112,16 +112,19 @@ enum {
     /* Whether we can use StretchBlt with this surface */
     CAIRO_WIN32_SURFACE_CAN_STRETCHBLT = (1<<4),
 
     /* Whether we can use StretchDIBits with this surface */
     CAIRO_WIN32_SURFACE_CAN_STRETCHDIB = (1<<5),
 
     /* Whether we can use GradientFill rectangles with this surface */
     CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT = (1<<6),
+
+    /* if this DDB surface can be converted to a DIB if necessary */
+    CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB = (1<<7),
 };
 
 cairo_status_t
 _cairo_win32_print_gdi_error (const char *context);
 
 cairo_bool_t
 _cairo_surface_is_win32 (cairo_surface_t *surface);
 
--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
+++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
@@ -555,25 +555,86 @@ static cairo_status_t
 	FillRect(local->dc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));
     }
 
     *local_out = local;
 
     return CAIRO_STATUS_SUCCESS;
 }
 
+static void
+_cairo_win32_convert_ddb_to_dib (cairo_win32_surface_t *surface)
+{
+    cairo_win32_surface_t *new_surface;
+    int width = surface->extents.width;
+    int height = surface->extents.height;
+
+    BOOL ok;
+    HBITMAP oldbitmap;
+
+    new_surface = (cairo_win32_surface_t*)
+	_cairo_win32_surface_create_for_dc (surface->dc,
+					    surface->format,
+					    width,
+					    height);
+
+    if (new_surface->base.status)
+	return;
+
+    /* DDB can't be 32bpp, so BitBlt is safe */
+    ok = BitBlt (new_surface->dc,
+		 0, 0, width, height,
+		 surface->dc,
+		 0, 0, SRCCOPY);
+
+    if (!ok)
+	goto out;
+
+    /* Now swap around new_surface and surface's internal bitmap
+     * pointers. */
+    DeleteDC (new_surface->dc);
+    new_surface->dc = NULL;
+
+    oldbitmap = SelectObject (surface->dc, new_surface->bitmap);
+    DeleteObject (oldbitmap);
+
+    surface->image = new_surface->image;
+    surface->is_dib = new_surface->is_dib;
+    surface->bitmap = new_surface->bitmap;
+
+    new_surface->bitmap = NULL;
+    new_surface->image = NULL;
+
+    /* Finally update flags */
+    surface->flags = _cairo_win32_flags_for_dc (surface->dc);
+
+  out:
+    cairo_surface_destroy ((cairo_surface_t*)new_surface);
+}
+
 static cairo_status_t
 _cairo_win32_surface_acquire_source_image (void                    *abstract_surface,
 					   cairo_image_surface_t  **image_out,
 					   void                   **image_extra)
 {
     cairo_win32_surface_t *surface = abstract_surface;
     cairo_win32_surface_t *local = NULL;
     cairo_status_t status;
 
+    if (!surface->image && !surface->is_dib && surface->bitmap &&
+	(surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0)
+    {
+	/* This is a DDB, and we're being asked to use it as a source for
+	 * something that we couldn't support natively.  So turn it into
+	 * a DIB, so that we have an equivalent image surface, as long
+	 * as we're allowed to via flags.
+	 */
+	_cairo_win32_convert_ddb_to_dib (surface);
+    }
+
     if (surface->image) {
 	*image_out = (cairo_image_surface_t *)surface->image;
 	*image_extra = NULL;
 
 	return CAIRO_STATUS_SUCCESS;
     }
 
     status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0,
@@ -2128,8 +2189,66 @@ void
     for (z = 0; z < rd->rdh.nCount; z++) {
 	RECT r = ((RECT*)rd->Buffer)[z];
 	fprintf (stderr, " [%d]: [%d %d %d %d]\n", z, r.left, r.top, r.right - r.left, r.bottom - r.top);
     }
 
     free(rd);
     fflush (stderr);
 }
+
+/**
+ * cairo_win32_surface_set_can_convert_to_dib
+ * @surface: a #cairo_surface_t
+ * @can_convert: a #cairo_bool_t indicating whether this surface can
+ *               be coverted to a DIB if necessary
+ *
+ * A DDB surface with this flag set can be converted to a DIB if it's
+ * used as a source in a way that GDI can't natively handle; for
+ * example, drawing a RGB24 DDB onto an ARGB32 DIB.  Doing this
+ * conversion results in a significant speed optimization, because we
+ * can call on pixman to perform the operation natively, instead of
+ * reading the data from the DC each time.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully
+ * changed, or an error otherwise.
+ * 
+ */
+cairo_status_t
+cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t can_convert)
+{
+    cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
+    if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
+	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+    if (surface->bitmap) {
+	if (can_convert)
+	    surface->flags |= CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB;
+	else
+	    surface->flags &= ~CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_win32_surface_get_can_convert_to_dib
+ * @surface: a #cairo_surface_t
+ * @can_convert: a #cairo_bool_t* that receives the return value
+ *
+ * Returns the value of the flag indicating whether the surface can be
+ * converted to a DIB if necessary, as set by
+ * cairo_win32_surface_set_can_convert_to_dib.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully
+ * retreived, or an error otherwise.
+ * 
+ */
+cairo_status_t
+cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t *can_convert)
+{
+    cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
+    if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
+	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+    *can_convert = ((surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0);
+    return CAIRO_STATUS_SUCCESS;
+}
--- a/gfx/cairo/cairo/src/cairo-win32.h
+++ b/gfx/cairo/cairo/src/cairo-win32.h
@@ -69,16 +69,22 @@ cairo_public cairo_surface_t *
 cairo_win32_surface_get_image (cairo_surface_t *surface);
 
 #if CAIRO_HAS_WIN32_FONT
 
 /*
  * Win32 font support
  */
 
+cairo_public cairo_status_t
+cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t can_convert);
+
+cairo_public cairo_status_t
+cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t *can_convert);
+
 cairo_public cairo_font_face_t *
 cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont);
 
 cairo_public cairo_font_face_t *
 cairo_win32_font_face_create_for_hfont (HFONT font);
 
 cairo_public cairo_font_face_t *
 cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font);
--- a/gfx/cairo/cairo/src/cairo.h
+++ b/gfx/cairo/cairo/src/cairo.h
@@ -1870,16 +1870,17 @@ cairo_surface_status (cairo_surface_t *s
  * @CAIRO_SURFACE_TYPE_QUARTZ: The surface is of type quartz
  * @CAIRO_SURFACE_TYPE_WIN32: The surface is of type win32
  * @CAIRO_SURFACE_TYPE_BEOS: The surface is of type beos
  * @CAIRO_SURFACE_TYPE_DIRECTFB: The surface is of type directfb
  * @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg
  * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2
  * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface
  * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image
+ * @CAIRO_SURFACE_TYPE_QPAINTER: The surface is of type qpainter
  *
  * #cairo_surface_type_t is used to describe the type of a given
  * surface. The surface types are also known as "backends" or "surface
  * backends" within cairo.
  *
  * The type of a surface is determined by the function used to create
  * it, which will generally be of the form cairo_<emphasis>type</emphasis>_surface_create(),
  * (though see cairo_surface_create_similar() as well).
@@ -1908,17 +1909,18 @@ typedef enum _cairo_surface_type {
     CAIRO_SURFACE_TYPE_GLITZ,
     CAIRO_SURFACE_TYPE_QUARTZ,
     CAIRO_SURFACE_TYPE_WIN32,
     CAIRO_SURFACE_TYPE_BEOS,
     CAIRO_SURFACE_TYPE_DIRECTFB,
     CAIRO_SURFACE_TYPE_SVG,
     CAIRO_SURFACE_TYPE_OS2,
     CAIRO_SURFACE_TYPE_WIN32_PRINTING,
-    CAIRO_SURFACE_TYPE_QUARTZ_IMAGE
+    CAIRO_SURFACE_TYPE_QUARTZ_IMAGE,
+    CAIRO_SURFACE_TYPE_QPAINTER
 } cairo_surface_type_t;
 
 cairo_public cairo_surface_type_t
 cairo_surface_get_type (cairo_surface_t *surface);
 
 cairo_public cairo_content_t
 cairo_surface_get_content (cairo_surface_t *surface);
 
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/qpainter-type.patch
@@ -0,0 +1,21 @@
+diff --git a/gfx/cairo/cairo/src/cairo.h b/gfx/cairo/cairo/src/cairo.h
+--- a/gfx/cairo/cairo/src/cairo.h
++++ b/gfx/cairo/cairo/src/cairo.h
+@@ -1875,6 +1875,7 @@
+  * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2
+  * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface
+  * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image
++ * @CAIRO_SURFACE_TYPE_QPAINTER: The surface is of type qpainter
+  *
+  * #cairo_surface_type_t is used to describe the type of a given
+  * surface. The surface types are also known as "backends" or "surface
+@@ -1913,7 +1914,8 @@
+     CAIRO_SURFACE_TYPE_SVG,
+     CAIRO_SURFACE_TYPE_OS2,
+     CAIRO_SURFACE_TYPE_WIN32_PRINTING,
+-    CAIRO_SURFACE_TYPE_QUARTZ_IMAGE
++    CAIRO_SURFACE_TYPE_QUARTZ_IMAGE,
++    CAIRO_SURFACE_TYPE_QPAINTER
+ } cairo_surface_type_t;
+ 
+ cairo_public cairo_surface_type_t
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/win32-ddb-dib.patch
@@ -0,0 +1,182 @@
+b=455513; add optional flag to allow converting a DDB to a DIB internally, if the surface is every used as a source; r=jmuizelaar
+
+If a DDB is used as a source for an operation that can't be handled
+natively by GDI, we end up needing to take a really slow path (creating a
+temporary surface for acquire_source) for each operation.  If we convert
+the DDB to a DIB, we then end up having a real image buffer and can hand
+things off to pixman directly.
+
+This isn't the default mode because I'm not sure if there are cases where a
+DDB is explicitly needed (e.g. for printing), and it would change
+current cairo behaviour.  It might become the default at some point in the
+future.
+
+diff --git a/gfx/cairo/cairo/src/cairo-win32-private.h b/gfx/cairo/cairo/src/cairo-win32-private.h
+--- a/gfx/cairo/cairo/src/cairo-win32-private.h
++++ b/gfx/cairo/cairo/src/cairo-win32-private.h
+@@ -117,6 +117,9 @@
+ 
+     /* Whether we can use GradientFill rectangles with this surface */
+     CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT = (1<<6),
++
++    /* if this DDB surface can be converted to a DIB if necessary */
++    CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB = (1<<7),
+ };
+ 
+ cairo_status_t
+diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
+@@ -560,6 +560,56 @@
+     return CAIRO_STATUS_SUCCESS;
+ }
+ 
++static void
++_cairo_win32_convert_ddb_to_dib (cairo_win32_surface_t *surface)
++{
++    cairo_win32_surface_t *new_surface;
++    int width = surface->extents.width;
++    int height = surface->extents.height;
++
++    BOOL ok;
++    HBITMAP oldbitmap;
++
++    new_surface = (cairo_win32_surface_t*)
++	_cairo_win32_surface_create_for_dc (surface->dc,
++					    surface->format,
++					    width,
++					    height);
++
++    if (new_surface->base.status)
++	return;
++
++    /* DDB can't be 32bpp, so BitBlt is safe */
++    ok = BitBlt (new_surface->dc,
++		 0, 0, width, height,
++		 surface->dc,
++		 0, 0, SRCCOPY);
++
++    if (!ok)
++	goto out;
++
++    /* Now swap around new_surface and surface's internal bitmap
++     * pointers. */
++    DeleteDC (new_surface->dc);
++    new_surface->dc = NULL;
++
++    oldbitmap = SelectObject (surface->dc, new_surface->bitmap);
++    DeleteObject (oldbitmap);
++
++    surface->image = new_surface->image;
++    surface->is_dib = new_surface->is_dib;
++    surface->bitmap = new_surface->bitmap;
++
++    new_surface->bitmap = NULL;
++    new_surface->image = NULL;
++
++    /* Finally update flags */
++    surface->flags = _cairo_win32_flags_for_dc (surface->dc);
++
++  out:
++    cairo_surface_destroy ((cairo_surface_t*)new_surface);
++}
++
+ static cairo_status_t
+ _cairo_win32_surface_acquire_source_image (void                    *abstract_surface,
+ 					   cairo_image_surface_t  **image_out,
+@@ -568,6 +618,17 @@
+     cairo_win32_surface_t *surface = abstract_surface;
+     cairo_win32_surface_t *local = NULL;
+     cairo_status_t status;
++
++    if (!surface->image && !surface->is_dib && surface->bitmap &&
++	(surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0)
++    {
++	/* This is a DDB, and we're being asked to use it as a source for
++	 * something that we couldn't support natively.  So turn it into
++	 * a DIB, so that we have an equivalent image surface, as long
++	 * as we're allowed to via flags.
++	 */
++	_cairo_win32_convert_ddb_to_dib (surface);
++    }
+ 
+     if (surface->image) {
+ 	*image_out = (cairo_image_surface_t *)surface->image;
+@@ -2133,3 +2194,61 @@
+     free(rd);
+     fflush (stderr);
+ }
++
++/**
++ * cairo_win32_surface_set_can_convert_to_dib
++ * @surface: a #cairo_surface_t
++ * @can_convert: a #cairo_bool_t indicating whether this surface can
++ *               be coverted to a DIB if necessary
++ *
++ * A DDB surface with this flag set can be converted to a DIB if it's
++ * used as a source in a way that GDI can't natively handle; for
++ * example, drawing a RGB24 DDB onto an ARGB32 DIB.  Doing this
++ * conversion results in a significant speed optimization, because we
++ * can call on pixman to perform the operation natively, instead of
++ * reading the data from the DC each time.
++ *
++ * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully
++ * changed, or an error otherwise.
++ * 
++ */
++cairo_status_t
++cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t can_convert)
++{
++    cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
++    if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
++	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
++
++    if (surface->bitmap) {
++	if (can_convert)
++	    surface->flags |= CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB;
++	else
++	    surface->flags &= ~CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB;
++    }
++
++    return CAIRO_STATUS_SUCCESS;
++}
++
++/**
++ * cairo_win32_surface_get_can_convert_to_dib
++ * @surface: a #cairo_surface_t
++ * @can_convert: a #cairo_bool_t* that receives the return value
++ *
++ * Returns the value of the flag indicating whether the surface can be
++ * converted to a DIB if necessary, as set by
++ * cairo_win32_surface_set_can_convert_to_dib.
++ *
++ * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully
++ * retreived, or an error otherwise.
++ * 
++ */
++cairo_status_t
++cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t *can_convert)
++{
++    cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
++    if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
++	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
++
++    *can_convert = ((surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0);
++    return CAIRO_STATUS_SUCCESS;
++}
+diff --git a/gfx/cairo/cairo/src/cairo-win32.h b/gfx/cairo/cairo/src/cairo-win32.h
+--- a/gfx/cairo/cairo/src/cairo-win32.h
++++ b/gfx/cairo/cairo/src/cairo-win32.h
+@@ -74,6 +74,12 @@
+  * Win32 font support
+  */
+ 
++cairo_public cairo_status_t
++cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t can_convert);
++
++cairo_public cairo_status_t
++cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t *can_convert);
++
+ cairo_public cairo_font_face_t *
+ cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont);
+ 
--- a/gfx/thebes/src/cairo-xlib-utils.c
+++ b/gfx/thebes/src/cairo-xlib-utils.c
@@ -54,16 +54,18 @@
 
 #if 0
 #include <stdio.h>
 #define CAIRO_GDK_DRAWING_NOTE(m) fprintf(stderr, m)
 #else
 #define CAIRO_GDK_DRAWING_NOTE(m) do {} while (0)
 #endif
 
+#define GDK_PIXMAP_SIZE_MAX 32767
+
 static cairo_user_data_key_t pixmap_free_key;
 static void pixmap_free_func (void *data)
 {
     GdkPixmap *pixmap = (GdkPixmap *) data;
     g_object_unref(pixmap);
 }
 
 /* We have three basic strategies available:
@@ -313,16 +315,20 @@ static cairo_bool_t
 }
 
 static cairo_surface_t *
 _create_temp_xlib_surface (cairo_t *cr, Display *dpy, int width, int height,
                            cairo_gdk_drawing_support_t capabilities)
 {
     cairo_surface_t *result = NULL;
 
+    if (width >= GDK_PIXMAP_SIZE_MAX ||
+        height >= GDK_PIXMAP_SIZE_MAX)
+        return NULL;
+
     /* base the temp surface on the *screen* surface, not any intermediate
      * group surface, because the screen surface is more likely to have
      * characteristics that the xlib-using code is likely to be happy with */
     cairo_surface_t *target = cairo_get_target (cr);
     Drawable target_drawable = cairo_xlib_surface_get_drawable (target);
     GdkDrawable *gdk_target_drawable = GDK_DRAWABLE(gdk_xid_table_lookup(target_drawable));
 
     GdkPixmap *pixmap = NULL;
--- a/gfx/thebes/src/gfxPlatformGtk.cpp
+++ b/gfx/thebes/src/gfxPlatformGtk.cpp
@@ -73,16 +73,18 @@
 #endif
 
 #include <fontconfig/fontconfig.h>
 
 #include "nsMathUtils.h"
 
 #include "lcms.h"
 
+#define GDK_PIXMAP_SIZE_MAX 32767
+
 #ifndef MOZ_PANGO
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #endif
 
 double gfxPlatformGtk::sDPI = -1.0;
 gfxFontconfigUtils *gfxPlatformGtk::sFontconfigUtils = nsnull;
 
@@ -144,16 +146,21 @@ gfxPlatformGtk::~gfxPlatformGtk()
 #endif
 }
 
 already_AddRefed<gfxASurface>
 gfxPlatformGtk::CreateOffscreenSurface(const gfxIntSize& size,
                                        gfxASurface::gfxImageFormat imageFormat)
 {
     nsRefPtr<gfxASurface> newSurface = nsnull;
+    PRBool sizeOk = PR_TRUE;
+
+    if (size.width >= GDK_PIXMAP_SIZE_MAX ||
+        size.height >= GDK_PIXMAP_SIZE_MAX)
+        sizeOk = PR_FALSE;
 
 #ifdef MOZ_X11
     int glitzf;
     int xrenderFormatID;
     switch (imageFormat) {
         case gfxASurface::ImageFormatARGB32:
             glitzf = 0; // GLITZ_STANDARD_ARGB32;
             xrenderFormatID = PictStandardARGB32;
@@ -180,17 +187,17 @@ gfxPlatformGtk::CreateOffscreenSurface(c
     Display* display = GDK_DISPLAY();
     if (!display)
         return nsnull;
 
     GdkPixmap* pixmap = nsnull;
     XRenderPictFormat* xrenderFormat =
         XRenderFindStandardFormat(display, xrenderFormatID);
 
-    if (xrenderFormat) {
+    if (xrenderFormat && sizeOk) {
         pixmap = gdk_pixmap_new(nsnull, size.width, size.height,
                                 xrenderFormat->depth);
 
         if (pixmap) {
             gdk_drawable_set_colormap(GDK_DRAWABLE(pixmap), nsnull);
             newSurface = new gfxXlibSurface(display,
                                             GDK_PIXMAP_XID(GDK_DRAWABLE(pixmap)),
                                             xrenderFormat,
@@ -206,28 +213,31 @@ gfxPlatformGtk::CreateOffscreenSurface(c
             // Ignore and let's fall back to image surfaces.
             newSurface = nsnull;
         }
 
         // always unref; SetGdkDrawable takes its own ref
         if (pixmap)
             g_object_unref(pixmap);
     }
-
-    if (!newSurface) {
-        // We don't have Render or we couldn't create an xlib surface for
-        // whatever reason; fall back to image surface for the data.
-        newSurface = new gfxImageSurface(gfxIntSize(size.width, size.height), imageFormat);
-    }
 #endif
 
 #ifdef MOZ_DFB
-    newSurface = new gfxDirectFBSurface(size, imageFormat);
+    if (sizeOk)
+        newSurface = new gfxDirectFBSurface(size, imageFormat);
 #endif
 
+
+    if (!newSurface) {
+        // We couldn't create a native surface for whatever reason;
+        // e.g., no RENDER, bad size, etc.
+        // Fall back to image surface for the data.
+        newSurface = new gfxImageSurface(gfxIntSize(size.width, size.height), imageFormat);
+    }
+
     if (newSurface) {
         gfxContext tmpCtx(newSurface);
         tmpCtx.SetOperator(gfxContext::OPERATOR_CLEAR);
         tmpCtx.Paint();
     }
 
     return newSurface.forget();
 }
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/451334-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body onload="document.body.style.MozColumnWidth = '1px';">
+
+<div style="display: inline-block;"></div><div style="float: left;"><div style="height: 32px;"></div></div><div>
+<div style="clear: both;"><br></div><div style="float: left;"></div></div>
+
+</body>
+</html>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -151,8 +151,9 @@ load 423055-1.html
 load 425253-1.html
 load 426272-1.html
 load 428263-1.html
 load 429981-1.html
 load 430352-1.html
 load 437156-1.html
 load 438259-1.html
 load 448903-1.html
+load 451334-1.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/cross-iframe-1-inner-1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	html, body {
+		margin: 0;
+		padding: 0;
+	}
+
+	</style>
+</head>
+<body>
+
+<p><span style="font-family: MarkA">A</span><span style="font-family: MarkB">B</span><span style="font-family: MarkC">C</span></p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/cross-iframe-1-inner-2.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkB";
+		src: url(../fonts/markB.ttf);
+	}
+
+	html, body {
+		margin: 0;
+		padding: 0;
+	}
+
+	</style>
+</head>
+<body>
+
+<p><span style="font-family: MarkA">A</span><span style="font-family: MarkB">B</span><span style="font-family: MarkC">C</span></p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/cross-iframe-1-ref.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkD";
+		src: url(../fonts/markD.ttf);
+	}
+
+	div {
+		margin: 0;
+		border: none;
+		padding: 0;
+		position: absolute;
+		left: 0;
+	}
+
+	</style>
+</head>
+<body>
+
+<!-- use nonexistent family to prevent kerning -->
+<p><span style="font-family: Nonexistent1">A</span><span style="font-family: Nonexistent2">B</span><span style="font-family: MarkD">D</span></p>
+
+<div style="top: 4em" src="cross-iframe-1-inner-1.html">
+<p><span style="font-family: MarkD">D</span><span style="font-family: Nonexistent2">B</span><span style="font-family: Nonexistent1">C</span></p>
+</div>
+<div style="top: 7em" src="cross-iframe-1-inner-2.html">
+<p><span>A</span><span style="font-family: MarkD">D</span><span>C</span></p>
+</div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/cross-iframe-1.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkC";
+		src: url(../fonts/markC.ttf);
+	}
+
+	iframe {
+		margin: 0;
+		border: none;
+		padding: 0;
+		position: absolute;
+		left: 0;
+	}
+
+	</style>
+</head>
+<body>
+
+<p><span style="font-family: MarkA">A</span><span style="font-family: MarkB">B</span><span style="font-family: MarkC">C</span></p>
+
+<iframe style="top: 4em" src="cross-iframe-1-inner-1.html"></iframe>
+<iframe style="top: 7em" src="cross-iframe-1-inner-2.html"></iframe>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/download-1-notref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	body { font-family: "MarkA"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/download-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	body { font-family: "MarkA"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/download-2-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkB";
+		src: url(../fonts/markB.ttf);
+	}
+
+	body { font-family: "MarkB"; }
+
+	</style>
+</head>
+<body>
+
+<p>B</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/download-2.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	body { font-family: "MarkA"; }
+
+	</style>
+</head>
+<body>
+
+<p>A</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/fallback-to-system-1-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	span {
+		/* to ensure the same vertical positioning of the text */
+		display: inline-block;
+		height: 3em;
+		width: 1em;
+		vertical-align: baseline;
+	}
+
+	</style>
+</head>
+<body>
+
+<p>DEF<span></span></p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/fallback-to-system-1.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	body { font-family: "MarkA"; line-height: 1.5em; }
+
+	span {
+		/* to ensure the same vertical positioning of the text */
+		display: inline-block;
+		height: 3em;
+		width: 1em;
+		vertical-align: baseline;
+	}
+
+	</style>
+</head>
+<body>
+
+<p>DEF<span></span></p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/multiple-descriptor-1-notref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+<p>ABC</p>
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/multiple-descriptor-1-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	/* The last descriptor in a given rule wins, per CSS 2.0 */
+
+	@font-face {
+		src: url(../fonts/markA.ttf);
+		font-family: "MarkA";
+	}
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+<p style="font-family: MarkA">ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/multiple-descriptor-1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	/* The last descriptor in a given rule wins, per CSS 2.0 */
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/markB.ttf);
+		src: url(../fonts/markA.ttf);
+		font-family: "Two";
+	}
+
+	</style>
+</head>
+<body>
+
+<p style="font-family: One">ABC</p>
+<p style="font-family: Two">ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/multiple-in-family-1-notref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	body { font-family: "MarkA"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/multiple-in-family-1-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	@font-face {
+		font-family: "MarkB";
+		src: url(../fonts/markB.ttf);
+	}
+
+	</style>
+</head>
+<body>
+
+<p><span style="font-family: MarkA">A</span><span style="font-family: MarkB">B</span>C</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/multiple-in-family-1.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	@font-face {
+		font-family: "MarkB";
+		src: url(../fonts/markB.ttf);
+	}
+
+	body { font-family: "MarkA", "MarkB"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/name-override-1-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkD";
+		src: url(../fonts/markD.ttf);
+	}
+
+	body { font-family: "MarkD"; }
+
+	</style>
+</head>
+<body>
+
+<p>DBD</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/name-override-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "Foobar";
+		src: url(../fonts/markA.ttf), url(../fonts/markC.ttf);
+	}
+
+	body { font-family: "Foobar"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/name-override-simple-1-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkD";
+		src: url(../fonts/markD.ttf);
+	}
+
+	body { font-family: "MarkD"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABD</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/name-override-simple-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "Foobar";
+		src: url(../fonts/markC.ttf);
+	}
+
+	body { font-family: "Foobar"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/order-1.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/mark2A.ttf);
+	}
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/markA.ttf);
+	}
+
+	body { font-family: "One"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/order-2.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/markB.ttf);
+	}
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/mark2B.ttf);
+	}
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/mark2A.ttf);
+	}
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/markA.ttf);
+	}
+
+	body { font-family: "One"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/order-3.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/markB.ttf);
+	}
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/mark2A.ttf);
+	}
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/mark2B.ttf);
+	}
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/markA.ttf);
+	}
+
+	body { font-family: "One"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/prop-order-over-rule-order-1a.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	@font-face {
+		font-family: "MarkB";
+		src: url(../fonts/markB.ttf);
+	}
+
+	body { font-family: "MarkA", "MarkB"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/prop-order-over-rule-order-1b.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	@font-face {
+		font-family: "MarkB";
+		src: url(../fonts/markB.ttf);
+	}
+
+	body { font-family: "MarkB", "MarkA"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/prop-order-over-rule-order-2a.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkB";
+		src: url(../fonts/markB.ttf);
+	}
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	body { font-family: "MarkA", "MarkB"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/prop-order-over-rule-order-2b.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkB";
+		src: url(../fonts/markB.ttf);
+	}
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	body { font-family: "MarkB", "MarkA"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/reftest.list
@@ -0,0 +1,27 @@
+# Everything here uses HTTP(..) because they use fonts in ../fonts/.  We
+# can't use file:/// URLs because of cross-directory access restrictions
+# on file: URLs.
+
+fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") HTTP(..) != download-1.html download-1-notref.html
+fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") HTTP(..) == download-2.html download-2-ref.html
+HTTP(..) != download-2.html about:blank
+HTTP(..) == fallback-to-system-1.html fallback-to-system-1-ref.html
+fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") HTTP(..) == name-override-simple-1.html name-override-simple-1-ref.html
+fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") HTTP(..) != name-override-simple-1.html download-1-notref.html
+fails HTTP(..) == name-override-1.html name-override-1-ref.html
+HTTP(..) == multiple-descriptor-1.html multiple-descriptor-1-ref.html
+fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") HTTP(..) != multiple-descriptor-1.html multiple-descriptor-1-notref.html
+HTTP(..) == src-list-1.html src-list-1-ref.html
+fails-if(MOZ_WIDGET_TOOLKIT!="gtk2") HTTP(..) == src-list-2.html src-list-2-ref.html
+fails-if(MOZ_WIDGET_TOOLKIT!="gtk2") HTTP(..) == src-list-3.html src-list-3-ref.html
+# FIXME: The behavior here is neither mandated nor specified by the spec, but
+# it really ought to be.
+HTTP(..) == order-1.html src-list-1-ref.html
+fails-if(MOZ_WIDGET_TOOLKIT!="gtk2") HTTP(..) == order-2.html src-list-2-ref.html
+fails-if(MOZ_WIDGET_TOOLKIT!="gtk2") HTTP(..) == order-3.html src-list-3-ref.html
+fails-if(MOZ_WIDGET_TOOLKIT=="cocoa") random-if(MOZ_WIDGET_TOOLKIT=="windows") HTTP(..) == multiple-in-family-1.html multiple-in-family-1-ref.html
+fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") fails-if(MOZ_WIDGET_TOOLKIT=="cocoa") random-if(MOZ_WIDGET_TOOLKIT=="windows") HTTP(..) != multiple-in-family-1.html multiple-in-family-1-notref.html
+random-if(MOZ_WIDGET_TOOLKIT=="windows") HTTP(..) == prop-order-over-rule-order-1a.html prop-order-over-rule-order-2a.html
+random-if(MOZ_WIDGET_TOOLKIT=="windows") HTTP(..) == prop-order-over-rule-order-1b.html prop-order-over-rule-order-2b.html
+random-if(MOZ_WIDGET_TOOLKIT=="windows") fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") HTTP(..) != prop-order-over-rule-order-1a.html prop-order-over-rule-order-1b.html
+fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") HTTP(..) == cross-iframe-1.html cross-iframe-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/src-list-1-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	body { font-family: "MarkA"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/src-list-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/markA.ttf), url(../fonts/mark2A.ttf);
+	}
+
+	body { font-family: "One"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/src-list-2-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	@font-face {
+		font-family: "Mark2B";
+		src: url(../fonts/mark2B.ttf);
+	}
+
+	</style>
+</head>
+<body>
+
+<p><span style="font-family: MarkA">A</span><span style="font-family: Mark2B">B</span>C</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/src-list-2.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/markA.ttf), url(../fonts/mark2A.ttf), url(../fonts/mark2B.ttf), url(../fonts/markB.ttf);
+	}
+
+	body { font-family: "One"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/src-list-3-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "MarkA";
+		src: url(../fonts/markA.ttf);
+	}
+
+	@font-face {
+		font-family: "Mark2B";
+		src: url(../fonts/mark2B.ttf);
+	}
+
+	</style>
+</head>
+<body>
+
+<p><span style="font-family: MarkA">A</span><span style="font-family: Mark2B">B</span>C</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/font-face/src-list-3.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+	"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+	<title></title>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+
+	@font-face {
+		font-family: "One";
+		src: url(../fonts/markA.ttf), url(../fonts/mark2B.ttf), url(../fonts/mark2A.ttf), url(../fonts/markB.ttf);
+	}
+
+	body { font-family: "One"; }
+
+	</style>
+</head>
+<body>
+
+<p>ABC</p>
+
+</body>
+</html>
new file mode 100755
--- /dev/null
+++ b/layout/reftests/fonts/mark-generate.py
@@ -0,0 +1,68 @@
+#!/usr/bin/python
+# vim: set shiftwidth=4 tabstop=8 autoindent 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/
+#
+# 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 mark-generate.py .
+#
+# The Initial Developer of the Original Code is the Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2008
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   L. David Baron <dbaron@dbaron.org>, Mozilla Corporation (original author)
+#
+# 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 *****
+
+# For general fontforge documentation, see:
+#   http://fontforge.sourceforge.net/
+# For fontforge scripting documentation, see:
+#   http://fontforge.sourceforge.net/scripting-tutorial.html
+#   http://fontforge.sourceforge.net/scripting.html
+# and most importantly:
+#   http://fontforge.sourceforge.net/python.html
+
+# To install what you need, on Ubuntu,
+#   sudo apt-get install python-fontforge
+
+import fontforge
+
+# generate a set of fonts, each with our special glyph at one codepoint,
+# and nothing else
+for codepoint in range(ord("A"), ord("D") + 1):
+    for (mark, width) in [("", 1500), ("2", 1800)]:
+        charname = chr(codepoint)
+        f = fontforge.font()
+        n = "Mark" + mark + charname
+        f.fontname = n
+        f.familyname = n
+        f.fullname = n
+        f.copyright = "Copyright (c) 2008 Mozilla Corporation"
+
+        g = f.createChar(codepoint, charname)
+        g.importOutlines("mark" + mark + "-glyph.svg")
+        g.width = width
+
+        f.generate("mark" + mark + charname + ".ttf")
new file mode 100644
--- /dev/null
+++ b/layout/reftests/fonts/mark-glyph.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 1500 1000" width="1500px" height="1000px">
+  <rect x="500" y="100" height="200" width="500" fill="black" />
+  <rect x="300" y="400" height="200" width="900" fill="black" />
+  <!-- baseline defaults to being at 800 -->
+  <rect x="100" y="700" height="200" width="1300" fill="black" />
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/fonts/mark2-glyph.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 1800 1000" width="1800px" height="1000px">
+  <polygon fill="black" points="100,100 100,800 1700,800 1700,100 1500,100 1500,600 300,600 300,100" />
+</svg>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..063cab5bcd33bed3761ccb981bb401512ec50538
GIT binary patch
literal 1524
zc%02tOK1~882)B=vuU+ziw_hkY_#|!aTBRT3Dw#fJeXRjmePZ=Bxw>%x?vL^Q4d1#
zQayN-UR3DS()Owc5k<v|C-LAx4?;nF9P}Vq<2RFSRIuRD8D{_Qf6h0Pe*ytu6|O+X
zU^00kH88XA3<$iUwe$GUp4iF7>^?w@P#;d0oHEwvpQu0JI-EXNwGh&3fNs!l<eW;m
zUS}l)UQlnz6>DSXNAI}wucLlBpLH^yhi+dYe$BNnPebdy;40RV{;qtfdSU%WT%|rs
zy}jtB9prI>`7P8}l$;A?9L0Y6<J7Hjr<A?>ep7({8-TX9>{hDv8FX|1Y5I3To{j76
z?w8L`uStBxN_~MAJdQ_7O&J2rY3;t`3k{9I9Ay~|%6;iqm3|+FvKY-AO=U^jAw(KA
zf-T|;$W5D6wXQd$c!Q|WWP;h+toBFs!LcJ5R_R5rZq?%$RoTrLG79-pt_-2>uTDQE
zK}{&#fTdVA*!J?bQQ!K*rE0?bwYg-~yn(+mTW0A05c7Wz)x+!;IeQ>)fp;u+X;VB~
z5Ro~7J&F0Cfe*1cGw?bwuWN*u)1^5e&GSK1d{4{^Y2Vus>e#-wV{1sXE<AanO)ZXv
zv=Moe8Nbtlz*-r^m3eB+KK=GUyZ@t2d|B*ouvv|n=DlyjfR6=CO!%11gPT6qu?6pZ
zY`_#7d>lZ#IOt=O_EWMe4B2zXRF=d7Z8+;=jd;?>I{I<T#|GL&i;n}a#AY9xv=5>W
zF3PCELjgIy)hat~2h!L{8H3FkCrUBuA|r~N4t1I3F;9LS<`js=fj+lf^9s3q)!LEX
zX~k?iZl&Cdg<{dM`dqK<dQP?AGA_j=4`;YRFBQi-6O$5I-f)5E;oDASGljF1NmaP2
zq(_2TMJqyD77i$4zbZ8>-3TMf8E(=?(jBiRT`!llViDUqU^R9m=nhAtVM%itO2gdG
zL&ZOyrRqi1?zUop;jCBTi7eZWMr_-*mZ`J!D(e%p|Lsj0%BrEO(J6fA+EU!3o@*ba
I{QW=gCs2#VG5`Po
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..541df8dc21b96ac9d3f07860bbcd4632690d5494
GIT binary patch
literal 1524
zc%02tOK1~882)B=vq`m8OH~9FH@+=2-9)Of1eH{S2U82yB0VTek|v=^H*Dgg>Ol}M
z)q_XzqJn3&z1o9<q8<eCq6ZINgkF3c>Oru^ZzkKcLJJ<9VfO$2=X^8yClCPIa1}ZR
zQmMgo|HGNrK;Q$dn@*hUj-C3Q+YgA-)Q2)<r=qXczfgb5mCT&4SqN+6fNs!l<eh4z
zUS}l)-ck?cOXDLKhVQ%ducdyakaM!%Pu{ys{E=&4frgeR!8X>C{_TZw?c%x(xK8~s
z^))3o<Dh^+=7*@SC_5J`IEDlC$EjOmPC57J+r|L>w*al9;#O<*N9f`H59sfNJR8^h
zE$`l(U7h%WmHG@VcoC13n=%ZT5435?7aAIaIm$8|l>5@JDlHy{vKY-AO=U^jVMH1=
zf*~;ra?>VMt(y%gJ|QYJnP9dwtNm4dVB~0)Rgy^RRy~ekmEDXXqp&aK$`b1S+T_P1
zs0pPTuoTM%+a!M*^{qdfuO`fe%_Xbm4J^rQxuO3{(EmJC53^t7JP3IUykpU+-Qm%K
zh)fD}C#Hf1zQiWYz{kXtt`TBVm*#*pPX$f!D={UceQ$fXeb?UhZDG+e^YW#3XKo~{
zot8J5^*b#HbjTpC>?`B(tDjG`>EEqlcJ4@n%_=->-uq7U`&hulH6OEiaNEZ^w&Jso
z4VYrRj{{gE4*A%m{fz7iL-yQpl_jx2E6({?BcAZFjw871V*{-s<l_J=vDwEa?E~n8
ziwef!p@=-+YK<MY8yW1OjKSuN6Qvn-i4i4Ehq}!2m?ysubBaV`K%ZL~_lo&K&Dx#W
zW5sMcZl&Ez#Zt+!`dqK#dQPqAGA_*|52M^5NyYI-V^Si=8!qxZeB0?<ws?**r3%-S
z^hhwLXhlfNLa!pWP^n?*K^LN&T}}E(xns4I>*aG+EMi-|R%1tko~~%LOVV71(h&Fa
zQ1y>zsd`bhyR8^tDCbpqBFna;5!<${W$Mho%KA0hmwJ<ivTE4T=oG$lZ9eY#!5d$t
IT>PK+2Pz-NG5`Po
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..798eddd32b4b763377192b0c6c4ad28146053b98
GIT binary patch
literal 1524
zc%02tKWGzC9RA+j<<e@a)~YyExTvTVl3r3OQ9>0{gM+DsYLO1gk)%mz(hJw9jXDU$
zr8+o@iwd1f+tm&o6zw983Jxwp7e%lRf;E2c<r)<%IQky%e&6r!``*j%1p+`jZbQdV
zDs>?}IJde21m4ivb$+-fcCns|1L6|(kxbdCV5k0x`ZCw)%(be8kX8e9gMK6LOjhc3
zRzl!4^_G08Hg<jViA(=3>bD9xC;NH$@lE1)T*nGDv_1{CvzGK9ER?HLyZ7P_^?B+$
zN^ZtM0T-CxLVZiwnX2F{j?+(0uqK>xZsE(m0R8s?t+V1zR_pWV<NmYsAB8*{*W312
zD_6EBzGADsN(-LHBju(H0p_f>B>6%^V=zZqMuT!+`c<XhhoLM+Ge=Wd(sl}wMvY*L
zSOd9fGpg3Th7|7+6`D*iTbtGXs6I4yCd(>GBz3DE$EeC~#*k6SmvUtZb$@mFF$roy
z=>{ytvcWdV-$s4w%Nx~%`D=5@s(Ax{Wwz<i|0(MK9jb@fFLL%n-U9Df9MvB3WI;q0
z1bPySK?5IS3ufS5Vo}!!v7k$HK$;hWrud#%6w=<?5$ZVH+i@TyT327Z&>pUjg|tiZ
zCbNF01%XZ(#Fc$%%)k7$tS$X$6Km@O4K~{_*Sz;b81%7#iD@6RdGNr;I`-p(j}4e&
zkB<Y`Ax`?(r2Vq&3PbkXIh7@`KpU?5SR<bCv5o;e^09$7(c<F(EV0kWChbG)QWq7}
z;Gu{--)fZ|cLW)9Q^sI(#);C5y1|GNr$b$4dCZevhdD)}3AUzNsd>eGp=uq;bXzgo
zj$3K>MzK_KtfcEzT+gW%UB;!E<YAl}B&j&wcuY#<c*8}Whi^Nb%NDOvrc~jok{$`>
z6s-tpSvaAH{i@Wk^dXEWXShipDR-ipa=m=cibZVegw@!Qpf4Pah9%8SD2;GG50n1!
zELAV6cDEG+jO4sYp2)K8XvDT{Ym+(~ud+T(``_NAp{yFaMRf|_xwaAatpDyODS!XZ
F`w0WT#xei^
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ab0765c30a05734a2e374f8ff0f3ca9522c37369
GIT binary patch
literal 1524
zc%02tO=uHA6#iy+vuU;3ma2GA+^E<eXu3(M#1d3&4IWG_D5dnEEJ>P#Cf%@2wXGh6
z;-z}<D7~oAv)Fo6(1T*Vc@aE#@FMi$&!HlMHGVVMrWIQ7=nS*(dvD%*^UcefKmgc<
zo6s?sNL)$|JX(1J1m4qn;KIeO=nyg|05ME`C{=VyXf(f4f5CMob+v3Eq)h|5!F?m^
zOq41WW<uaC^}1|fdi2`JLznw|sNcwCob<Pg53Un`;5we8q5f%b7jwz|qq$;vYVUsB
zqW+kAQ^8F+$l(&>>!|N2I#VT_gX9yVZjC#|%#$C70^Gj~Xe}joqFh--56@rVeh1{;
zxZVX{zPi#F|Bjvd3N3gQ3m0oL1egojGszbks)I4gG7^;Ma$jZIJPc(qYB8FMlD5+b
zS91jG#45;5n^U>&RHgWch|pw!SzpWcSN6ftvuS4ON55`WVi-};H6Jnx`BJVlq3*8(
zzE7N*P`Uw2v8=M~=WnCFmFMf(gt>9JWL2xcmdv&v`oFpTuS4}P`$f)^kX2xfMTa)e
ziv<y06zGaC1r2<TE}DT4@g-d&#G)?E0cl<en&MY{Nl1HlbEvtsyZJ~+)UUjLt<A5E
zhO}W>$+X{TL7+uCaiy1y$ICxoXwQB(h}E_JDx2MSRIB?j4ER{U#H^3mJh<m$9f$GB
z#|BKX&&L5YiBmo{X}>JH!jL_8UPVbP(11xFYs7Ot*4cgceQcmX)cH67OC0pEN&6uB
z;G%?Sc*rBmw_0Y$wIPLe$|!8k7*Ud5GxR8MI@D#9$2j?Q7?UR&2m0L7w3pB3%2r#d
z-HO_F%u2d5`9i_5`dqK%dQLg-(l5y%4`V!l6-zqaSX4@6Sm8YH!?&Hxr1O)M36;34
zq(_1oMJr5N7J3!2jZ9Tb4>}Ry?5xp8!W}OsTrZokqG8+WwW=o)^mIldos#A@l=$CC
z<Ai@bOXZ8G({057Lm6*^H?nLy61HvI+9uEXtE|k@zSWyFlvP7(wNv=cwROLjCvSg|
Ia`S)Q9}n5ZGXMYp
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..66f5fee2e4f29f68a13d286a3d790561581f100d
GIT binary patch
literal 1528
zc%02tO=uHA6#iy^(jT?9*y6>*28*^r<0hpNN)&4~BA8ZaDdI(zBuzq-ZrG;M76e5_
zP!AsTQhMmoQ?bR1;6X%by?gAT$6mA`@#IhZW_A-5B6xI$$@|`W^WJ<j`(7Xb9KbE;
zxROjxrpDHG-vj10tw+WuPR6F3OgkX1QlCnfoH7pTd(>ZZolD=SSO{y2fNs!l<ea&3
zt;S3+KTvPY6&I&lBg^CTcT<0q&pMgCzSYCTGXJSO4XuyOo6IHsz4=mQq3a3mQGZUo
zv*@NBlrhQpR_g5~XQ7NsI8A??x;5*RvP+#k!}PBLT1(lTtJKyo#Pgrge**GuT(Lu+
zUtT|$_=0`<E-iQ$kCYlRTw5_0v{#ZZH28xt+N;_MuBmr`xu7!rJ`80suwUMmLsL=G
zb`}vohq*1PFzI@rvOn^r*g;fiG9c8}$o5C}E7KP;%rb%z-KxcLjZv9jd&nppNV%j(
z57v=jPlB3Ix&ceE^x2N^+o<2#n|d}8`gOQu)p&uX%>FX;f7tVX4^_jg7dexVufR7J
zC$weWtp$;Ffs=`i76Tt+>mhSHv7u{(Sl6Z5l;(|=kocC^5Yj%_74AAd*mX24+IBZL
zwdL>AVeP7XrA$z1GUq<#9}4$0R#jBLpV#kxePMQs>ekke5B^f@I<VIG4#zMSV8PnD
z9bgs{o(5Pa_niP6FhxgzO>~M=0S?hVA*;lYHF#M?Ni5Kg`2cII?fU`NaSkg1Hqg#%
z`q+fU&h>GK_KO&W%Z^=yhXQiQqrwX8LmK^*G1#1Oq7-*6aYvEUp)R95#>roYF$JPo
zVAL%ydWBrRV)do_t(a}ct(3b|C>9-S)b+})=Tr(V_oW!*VTLV(l;h1vfh=32z{{}F
zQ`t;mo-(PjR+RL}E~_|2$jQR6VpUJ$>q8holyjiL97%V!l61XX)`~@JYuHl9Q4S46
zqXUv)Gcr>=$-`W5D@)~ys&j31Hm0)P94}(ob~Iw!w$&_2y)$cf$fj9e8p^7n*RKxt
Rx>n!!_RPaiQvUwG_Y?g_(Ifx>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f3317b5f49d3a95bd6b5194487a32c7923f11bf0
GIT binary patch
literal 1528
zc%02tO=uHA6#iy^(zMn7NTrt^Hu%>T8aF8|L83^j5y7Zn3B`-7Nty(cZrG;Mih`md
zs0R;vDLwS)soElV@t~ln_a1xbu@~zlp8Sd5%x<GX1&_`!dEa|)-kWb`-wOnQ-M9%I
z=ToVP^x5UjkAS&OYu~vGhvS*cxpqKIQqN>7PF3&JKT&_pHI}_vvk=kd0o|bAC^$3K
zMuVAPu2Byc%JWm<=)yVrd#FDw=AGQ<{-s`GnSZoML->(-gSn)Cf3Z@V>wb*8)SpxD
zD!W++RZK8GOuen*%vEt3N9j*cx2By+{`#KYB>hW()>3t6YK><Y;rUPLAB4OcSAYA~
z%S)ZfFKE{{X~DZhw9=H3#-cf=y^?&P!5@s#UNshRQN07qIhE=6VJM4%UGlaZnu?OP
z<B0k>%ym(RN!NXq{h=?#24X^!0io7rwm-6;pE{XimNAU!RwIFnjLQAmLq?H6$|XH|
zu=;{MNoqpr1}w$WXFJAkqkbE2wzG-Qufrv)<_qk|>~BN=mreikP&LeYkuwGP3VdTR
zs4ehnEr_lN98Rvb82A)l37PB3Rb3;*iZ0EjG_SUV#JA+CkoMv3NcW-P?tKx_y7~US
zw(xx_qD{(2$_14sbIP6?iR^8zx~P9Yq2KxX!t4?C4<CN4`Af0uz;g3D9K_iG3)a@H
z0JE6zB)~ekZv@zYDLMjdqDzbhI7IseStW+7!80mKVu3cy23TWl-wUwLYFrGkfi_;#
z$0jUxu8%{spTam?cI-Snlu$qsHCA9hvKXL@!{$s7rMc@Gca%9D>N3h>ocwhdQzDuM
z#@*_?S1J^1R)2QDiraR=O1sxe<+5XqyI$4xoLb4{zBGe8Tp>xM9q)=1$g?#{ybK#X
zozInKDN`zIO-YaJ@`_WGoGctsthUqm`Ur*);~Z)-N6MY9rChI&x8hOTI%28gC`X23
zu^~xtCo&nH<Y6Ybm8EjU)Va1g8=1T}!;4t99gEtwZS9m~yE7ZN$!4d%G?Z1t0lzxf
R>)Q6dx5plAN%{N#-cQz&(Ifx>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5894180a899b1a459d1545158de520ac0d637472
GIT binary patch
literal 1528
zc%02tO-NKx7(Ms>jK7(N5rK<06s3aWn+auzDMD)^Xi889aTAZ3ab{@7H$2Bsp(rYX
za?zqT;=*MsQ)%g<MWO9mx^O8$wy{;&bncz!XkgH?d%5$S@4NRq_uTuwI}iYl;URPk
z#p5H1TT9!Y0CSVkj@!e%kz^{}42V(s$yCuP;e@_J|2@Y*>R#DGP@4gCgY!n#nJiT*
zWP<sbetotuGhQE>zs>n}`g6IAlm6DZc#2r^_vaX>e`($)mz+PFE0(9*Ug0VIW%{iJ
zH|3y&5$4y^Z!9{~CEUOz&PVB66HYPnp#Ah^&MyL5UCEs+SC-Mo{oioD8}e)%U3*{N
z-Z>uojwXGZ5qyk>iZvUoESS^Un$!yo)y5oct+IeIbq_G771QBnsEC1Pd0IA2Wy#nT
zgsL3orr3qasppD)u4=^=!a|b?frc8}AM8Wp*V1Ge#Gr0fq8MXV`d|$e1$`@roYDQ!
z;jf9&6UsMWDV0^VgZwt?xAK0UO#}`$m#S(vaHO)oHvOMg{l87sFzZG3IOHwxjzzaN
z&y&?5v?9<OTdgzjHL?;gH)E^1Mu-(%hD{k>tqX{su~i}C7utet=P$IK35tg8^>uB2
zZ#<}t$~#K?l_oix$ln({SsS}zckinH<i|U+UF>db{Q6uSN?i+<YTw};ZuwZSwjTMI
z#e~;B)~S8V#|BK%;$su7qTj~>#)oB<7_tU$sw{~G8ZqT#jkW#E$2tbE;9~=gJZ2S}
zu=sMTIKcRIR;|kyI|C1SWRXLe71)Usx@aS?*`q`WuA1eF0=q+BW_iq$zYcTqL=!Ak
zw>0DBv$?X>nd-74wjH$+?rgqLaI8VsE4iLi&bwTfV3LQsq(Q>*?n;9UpGKaC;X_Yk
z()lUcxMD3U>rq`sX$n!3g^NnnK26o{Ll462JvHTsyA$QO>t!=mBxG9`EwvqOUr#vP
zBLyBMCdr*VO!}v?6jxa7YpcDH%y^SLh-KU1kZs%6Q6&2>v+|f~j(V4difXJ7RULfm
Q+Wxu^mtX8id-#9vH@+d#BLDyZ
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5440c1c4cf3620e57453981f29be18a869debf27
GIT binary patch
literal 1528
zc%02tO-vI(6#iy^3W5Sn{5i;B8~>1mvaP{1rWylSV`9MsLI@YLX-nJCvdvNrH8Ihs
zF;Na4^g=jr(-RTm(L@g#@a~ZVM=r_*PyW<zW|x8qCLW#1^nLHWd2ha%eXkGz4&fej
z+(@NH(?d&}p8#{6*3RLP-uPHHdk7HY)W<Sqr-Js-7WMaB{h8ZU3lXgb=mz~p-kGV?
z>&yi6Gxg?tsW#ahogb$EF!i}Y&dF|dEgmD5`Og<<XntniWiIJIStwU$!_V=E`fKVP
zB{$=sf>FjdQ*S9dvlU#!Mfwxet!by6yLa^XW%?HZt*PS9RO`#=<N2@X?}oe^*QxIt
zFK@OdzoAv%qy--n(Q-pZ>I>$q_Ez$R27fR{ds|<?gn9><vntcxVJM4%gYvc<nu?OP
z%ZU0p%yqF1lddN!`<ySu24X^!0igqpY=30GF?l7+EQ1)-t$G3zjLPorA)`nj<&qvf
zSe?P1BsHOQ1D0axvmNBOQNQ(fJK02N_i)Lo@dEoY`}@%UZQK7kR1LFU<V-=n0^eA4
zYxBHV6Qauky~&j(17G6HA#**sqHBa$)}`5$=9Q+9_>o)@(ta)+37<I^J`oWIHdj}*
z`Ja;!ZCpN4HmEe2vz7V#B1al)TWtRv)E|C-V;&aUYiqwg`%AHF!&2iroW@Xq1#9bm
zfLTm<5n!F%Hv(+H6m0=E(IL(UI7Iu1tP(@k;B^%xu|NxM1z2NkKMt^tek=soKnt(w
zV-prT*T*5+uVMf$JGKT7MdVRHl@-{949-%<VRI&k(%f~2J4&1mbs6O`PX0QKDH2Ts
z18$|}74wCv)s;DG#cexbrQJKlQpvFfT(9DKPPOQAUz$N4rr0t_JKmHO$gwqwybK#X
zoy!()QKnSZs*)br<rJqVIa#=%SnZ_o^*;0<#@W+gj+8rHO}Sn^XT_tob-_}{QTFx3
zVm*@Jeq_dYl82e#R+h>YQ|H?1Y>ef+8D7M)?O4>dZEL?IJDpj7KsNjJrJ<}E&iK{A
SUe|W^eYo`WtCV~H_kIJHdD0^Q
new file mode 100644
--- /dev/null
+++ b/layout/reftests/reftest-sanity/blank.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<body>
+</body>
+</html>
--- a/layout/reftests/reftest-sanity/reftest.list
+++ b/layout/reftests/reftest-sanity/reftest.list
@@ -14,8 +14,21 @@
 != html-vs-xhtml-by-extension.html html-vs-xhtml-by-extension.xhtml
 HTTP != html-vs-xhtml-by-extension.html html-vs-xhtml-by-extension.xhtml
 
 # make sure red and green colors are not the default and are different from
 # each other
 != green.html default.html
 != green.html red.html
 != red.html default.html
+
+# Make sure about:blank works, even via HTTP.
+== blank.html about:blank
+== about:blank blank.html
+HTTP == blank.html about:blank
+HTTP == about:blank blank.html
+# same for data:
+== default.html data:text/html,<div>Text</div>
+== data:text/html,<div>Text</div> default.html
+HTTP == default.html data:text/html,<div>Text</div>
+HTTP == data:text/html,<div>Text</div> default.html
+!= blank.html default.html
+HTTP != blank.html default.html
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -48,16 +48,19 @@ include counters/reftest.list
 include generated-content/reftest.list
 
 # first-letter/
 include first-letter/reftest.list
 
 # first-line/
 include first-line/reftest.list
 
+# font-face
+include font-face/reftest.list
+
 # forms
 include forms/reftest.list
 
 # block-inside-inline splits
 include ib-split/reftest.list
 
 # image-region/
 include image-region/reftest.list
--- a/layout/tools/reftest/README.txt
+++ b/layout/tools/reftest/README.txt
@@ -74,26 +74,29 @@ 2. A test item
                          particular platform (i.e. it allows us to get test
                          coverage on the other platforms).
 
       Examples of using conditions:
           fails-if(MOZ_WIDGET_TOOLKIT=="windows") ...
           fails-if(MOZ_WIDGET_TOOLKIT=="cocoa") ...
           fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") ...
 
-   b. <http>, if present, is the string "HTTP" (sans quotes), indicating that
+   b. <http>, if present, is one of the strings (sans quotes) "HTTP" or
+      "HTTP(..)" or "HTTP(../..)" or "HTTP(../../..)", etc. , indicating that
       the test should be run over an HTTP server because it requires certain
       HTTP headers or a particular HTTP status.  (Don't use this if your test
       doesn't require this functionality, because it unnecessarily slows down
       the test.)
 
-      HTTP tests have the restriction that any resource an HTTP test accesses
-      must be accessed using a relative URL, and the test and the resource must
-      be within the directory containing the reftest manifest that describes
-      the test (or within a descendant directory).
+      With "HTTP", HTTP tests have the restriction that any resource an HTTP
+      test accesses must be accessed using a relative URL, and the test and
+      the resource must be within the directory containing the reftest
+      manifest that describes the test (or within a descendant directory).
+      The variants "HTTP(..)", etc., can be used to relax this restriction by
+      allowing resources in the parent directory, etc.
 
       To modify the HTTP status or headers of a resource named FOO, create a
       sibling file named FOO^headers^ with the following contents:
 
       [<http-status>]
       <http-header>*
 
       <http-status> A line of the form "HTTP ###[ <description>]", where
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -205,53 +205,62 @@ function ReadManifest(aURL)
                 } else if (stat == "random") {
                     expected_status = EXPECTED_RANDOM;
                 } else if (stat == "skip") {
                     expected_status = EXPECTED_DEATH;
                 }
             }
         }
 
-        var runHttp = items[0] == "HTTP";
-        if (runHttp)
+        var runHttp = false;
+        var httpDepth;
+        if (items[0] == "HTTP") {
+            runHttp = true;
+            httpDepth = 0;
             items.shift();
+        } else if (items[0].match(/HTTP\(\.\.(\/\.\.)*\)/)) {
+            // Accept HTTP(..), HTTP(../..), HTTP(../../..), etc.
+            runHttp = true;
+            httpDepth = (items[0].length - 5) / 3;
+            items.shift();
+        }
 
         if (items[0] == "include") {
             if (items.length != 2 || runHttp)
                 throw "Error in manifest file " + aURL.spec + " line " + lineNo;
             var incURI = gIOService.newURI(items[1], null, listURL);
             secMan.checkLoadURI(aURL, incURI,
                                 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
             ReadManifest(incURI);
         } else if (items[0] == "load") {
             if (expected_status == EXPECTED_PASS)
                 expected_status = EXPECTED_LOAD;
             if (items.length != 2 ||
                 (expected_status != EXPECTED_LOAD &&
                  expected_status != EXPECTED_DEATH))
                 throw "Error in manifest file " + aURL.spec + " line " + lineNo;
             var [testURI] = runHttp
-                            ? ServeFiles(aURL,
+                            ? ServeFiles(aURL, httpDepth,
                                          listURL.file.parent, [items[1]])
                             : [gIOService.newURI(items[1], null, listURL)];
             var prettyPath = runHttp
                            ? gIOService.newURI(items[1], null, listURL).spec
                            : testURI.spec;
             secMan.checkLoadURI(aURL, testURI,
                                 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
             gURLs.push( { equal: true /* meaningless */,
                           expected: expected_status,
                           prettyPath: prettyPath,
                           url1: testURI,
                           url2: null } );
         } else if (items[0] == "==" || items[0] == "!=") {
             if (items.length != 3)
                 throw "Error in manifest file " + aURL.spec + " line " + lineNo;
             var [testURI, refURI] = runHttp
-                                  ? ServeFiles(aURL,
+                                  ? ServeFiles(aURL, httpDepth,
                                                listURL.file.parent, [items[1], items[2]])
                                   : [gIOService.newURI(items[1], null, listURL),
                                      gIOService.newURI(items[2], null, listURL)];
             var prettyPath = runHttp
                            ? gIOService.newURI(items[1], null, listURL).spec
                            : testURI.spec;
             secMan.checkLoadURI(aURL, testURI,
                                 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
@@ -263,34 +272,47 @@ function ReadManifest(aURL)
                           url1: testURI,
                           url2: refURI } );
         } else {
             throw "Error in manifest file " + aURL.spec + " line " + lineNo;
         }
     } while (more);
 }
 
-function ServeFiles(manifestURL, directory, files)
+function ServeFiles(manifestURL, depth, directory, files)
 {
     if (!gServer)
         gServer = CC["@mozilla.org/server/jshttp;1"].
                       createInstance(CI.nsIHttpServer);
 
+    // Allow serving a tree that's an ancestor of the directory containing
+    // the files so that they can use resources in ../ (etc.).
+    var dirPath = "/";
+    while (depth > 0) {
+        dirPath = "/" + directory.leafName + dirPath;
+        directory = directory.parent;
+        --depth;
+    }
+
     gCount++;
-    var path = "/" + gCount + "/";
-    gServer.registerDirectory(path, directory);
+    var path = "/" + gCount;
+    gServer.registerDirectory(path + "/", directory);
 
     var secMan = CC[NS_SCRIPTSECURITYMANAGER_CONTRACTID]
                      .getService(CI.nsIScriptSecurityManager);
 
+    var testbase = gIOService.newURI("http://localhost:" + HTTP_SERVER_PORT +
+                                         path + dirPath,
+                                     null, null);
+
     function FileToURI(file)
     {
-        var testURI = gIOService.newURI("http://localhost:" + HTTP_SERVER_PORT +
-                                        path + file,
-                                        null, null);
+        // Only serve relative URIs via the HTTP server, not absolute
+        // ones like about:blank.
+        var testURI = gIOService.newURI(file, null, testbase);
 
         // XXX necessary?  manifestURL guaranteed to be file, others always HTTP
         secMan.checkLoadURI(manifestURL, testURI,
                             CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
 
         return testURI;
     }
 
--- a/media/liboggplay/README_MOZILLA
+++ b/media/liboggplay/README_MOZILLA
@@ -1,11 +1,11 @@
 The source from this directory was copied from the liboggplay svn
 source using the update.sh script. The only changes made were those
 applied by update.sh and the addition/upate of Makefile.in files for
 the Mozilla build system.
 
 http://svn.annodex.net/liboggplay/trunk/
 
-The svn revision number used was r3761.
+The svn revision number used was r3774.
 
 The patch from Annodex trac ticket 421 is applied to fix bug 459938:
   http://trac.annodex.net/ticket/421
--- a/media/liboggplay/src/liboggplay/oggplay.c
+++ b/media/liboggplay/src/liboggplay/oggplay.c
@@ -639,19 +639,22 @@ oggplay_get_duration(OggPlay *me) {
 
   if (me == NULL) {
     return E_OGGPLAY_BAD_OGGPLAY;
   }
 
   if (me->reader->duration) 
     return me->reader->duration(me->reader);
   else {
-    ogg_int64_t pos = oggz_tell_units(me->oggz);
-    ogg_int64_t duration = oggz_seek_units(me->oggz, 0, SEEK_END);
+    ogg_int64_t pos;
+    ogg_int64_t duration;
+    pos = oggz_tell_units(me->oggz);
+    duration = oggz_seek_units(me->oggz, 0, SEEK_END);
     oggz_seek_units(me->oggz, pos, SEEK_SET);
+    oggplay_seek_cleanup(me, pos);
     return duration;
   }
 }
 
 int
 oggplay_media_finished_retrieving(OggPlay *me) {
 
   if (me == NULL) {
--- a/media/liboggplay/src/liboggplay/oggplay_private.h
+++ b/media/liboggplay/src/liboggplay/oggplay_private.h
@@ -224,16 +224,19 @@ struct _OggPlay {
 
 void
 oggplay_set_data_callback_force(OggPlay *me, OggPlayDataCallback callback,
                 void *user);
 
 void
 oggplay_take_out_trash(OggPlay *me, OggPlaySeekTrash *trash);
 
+void
+oggplay_seek_cleanup(OggPlay *me, ogg_int64_t milliseconds);
+
 typedef struct {
   void (*init)(void *user_data);
   int (*callback)(OGGZ * oggz, ogg_packet * op, long serialno,
                                                           void * user_data);
   void (*shutdown)(void *user_data);
   int size;
 } OggPlayCallbackFunctions;
 
--- a/media/liboggplay/src/liboggplay/oggplay_seek.c
+++ b/media/liboggplay/src/liboggplay/oggplay_seek.c
@@ -36,21 +36,17 @@
  * Shane Stephens <shane.stephens@annodex.net>
  */
 
 #include "oggplay_private.h"
 
 OggPlayErrorCode
 oggplay_seek(OggPlay *me, ogg_int64_t milliseconds) {
 
-  OggPlaySeekTrash    * trash;
-  OggPlaySeekTrash   ** p;
-  OggPlayDataHeader  ** end_of_list_p;
-  int                   i;
-  int                   eof;
+  ogg_int64_t           eof;
 
   if (me == NULL) {
     return E_OGGPLAY_BAD_OGGPLAY;
   }
 
   if (milliseconds < 0) {
     return E_OGGPLAY_CANT_SEEK;
   }
@@ -71,16 +67,31 @@ oggplay_seek(OggPlay *me, ogg_int64_t mi
       return E_OGGPLAY_CANT_SEEK;
     }
   } else {
     if (oggz_seek_units(me->oggz, milliseconds, SEEK_SET) == -1) {
       return E_OGGPLAY_CANT_SEEK;
     }
   }
 
+  oggplay_seek_cleanup(me, milliseconds);
+
+  return E_OGGPLAY_OK;
+
+}
+
+void
+oggplay_seek_cleanup(OggPlay* me, ogg_int64_t milliseconds)
+{
+
+  OggPlaySeekTrash    * trash;
+  OggPlaySeekTrash   ** p;
+  OggPlayDataHeader  ** end_of_list_p;
+  int                   i;
+
   /*
    * first, create a trash object to store the context that we want to
    * delete but can't until the presentation thread is no longer using it -
    * this will occur as soon as the thread calls oggplay_buffer_release_next
    */
 
   trash = calloc(sizeof(OggPlaySeekTrash), 1);
 
@@ -124,19 +135,16 @@ oggplay_seek(OggPlay *me, ogg_int64_t mi
   trash->next = NULL;
 
   p = &(me->trash);
   while (*p != NULL) {
     p = &((*p)->next);
   }
 
   *p = trash;
-
-  return E_OGGPLAY_OK;
-
 }
 
 void
 oggplay_take_out_trash(OggPlay *me, OggPlaySeekTrash *trash) {
 
   OggPlaySeekTrash *p = NULL;
 
   for (; trash != NULL; trash = trash->next) {
new file mode 100644
--- /dev/null
+++ b/widget/src/cocoa/crashtests/460387-1.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html><head></head><body><div style="display: table; padding: 625203mm; -moz-appearance: menulist;"></div></body></html>
--- a/widget/src/cocoa/crashtests/crashtests.list
+++ b/widget/src/cocoa/crashtests/crashtests.list
@@ -1,6 +1,7 @@
 load 397209-1.html
 load 403296-1.xhtml
 load 419737-1.html
 load 444260-1.xul
 load 444864-1.html
 load 449111-1.html
+load 460387-1.html
--- a/widget/src/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/src/cocoa/nsNativeThemeCocoa.mm
@@ -251,40 +251,43 @@ static void DrawCellWithScaling(NSCell *
     drawRect.size.height = destRect.size.height * naturalSize.width / destRect.size.width;
 
   // Honor minimum sizes.
   if (drawRect.size.width < minimumSize.width)
     drawRect.size.width = minimumSize.width;
   if (drawRect.size.height < minimumSize.height)
     drawRect.size.height = minimumSize.height;
 
-  CGAffineTransform savedCTM = CGContextGetCTM(cgContext);
-  NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
+  [NSGraphicsContext saveGraphicsState];
 
   if (flip) {
     // This flips the image in place and is necessary to work around a bug in the way
     // NSButtonCell draws buttons.
     CGContextScaleCTM(cgContext, 1.0f, -1.0f);
     CGContextTranslateCTM(cgContext, 0.0f, -(2.0 * destRect.origin.y + destRect.size.height));
   }
 
   // Fall back to no scaling if the area of our cell (in pixels^2) is too large.
   BOOL noBufferOverride = (drawRect.size.width * drawRect.size.height > CELL_SCALING_MAX_AREA);
 
   if ((!needsBuffer && drawRect.size.width == destRect.size.width &&
        drawRect.size.height == destRect.size.height) || noBufferOverride) {
     // Inflate the rect Gecko gave us by the margin for the control.
     InflateControlRect(&drawRect, controlSize, marginSet);
 
+    NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
+
     // Set up the graphics context we've been asked to draw to.
     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]];
 
     // [NSView focusView] may return nil here, but
     // [NSCell drawWithFrame:inView:] can deal with that.
     [cell drawWithFrame:drawRect inView:[NSView focusView]];
+
+    [NSGraphicsContext setCurrentContext:savedContext];
   }
   else {
     float w = ceil(drawRect.size.width);
     float h = ceil(drawRect.size.height);
 
     NSRect tmpRect = NSMakeRect(0.0f, 0.0f, w, h);
 
     // inflate to figure out the frame we need to tell NSCell to draw in, to get something that's 0,0,w,h
@@ -298,16 +301,18 @@ static void DrawCellWithScaling(NSCell *
     CGContextRef ctx = CGBitmapContextCreate(NULL,
                                              (int) w, (int) h,
                                              8, (int) w * 4,
                                              rgb, kCGImageAlphaPremultipliedFirst);
     CGColorSpaceRelease(rgb);
 
     CGContextTranslateCTM(ctx, MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH);
 
+    NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
+
     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:YES]];
 
     // [NSView focusView] may return nil here, but
     // [NSCell drawWithFrame:inView:] can deal with that.
     [cell drawWithFrame:tmpRect inView:[NSView focusView]];
 
     [NSGraphicsContext setCurrentContext:savedContext];
 
@@ -325,19 +330,17 @@ static void DrawCellWithScaling(NSCell *
                                              destRect.size.width + scaledFocusRingX * 2,
                                              destRect.size.height + scaledFocusRingY * 2),
                        img);
 
     CGImageRelease(img);
     CGContextRelease(ctx);
   }
 
-  CGContextSetCTM(cgContext, savedCTM);
-
-  [NSGraphicsContext setCurrentContext:savedContext];
+  [NSGraphicsContext restoreGraphicsState];
 
 #if DRAW_IN_FRAME_DEBUG
   CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
   CGContextFillRect(cgContext, destRect);
 #endif
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -4847,17 +4847,16 @@ PRBool nsWindow::ProcessMessage(UINT msg
         // the window origin in screen coordinates...
         RECT r;
         ::GetWindowRect(mWnd, &r);
         PRInt32 newWidth, newHeight;
         newWidth = PRInt32(r.right - r.left);
         newHeight = PRInt32(r.bottom - r.top);
         nsRect rect(wp->x, wp->y, newWidth, newHeight);
 
-
 #ifdef MOZ_XUL
         if (eTransparencyTransparent == mTransparencyMode)
           ResizeTranslucentWindow(newWidth, newHeight);
 #endif
 
         if (newWidth > mLastSize.width)
         {
           RECT drect;
@@ -4888,18 +4887,20 @@ PRBool nsWindow::ProcessMessage(UINT msg
         mBounds.width  = newWidth;
         mBounds.height = newHeight;
         mLastSize.width = newWidth;
         mLastSize.height = newHeight;
         ///nsRect rect(wp->x, wp->y, wp->cx, wp->cy);
 
         // If we're being minimized, don't send the resize event to Gecko because
         // it will cause the scrollbar in the content area to go away and we'll
-        // forget the scroll position of the page.
-        if ( !newWidth && !newHeight && IsIconic(mWnd)) {
+        // forget the scroll position of the page.  Note that we need to check the
+        // toplevel window, because child windows seem to go to 0x0 on minimize.
+        HWND toplevelWnd = GetTopLevelHWND(mWnd);
+        if ( !newWidth && !newHeight && IsIconic(toplevelWnd)) {
           result = PR_FALSE;
           break;
         }
 
         // recalculate the width and height
         // this time based on the client area
         if (::GetClientRect(mWnd, &r)) {
           rect.width  = PRInt32(r.right - r.left);