Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 14 Oct 2013 14:54:35 +0200
changeset 164445 b6dfeb4a34c89a3c6409f62d4babd1b515dbce33
parent 164444 ff0cb6c1f106c3a5419bd0f7a9463d39421f7959 (current diff)
parent 164437 4c4dec8506ab4cecbe730b7add710bbf5b3b74fa (diff)
child 164446 8a4374c8fc92f90c39ab7adf004172ab16460a68
child 164537 a21ab2d667de6be5e82b270848566577b1758dbd
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to fx-team
dom/workers/Worker.cpp
dom/workers/Worker.h
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "d10b41974fe05af122c93b3e11b12ea98edf220b", 
+    "revision": "47d1ef9819810cecf9ce8cdcfd7f211530e43668", 
     "repo_path": "/integration/gaia-central"
 }
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -69,17 +69,17 @@ AudioChannelService::Shutdown()
   }
 }
 
 NS_IMPL_ISUPPORTS2(AudioChannelService, nsIObserver, nsITimerCallback)
 
 AudioChannelService::AudioChannelService()
 : mCurrentHigherChannel(AUDIO_CHANNEL_LAST)
 , mCurrentVisibleHigherChannel(AUDIO_CHANNEL_LAST)
-, mActiveContentChildIDsFrozen(false)
+, mPlayableHiddenContentChildID(CONTENT_PROCESS_ID_UNKNOWN)
 , mDisabled(false)
 , mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN)
 {
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->AddObserver(this, "ipc:content-shutdown", false);
       obs->AddObserver(this, "xpcom-shutdown", false);
@@ -132,16 +132,28 @@ AudioChannelService::RegisterType(AudioC
       mDeferTelChannelTimer = nullptr;
       UnregisterTypeInternal(aType, mTimerElementHidden, mTimerChildID, false);
     }
 
     if (aWithVideo) {
       mWithVideoChildIDs.AppendElement(aChildID);
     }
 
+    // One hidden content channel can be playable only when there is no any
+    // content channel in the foreground.
+    if (type == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
+        mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
+      mPlayableHiddenContentChildID = aChildID;
+    }
+    // No hidden content channel can be playable if there is an content channel
+    // in foreground.
+    else if (type == AUDIO_CHANNEL_INT_CONTENT) {
+      mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN;
+    }
+
     // In order to avoid race conditions, it's safer to notify any existing
     // agent any time a new one is registered.
     SendAudioChannelChangedNotification(aChildID);
     Notify();
   }
 }
 
 void
@@ -197,23 +209,22 @@ AudioChannelService::UnregisterTypeInter
   // this should remove only the first one.
   AudioChannelInternalType type = GetInternalType(aType, aElementHidden);
   MOZ_ASSERT(mChannelCounters[type].Contains(aChildID));
   mChannelCounters[type].RemoveElement(aChildID);
 
   // In order to avoid race conditions, it's safer to notify any existing
   // agent any time a new one is registered.
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
-    // We only remove ChildID when it is in the foreground.
-    // If in the background, we kept ChildID for allowing it to play next song.
+    // No hidden content channel is playable if the original playable hidden
+    // process does not need to play audio from background anymore.
     if (aType == AUDIO_CHANNEL_CONTENT &&
-        mActiveContentChildIDs.Contains(aChildID) &&
-        !aElementHidden &&
-        !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID)) {
-      mActiveContentChildIDs.RemoveElement(aChildID);
+        mPlayableHiddenContentChildID == aChildID &&
+        !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains(aChildID)) {
+      mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN;
     }
 
     if (aWithVideo) {
       MOZ_ASSERT(mWithVideoChildIDs.Contains(aChildID));
       mWithVideoChildIDs.RemoveElement(aChildID);
     }
 
     SendAudioChannelChangedNotification(aChildID);
@@ -231,16 +242,29 @@ AudioChannelService::UpdateChannelType(A
   AudioChannelInternalType newType = GetInternalType(aType, aElementHidden);
   AudioChannelInternalType oldType = GetInternalType(aType, aElementWasHidden);
 
   if (newType != oldType) {
     mChannelCounters[newType].AppendElement(aChildID);
     MOZ_ASSERT(mChannelCounters[oldType].Contains(aChildID));
     mChannelCounters[oldType].RemoveElement(aChildID);
   }
+
+  // The last content channel which goes from foreground to background can also
+  // be playable.
+  if (oldType == AUDIO_CHANNEL_INT_CONTENT &&
+      newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
+      mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
+    mPlayableHiddenContentChildID = aChildID;
+  }
+  // No hidden content channel can be playable if there is an content channel
+  // in foreground.
+  else if (newType == AUDIO_CHANNEL_INT_CONTENT) {
+    mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN;
+  }
 }
 
 AudioChannelState
 AudioChannelService::GetState(AudioChannelAgent* aAgent, bool aElementHidden)
 {
   AudioChannelAgentData* data;
   if (!mAgents.Get(aAgent, &data)) {
     return AUDIO_CHANNEL_STATE_MUTED;
@@ -260,53 +284,16 @@ AudioChannelService::GetStateInternal(Au
                                       bool aElementHidden, bool aElementWasHidden)
 {
   UpdateChannelType(aType, aChildID, aElementHidden, aElementWasHidden);
 
   // Calculating the new and old type and update the hashtable if needed.
   AudioChannelInternalType newType = GetInternalType(aType, aElementHidden);
   AudioChannelInternalType oldType = GetInternalType(aType, aElementWasHidden);
 
-  // If the audio content channel is visible, let's remember this ChildID.
-  if (newType == AUDIO_CHANNEL_INT_CONTENT &&
-      oldType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN) {
-
-    if (mActiveContentChildIDsFrozen) {
-      mActiveContentChildIDsFrozen = false;
-      mActiveContentChildIDs.Clear();
-    }
-
-    if (!mActiveContentChildIDs.Contains(aChildID)) {
-      mActiveContentChildIDs.AppendElement(aChildID);
-    }
-  }
-  else if (newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
-           oldType == AUDIO_CHANNEL_INT_CONTENT &&
-           !mActiveContentChildIDsFrozen) {
-    // If nothing is visible, the list has to been frozen.
-    // Or if there is still any one with other ChildID in foreground then
-    // it should be removed from list and left other ChildIDs in the foreground
-    // to keep playing. Finally only last one childID which go to background
-    // will be in list.
-    if (mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
-      mActiveContentChildIDsFrozen = true;
-    } else if (!mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID)) {
-      MOZ_ASSERT(mActiveContentChildIDs.Contains(aChildID));
-      mActiveContentChildIDs.RemoveElement(aChildID);
-    }
-  }
-  else if (newType == AUDIO_CHANNEL_INT_NORMAL &&
-           oldType == AUDIO_CHANNEL_INT_NORMAL_HIDDEN &&
-           mWithVideoChildIDs.Contains(aChildID)) {
-    if (mActiveContentChildIDsFrozen) {
-      mActiveContentChildIDsFrozen = false;
-      mActiveContentChildIDs.Clear();
-    }
-  }
-
   if (newType != oldType &&
       (aType == AUDIO_CHANNEL_CONTENT ||
        (aType == AUDIO_CHANNEL_NORMAL &&
         mWithVideoChildIDs.Contains(aChildID)))) {
     Notify();
   }
 
   SendAudioChannelChangedNotification(aChildID);
@@ -317,17 +304,26 @@ AudioChannelService::GetStateInternal(Au
       return AUDIO_CHANNEL_STATE_FADED;
     }
     return AUDIO_CHANNEL_STATE_NORMAL;
   }
 
   // We are not visible, maybe we have to mute.
   if (newType == AUDIO_CHANNEL_INT_NORMAL_HIDDEN ||
       (newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN &&
-       !mActiveContentChildIDs.Contains(aChildID))) {
+       // One process can have multiple content channels; and during the
+       // transition from foreground to background, its content channels will be
+       // updated with correct visibility status one by one. All its content
+       // channels should remain playable until all of their visibility statuses
+       // have been updated as hidden. After all its content channels have been
+       // updated properly as hidden, mPlayableHiddenContentChildID is used to
+       // check whether this background process is playable or not.
+       !(mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID) ||
+         (mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty() &&
+          mPlayableHiddenContentChildID == aChildID)))) {
     return AUDIO_CHANNEL_STATE_MUTED;
   }
 
   // After checking the condition on normal & content channel, if the state
   // is not on muted then checking other higher channels type here.
   if (ChannelsActiveWithHigherPriorityThan(newType)) {
     MOZ_ASSERT(newType != AUDIO_CHANNEL_INT_NORMAL_HIDDEN);
     if (CheckVolumeFadedCondition(newType, aElementHidden)) {
@@ -488,22 +484,18 @@ AudioChannelService::SendAudioChannelCha
     else if (!mChannelCounters[AUDIO_CHANNEL_INT_ALARM_HIDDEN].IsEmpty()) {
       higher = AUDIO_CHANNEL_ALARM;
     }
 
     else if (!mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN].IsEmpty()) {
       higher = AUDIO_CHANNEL_NOTIFICATION;
     }
 
-    // There is only one Child can play content channel in the background.
-    // And need to check whether there is any content channels under playing
-    // now.
-    else if (!mActiveContentChildIDs.IsEmpty() &&
-             mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains(
-             mActiveContentChildIDs[0])) {
+    // Check whether there is any playable hidden content channel or not.
+    else if (mPlayableHiddenContentChildID != CONTENT_PROCESS_ID_UNKNOWN) {
       higher = AUDIO_CHANNEL_CONTENT;
     }
   }
 
   if (higher != mCurrentHigherChannel) {
     mCurrentHigherChannel = higher;
 
     nsString channelName;
@@ -636,19 +628,22 @@ AudioChannelService::Observe(nsISupports
            type < AUDIO_CHANNEL_INT_LAST;
            ++type) {
 
         while ((index = mChannelCounters[type].IndexOf(childID)) != -1) {
           mChannelCounters[type].RemoveElementAt(index);
         }
       }
 
-      while ((index = mActiveContentChildIDs.IndexOf(childID)) != -1) {
-        mActiveContentChildIDs.RemoveElementAt(index);
+      // No hidden content channel is playable if the original playable hidden
+      // process shuts down.
+      if (mPlayableHiddenContentChildID == childID) {
+        mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN;
       }
+
       while ((index = mWithVideoChildIDs.IndexOf(childID)) != -1) {
         mWithVideoChildIDs.RemoveElementAt(index);
       }
 
       // We don't have to remove the agents from the mAgents hashtable because if
       // that table contains only agents running on the same process.
 
       SendAudioChannelChangedNotification(childID);
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -164,19 +164,36 @@ protected:
 
   nsClassHashtable< nsPtrHashKey<AudioChannelAgent>, AudioChannelAgentData > mAgents;
 
   nsTArray<uint64_t> mChannelCounters[AUDIO_CHANNEL_INT_LAST];
 
   AudioChannelType mCurrentHigherChannel;
   AudioChannelType mCurrentVisibleHigherChannel;
 
-  nsTArray<uint64_t> mActiveContentChildIDs;
   nsTArray<uint64_t> mWithVideoChildIDs;
-  bool mActiveContentChildIDsFrozen;
+
+  // mPlayableHiddenContentChildID stores the ChildID of the process which can
+  // play content channel(s) in the background.
+  // A background process contained content channel(s) will become playable:
+  //   1. When this background process registers its content channel(s) in
+  //   AudioChannelService and there is no foreground process with registered
+  //   content channel(s).
+  //   2. When this process goes from foreground into background and there is
+  //   no foreground process with registered content channel(s).
+  // A background process contained content channel(s) will become non-playable:
+  //   1. When there is a foreground process registering its content channel(s)
+  //   in AudioChannelService.
+  //   ps. Currently this condition is never satisfied because the default value
+  //   of visibility status of each channel during registering is hidden = true.
+  //   2. When there is a process with registered content channel(s) goes from
+  //   background into foreground.
+  //   3. When this process unregisters all hidden content channels.
+  //   4. When this process shuts down.
+  uint64_t mPlayableHiddenContentChildID;
 
   bool mDisabled;
 
   nsCOMPtr<nsITimer> mDeferTelChannelTimer;
   bool mTimerElementHidden;
   uint64_t mTimerChildID;
 
   uint64_t mDefChannelChildID;
--- a/dom/audiochannel/tests/TestAudioChannelService.cpp
+++ b/dom/audiochannel/tests/TestAudioChannelService.cpp
@@ -267,33 +267,33 @@ TestContentChannels()
   NS_ENSURE_SUCCESS(rv, rv);
   rv = agent2->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = agent1->StopPlaying();
   NS_ENSURE_SUCCESS(rv, rv);
   rv = agent2->StopPlaying();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Test that content channels can't be allow to play when they starts from
+  // Test that content channels can be allow to play when they starts from
   // the background state
   rv = agent1->SetVisibilityState(false);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = agent2->SetVisibilityState(false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = agent1->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test3: A content channel unvisible agent1 must be muted while playing "
+  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
+    "Test3: A content channel unvisible agent1 must be playable "
     "from background state");
 
   rv = agent2->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test3: A content channel unvisible agent2 must be muted while playing "
+  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
+    "Test3: A content channel unvisible agent2 must be playable "
     "from background state");
 
   agent1->StopPlaying();
   agent2->StopPlaying();
   return rv;
 }
 
 nsresult
@@ -413,18 +413,18 @@ TestPriorities()
 
   rv = normalAgent->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
     "Test5: A normal channel unvisible agent must be muted");
 
   rv = contentAgent->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
-  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
-    "Test5: A content channel unvisible agent agent must be muted while "
+  TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
+    "Test5: A content channel unvisible agent must be playable while "
     "playing from background state");
 
   rv = notificationAgent->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
     "Test5: A notification channel unvisible agent must be playable");
 
   rv = alarmAgent->StartPlaying(&playable);
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -708,23 +708,32 @@ nsEventStatus AsyncPanZoomController::On
     controller->HandleLongTap(gfx::RoundedToInt(point));
     return nsEventStatus_eConsumeNoDefault;
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
+  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
+  if (controller && mAllowZoom) {
+    ReentrantMonitorAutoEnter lock(mMonitor);
+
+    CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, mFrameMetrics.mZoom);
+    controller->HandleSingleTap(gfx::RoundedToInt(point));
+    return nsEventStatus_eConsumeNoDefault;
+  }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a single-tap-confirmed in state %d\n", this, mState);
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
-  if (controller) {
+  // If zooming is disabled, we handle this in OnSingleTapUp
+  if (controller && mAllowZoom) {
     ReentrantMonitorAutoEnter lock(mMonitor);
 
     CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, mFrameMetrics.mZoom);
     controller->HandleSingleTap(gfx::RoundedToInt(point));
     return nsEventStatus_eConsumeNoDefault;
   }
   return nsEventStatus_eIgnore;
 }
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -54,74 +54,126 @@ using namespace gfx;
 namespace layers {
 
 using namespace mozilla::gl;
 
 static inline IntSize ns2gfxSize(const nsIntSize& s) {
   return IntSize(s.width, s.height);
 }
 
-// Draw the supplied geometry with the already selected shader. Both aArray1
-// and aArray2 are expected to have a stride of 2 * sizeof(GLfloat).
+// Draw the given quads with the already selected shader. Texture coordinates
+// are supplied if the shader requires them.
 static void
-DrawWithVertexBuffer2(GLContext *aGLContext, VBOArena &aVBOs,
-                      GLenum aMode, GLsizei aElements,
-                      GLint aAttr1, GLfloat *aArray1,
-                      GLint aAttr2, GLfloat *aArray2)
+DrawQuads(GLContext *aGLContext,
+          VBOArena &aVBOs,
+          ShaderProgramOGL *aProg,
+          GLContext::RectTriangles &aRects)
 {
-  GLsizei bytes = aElements * 2 * sizeof(GLfloat);
+  NS_ASSERTION(aProg->HasInitialized(), "Shader program not correctly initialized");
+  GLuint vertAttribIndex =
+    aProg->AttribLocation(ShaderProgramOGL::VertexCoordAttrib);
+  GLuint texCoordAttribIndex =
+    aProg->AttribLocation(ShaderProgramOGL::TexCoordAttrib);
+  bool texCoords = (texCoordAttribIndex != GLuint(-1));
+
+  GLsizei elements = aRects.elements();
+  GLsizei bytes = elements * 2 * sizeof(GLfloat);
+
+  GLsizei total = bytes;
+  if (texCoords) {
+    total *= 2;
+  }
 
   aGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER,
                           aVBOs.Allocate(aGLContext));
   aGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER,
-                          2 * bytes,
+                          total,
                           nullptr,
                           LOCAL_GL_STREAM_DRAW);
 
   aGLContext->fBufferSubData(LOCAL_GL_ARRAY_BUFFER,
                              0,
                              bytes,
-                             aArray1);
-  aGLContext->fBufferSubData(LOCAL_GL_ARRAY_BUFFER,
-                             bytes,
-                             bytes,
-                             aArray2);
-
-  aGLContext->fEnableVertexAttribArray(aAttr1);
-  aGLContext->fEnableVertexAttribArray(aAttr2);
-
-  aGLContext->fVertexAttribPointer(aAttr1,
+                             aRects.vertexPointer());
+  aGLContext->fEnableVertexAttribArray(vertAttribIndex);
+  aGLContext->fVertexAttribPointer(vertAttribIndex,
                                    2, LOCAL_GL_FLOAT,
                                    LOCAL_GL_FALSE,
                                    0, BUFFER_OFFSET(0));
-  aGLContext->fVertexAttribPointer(aAttr2,
-                                   2, LOCAL_GL_FLOAT,
-                                   LOCAL_GL_FALSE,
-                                   0, BUFFER_OFFSET(bytes));
 
-  aGLContext->fDrawArrays(aMode, 0, aElements);
+  if (texCoords) {
+    aGLContext->fBufferSubData(LOCAL_GL_ARRAY_BUFFER,
+                               bytes,
+                               bytes,
+                               aRects.texCoordPointer());
+    aGLContext->fEnableVertexAttribArray(texCoordAttribIndex);
+    aGLContext->fVertexAttribPointer(texCoordAttribIndex,
+                                     2, LOCAL_GL_FLOAT,
+                                     LOCAL_GL_FALSE,
+                                     0, BUFFER_OFFSET(bytes));
+  }
 
-  aGLContext->fDisableVertexAttribArray(aAttr1);
-  aGLContext->fDisableVertexAttribArray(aAttr2);
+  aGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, elements);
+
+  aGLContext->fDisableVertexAttribArray(vertAttribIndex);
+  if (texCoords) {
+    aGLContext->fDisableVertexAttribArray(texCoordAttribIndex);
+  }
 
   aGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
 }
 
+// Size of the builtin font.
+static const float FontHeight = 7.f;
+static const float FontWidth = 5.f;
+static const float FontStride = 4.f;
+
+// Scale the font when drawing it to the viewport for better readability.
+static const float FontScaleX = 2.f;
+static const float FontScaleY = 3.f;
+
+// Size of the font texture (POT).
+static const size_t FontTextureWidth = 64;
+static const size_t FontTextureHeight = 8;
+
+static void
+AddDigits(GLContext::RectTriangles &aRects,
+          const gfx::IntSize aViewportSize,
+          unsigned int aOffset,
+          unsigned int aValue)
+{
+  unsigned int divisor = 100;
+  for (size_t n = 0; n < 3; ++n) {
+    gfxRect d(aOffset * FontWidth, 0.f, FontWidth, FontHeight);
+    d.Scale(FontScaleX / aViewportSize.width, FontScaleY / aViewportSize.height);
+
+    unsigned int digit = aValue % (divisor * 10) / divisor;
+    gfxRect t(digit * FontStride, 0.f, FontWidth, FontHeight);
+    t.Scale(1.f / FontTextureWidth, 1.f / FontTextureHeight);
+
+    aRects.addRect(d.x, d.y, d.x + d.width, d.y + d.height,
+                   t.x, t.y, t.x + t.width, t.y + t.height,
+                   false);
+    divisor /= 10;
+    ++aOffset;
+  }
+}
+
 void
-FPSState::DrawCounter(float offset,
-                      unsigned value,
-                      GLContext* context,
-                      ShaderProgramOGL* copyprog)
+FPSState::DrawFPS(TimeStamp aNow,
+                  unsigned int aFillRatio,
+                  GLContext* aContext,
+                  ShaderProgramOGL* aProgram)
 {
   if (!mTexture) {
     // Bind the number of textures we need, in this case one.
-    context->fGenTextures(1, &mTexture);
-    context->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
-    context->fTexParameteri(LOCAL_GL_TEXTURE_2D,LOCAL_GL_TEXTURE_MIN_FILTER,LOCAL_GL_NEAREST);
-    context->fTexParameteri(LOCAL_GL_TEXTURE_2D,LOCAL_GL_TEXTURE_MAG_FILTER,LOCAL_GL_NEAREST);
+    aContext->fGenTextures(1, &mTexture);
+    aContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
+    aContext->fTexParameteri(LOCAL_GL_TEXTURE_2D,LOCAL_GL_TEXTURE_MIN_FILTER,LOCAL_GL_NEAREST);
+    aContext->fTexParameteri(LOCAL_GL_TEXTURE_2D,LOCAL_GL_TEXTURE_MAG_FILTER,LOCAL_GL_NEAREST);
 
     const char *text =
       "                                         "
       " XXX XX  XXX XXX X X XXX XXX XXX XXX XXX "
       " X X  X    X   X X X X   X     X X X X X "
       " X X  X  XXX XXX XXX XXX XXX   X XXX XXX "
       " X X  X  X     X   X   X X X   X X X   X "
       " XXX XXX XXX XXX   X XXX XXX   X XXX   X "
@@ -131,102 +183,53 @@ FPSState::DrawCounter(float offset,
     uint32_t* buf = (uint32_t *) malloc(64 * 8 * sizeof(uint32_t));
     for (int i = 0; i < 7; i++) {
       for (int j = 0; j < 41; j++) {
         uint32_t purple = 0xfff000ff;
         uint32_t white  = 0xffffffff;
         buf[i * 64 + j] = (text[i * 41 + j] == ' ') ? purple : white;
       }
     }
-    context->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, 64, 8, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, buf);
+    aContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, 64, 8, 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, buf);
     free(buf);
   }
 
-  GLint viewport[4];
-  context->fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
+  mVBOs.Reset();
 
-  struct Vertex2D {
-    float x,y;
-  };
-  float oneOverVP2 = 1.0 / viewport[2];
-  float oneOverVP3 = 1.0 / viewport[3];
-  const Vertex2D vertices[] = {
-    { -1.0f + (offset +  0.f) * oneOverVP2, 1.0f - 42.f * oneOverVP3 },
-    { -1.0f + (offset +  0.f) * oneOverVP2, 1.0f},
-    { -1.0f + (offset + 22.f) * oneOverVP2, 1.0f - 42.f * oneOverVP3 },
-    { -1.0f + (offset + 22.f) * oneOverVP2, 1.0f },
-
-    { -1.0f + (offset + 22.f) * oneOverVP2, 1.0f - 42.f * oneOverVP3 },
-    { -1.0f + (offset + 22.f) * oneOverVP2, 1.0f },
-    { -1.0f + (offset + 44.f) * oneOverVP2, 1.0f - 42.f * oneOverVP3 },
-    { -1.0f + (offset + 44.f) * oneOverVP2, 1.0f },
+  GLint viewport[4];
+  aContext->fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
+  gfx::IntSize viewportSize(viewport[2], viewport[3]);
 
-    { -1.0f + (offset + 44.f) * oneOverVP2, 1.0f - 42.f * oneOverVP3 },
-    { -1.0f + (offset + 44.f) * oneOverVP2, 1.0f },
-    { -1.0f + (offset + 66.f) * oneOverVP2, 1.0f - 42.f * oneOverVP3 },
-    { -1.0f + (offset + 66.f) * oneOverVP2, 1.0f }
-  };
-
-  unsigned v1   = value % 10;
-  unsigned v10  = (value % 100) / 10;
-  unsigned v100 = (value % 1000) / 100;
+  unsigned int fps = unsigned(mCompositionFps.AddFrameAndGetFps(aNow));
+  unsigned int txnFps = unsigned(mTransactionFps.GetFpsAt(aNow));
 
-  const GLfloat texCoords[] = {
-    (v100 * 4.f) / 64, 7.f / 8,
-    (v100 * 4.f) / 64, 0.0f,
-    (v100 * 4.f + 4) / 64, 7.f / 8,
-    (v100 * 4.f + 4) / 64, 0.0f,
-
-    (v10 * 4.f) / 64, 7.f / 8,
-    (v10 * 4.f) / 64, 0.0f,
-    (v10 * 4.f + 4) / 64, 7.f / 8,
-    (v10 * 4.f + 4) / 64, 0.0f,
-
-    (v1 * 4.f) / 64, 7.f / 8,
-    (v1 * 4.f) / 64, 0.0f,
-    (v1 * 4.f + 4) / 64, 7.f / 8,
-    (v1 * 4.f + 4) / 64, 0.0f,
-  };
+  GLContext::RectTriangles rects;
+  AddDigits(rects, viewportSize, 0, fps);
+  AddDigits(rects, viewportSize, 4, txnFps);
+  AddDigits(rects, viewportSize, 8, aFillRatio);
 
   // Turn necessary features on
-  context->fEnable(LOCAL_GL_BLEND);
-  context->fBlendFunc(LOCAL_GL_ONE, LOCAL_GL_SRC_COLOR);
-
-  context->fActiveTexture(LOCAL_GL_TEXTURE0);
-  context->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
+  aContext->fEnable(LOCAL_GL_BLEND);
+  aContext->fBlendFunc(LOCAL_GL_ONE, LOCAL_GL_SRC_COLOR);
 
-  copyprog->Activate();
-  copyprog->SetTextureUnit(0);
-
-  // "COPY"
-  context->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ZERO,
-                              LOCAL_GL_ONE, LOCAL_GL_ZERO);
-
-  GLint vcattr = copyprog->AttribLocation(ShaderProgramOGL::VertexCoordAttrib);
-  GLint tcattr = copyprog->AttribLocation(ShaderProgramOGL::TexCoordAttrib);
+  aContext->fActiveTexture(LOCAL_GL_TEXTURE0);
+  aContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
 
-  DrawWithVertexBuffer2(context, mVBOs,
-                        LOCAL_GL_TRIANGLE_STRIP, 12,
-                        vcattr, (GLfloat *) vertices,
-                        tcattr, (GLfloat *) texCoords);
-}
+  aProgram->Activate();
+  aProgram->SetTextureUnit(0);
+  aProgram->SetLayerQuadRect(gfx::Rect(0.f, 0.f, viewport[2], viewport[3]));
+  aProgram->SetLayerOpacity(1.f);
+  aProgram->SetTextureTransform(gfx3DMatrix());
+  aProgram->SetLayerTransform(gfx3DMatrix());
+  aProgram->SetRenderOffset(0, 0);
 
-void
-FPSState::DrawFPS(TimeStamp aNow,
-                  unsigned aFillRatio,
-                  GLContext* context, ShaderProgramOGL* copyprog)
-{
-  mVBOs.Reset();
+  aContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ZERO,
+                               LOCAL_GL_ONE, LOCAL_GL_ZERO);
 
-  unsigned fps = unsigned(mCompositionFps.AddFrameAndGetFps(aNow));
-  unsigned txnFps = unsigned(mTransactionFps.GetFpsAt(aNow));
-
-  DrawCounter(0, fps, context, copyprog);
-  DrawCounter(80, txnFps, context, copyprog);
-  DrawCounter(160, aFillRatio, context, copyprog);
+  DrawQuads(aContext, mVBOs, aProgram, rects);
 }
 
 #ifdef CHECK_CURRENT_PROGRAM
 int ShaderProgramOGL::sCurrentProgramKey = 0;
 #endif
 
 CompositorOGL::CompositorOGL(nsIWidget *aWidget, int aSurfaceWidth,
                              int aSurfaceHeight, bool aUseExternalSurfaceSize)
@@ -285,17 +288,17 @@ CompositorOGL::AddPrograms(ShaderProgram
 GLuint
 CompositorOGL::GetTemporaryTexture(GLenum aTextureUnit)
 {
   size_t index = aTextureUnit - LOCAL_GL_TEXTURE0;
   // lazily grow the array of temporary textures
   if (mTextures.Length() <= index) {
     size_t prevLength = mTextures.Length();
     mTextures.SetLength(index + 1);
-    for(unsigned i = prevLength; i <= index; ++i) {
+    for(unsigned int i = prevLength; i <= index; ++i) {
       mTextures[i] = 0;
     }
   }
   // lazily initialize the temporary textures
   if (!mTextures[index]) {
     gl()->MakeCurrent();
     gl()->fGenTextures(1, &mTextures[index]);
   }
@@ -539,30 +542,23 @@ CompositorOGL::Initialize()
   return true;
 }
 
 // |aTexCoordRect| is the rectangle from the texture that we want to
 // draw using the given program.  The program already has a necessary
 // offset and scale, so the geometry that needs to be drawn is a unit
 // square from 0,0 to 1,1.
 //
-// |aTexSize| is the actual size of the texture, as it can be larger
-// than the rectangle given by |aTexCoordRect|.
+// |aTexture| is the texture we are drawing. Its actual size can be
+// larger than the rectangle given by |aTexCoordRect|.
 void
 CompositorOGL::BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg,
                                               const Rect& aTexCoordRect,
                                               TextureSource *aTexture)
 {
-  NS_ASSERTION(aProg->HasInitialized(), "Shader program not correctly initialized");
-  GLuint vertAttribIndex =
-    aProg->AttribLocation(ShaderProgramOGL::VertexCoordAttrib);
-  GLuint texCoordAttribIndex =
-    aProg->AttribLocation(ShaderProgramOGL::TexCoordAttrib);
-  NS_ASSERTION(texCoordAttribIndex != GLuint(-1), "no texture coords?");
-
   // Given what we know about these textures and coordinates, we can
   // compute fmod(t, 1.0f) to get the same texture coordinate out.  If
   // the texCoordRect dimension is < 0 or > width/height, then we have
   // wraparound that we need to deal with by drawing multiple quads,
   // because we can't rely on full non-power-of-two texture support
   // (which is required for the REPEAT wrap mode).
 
   GLContext::RectTriangles rects;
@@ -606,20 +602,17 @@ CompositorOGL::BindAndDrawQuadWithTextur
   } else {
     nsIntRect tcRect(texCoordRect.x, texCoordRect.y,
                      texCoordRect.width, texCoordRect.height);
     GLContext::DecomposeIntoNoRepeatTriangles(tcRect,
                                               nsIntSize(realTexSize.width, realTexSize.height),
                                               rects, flipped);
   }
 
-  DrawWithVertexBuffer2(mGLContext, mVBOs,
-                        LOCAL_GL_TRIANGLES, rects.elements(),
-                        vertAttribIndex, rects.vertexPointer(),
-                        texCoordAttribIndex, rects.texCoordPointer());
+  DrawQuads(mGLContext, mVBOs, aProg, rects);
 }
 
 void
 CompositorOGL::PrepareViewport(const gfx::IntSize& aSize,
                                const gfxMatrix& aWorldTransform)
 {
   // Set the viewport correctly.
   mGLContext->fViewport(0, 0, aSize.width, aSize.height);
@@ -1305,17 +1298,17 @@ CompositorOGL::EndFrame()
 
   if (mFPS) {
     float fillRatio = 0;
     if (mPixelsFilled > 0 && mPixelsPerFrame > 0) {
       fillRatio = 100.0f * float(mPixelsFilled) / float(mPixelsPerFrame);
       if (fillRatio > 999.0f)
         fillRatio = 999.0f;
     }
-    mFPS->DrawFPS(TimeStamp::Now(), unsigned(fillRatio), mGLContext, GetProgram(Copy2DProgramType));
+    mFPS->DrawFPS(TimeStamp::Now(), unsigned(fillRatio), mGLContext, GetProgram(RGBXLayerProgramType));
   }
 
   mGLContext->SwapBuffers();
   mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
 }
 
 void
 CompositorOGL::EndFrameForExternalComposition(const gfxMatrix& aTransform)
--- a/gfx/layers/opengl/FPSCounter.h
+++ b/gfx/layers/opengl/FPSCounter.h
@@ -70,17 +70,16 @@ private:
 struct FPSState {
   GLuint mTexture;
   FPSCounter mCompositionFps;
   FPSCounter mTransactionFps;
   gl::VBOArena mVBOs;
 
   FPSState() : mTexture(0) { }
 
-  void DrawCounter(float, unsigned, gl::GLContext*, ShaderProgramOGL*);
   void DrawFPS(TimeStamp, unsigned, gl::GLContext*, ShaderProgramOGL*);
 
   void NotifyShadowTreeTransaction() {
     mTransactionFps.AddFrame(TimeStamp::Now());
   }
 };
 
 }
--- a/js/src/jit-test/tests/ion/900683.js
+++ b/js/src/jit-test/tests/ion/900683.js
@@ -1,8 +1,11 @@
+if (typeof ParallelArray === "undefined")
+  quit();
+
 ParallelArray(11701, function() {
     return /x/
 }).reduce(function(a) {
     if (a % 9) {
         for (var y = 0; y; ++y) {}
         return []
     }
 })