From ceb21144c4f15b3ae9534086906717eef70ef925 Mon Sep 17 00:00:00 2001
From: Oleg Romashin
Date: Sat, 7 May 2011 11:26:58 +0300
Subject: [PATCH 001/314] Bug 646412 - fix autodial pref initialization
r=cbiesinger
---
netwerk/base/src/nsIOService.cpp | 3 +++
netwerk/base/src/nsIOService.h | 1 +
2 files changed, 4 insertions(+)
diff --git a/netwerk/base/src/nsIOService.cpp b/netwerk/base/src/nsIOService.cpp
index 7401cf9ec545..62d34d2dfae9 100644
--- a/netwerk/base/src/nsIOService.cpp
+++ b/netwerk/base/src/nsIOService.cpp
@@ -182,6 +182,7 @@ nsIOService::nsIOService()
, mShutdown(PR_FALSE)
, mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY)
, mContentSniffers(NS_CONTENT_SNIFFER_CATEGORY)
+ , mAutoDialEnabled(PR_FALSE)
{
}
@@ -304,6 +305,7 @@ nsIOService::InitializeSocketTransportService()
if (mSocketTransportService) {
rv = mSocketTransportService->Init();
NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service init failed");
+ mSocketTransportService->SetAutodialEnabled(mAutoDialEnabled);
}
return rv;
@@ -850,6 +852,7 @@ nsIOService::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
PRBool enableAutodial = PR_FALSE;
nsresult rv = prefs->GetBoolPref(AUTODIAL_PREF, &enableAutodial);
// If pref not found, default to disabled.
+ mAutoDialEnabled = enableAutodial;
if (NS_SUCCEEDED(rv)) {
if (mSocketTransportService)
mSocketTransportService->SetAutodialEnabled(enableAutodial);
diff --git a/netwerk/base/src/nsIOService.h b/netwerk/base/src/nsIOService.h
index 7a1b73b9bb68..6e083301e77a 100644
--- a/netwerk/base/src/nsIOService.h
+++ b/netwerk/base/src/nsIOService.h
@@ -161,6 +161,7 @@ private:
nsTArray mRestrictedPortList;
+ PRPackedBool mAutoDialEnabled;
public:
// Necko buffer cache. Used for all default buffer sizes that necko
// allocates.
From 46e3ef201dd0076ca4cb26b21cc1d084586d76d8 Mon Sep 17 00:00:00 2001
From: Oleg Romashin
Date: Sat, 7 May 2011 11:27:13 +0300
Subject: [PATCH 002/314] Bug 646412 - After Connection is established on
MeeGo. Browser shows Error Page. r=wolfiR
---
netwerk/base/src/nsAutodialQt.cpp | 4 ++--
netwerk/system/qt/nsQtNetworkLinkService.cpp | 12 ++++------
netwerk/system/qt/nsQtNetworkManager.cpp | 25 ++++++++++++++++++--
netwerk/system/qt/nsQtNetworkManager.h | 11 ++++++---
4 files changed, 38 insertions(+), 14 deletions(-)
diff --git a/netwerk/base/src/nsAutodialQt.cpp b/netwerk/base/src/nsAutodialQt.cpp
index e29788f1eeca..cb2ef913ea94 100644
--- a/netwerk/base/src/nsAutodialQt.cpp
+++ b/netwerk/base/src/nsAutodialQt.cpp
@@ -61,7 +61,7 @@ nsAutodial::Init()
nsresult
nsAutodial::DialDefault(const PRUnichar* hostName)
{
- if (gQtNetworkManager->openConnection(QString::fromUtf16(hostName))) {
+ if (nsQtNetworkManager::get()->openConnection(QString::fromUtf16(hostName))) {
return NS_OK;
}
@@ -71,7 +71,7 @@ nsAutodial::DialDefault(const PRUnichar* hostName)
PRBool
nsAutodial::ShouldDialOnNetworkError()
{
- if (gQtNetworkManager->isOnline()) {
+ if (nsQtNetworkManager::get()->isOnline()) {
return PR_FALSE;
}
diff --git a/netwerk/system/qt/nsQtNetworkLinkService.cpp b/netwerk/system/qt/nsQtNetworkLinkService.cpp
index 11d7d2890396..15e6f617d6f7 100644
--- a/netwerk/system/qt/nsQtNetworkLinkService.cpp
+++ b/netwerk/system/qt/nsQtNetworkLinkService.cpp
@@ -58,14 +58,14 @@ nsQtNetworkLinkService::~nsQtNetworkLinkService()
NS_IMETHODIMP
nsQtNetworkLinkService::GetIsLinkUp(PRBool* aIsUp)
{
- *aIsUp = gQtNetworkManager->isOnline();
+ *aIsUp = nsQtNetworkManager::get()->isOnline();
return NS_OK;
}
NS_IMETHODIMP
nsQtNetworkLinkService::GetLinkStatusKnown(PRBool* aIsKnown)
{
- *aIsKnown = gQtNetworkManager->isOnline();
+ *aIsKnown = nsQtNetworkManager::get()->isOnline();
return NS_OK;
}
@@ -76,8 +76,7 @@ nsQtNetworkLinkService::Observe(nsISupports* aSubject,
{
if (!strcmp(aTopic, "xpcom-shutdown")) {
Shutdown();
- delete gQtNetworkManager;
- gQtNetworkManager = 0;
+ nsQtNetworkManager::get()->destroy();
}
if (!strcmp(aTopic, "browser-lastwindow-close-granted")) {
@@ -96,8 +95,7 @@ nsQtNetworkLinkService::Init(void)
return NS_ERROR_FAILURE;
}
- delete gQtNetworkManager;
- gQtNetworkManager = new nsQtNetworkManager();
+ nsQtNetworkManager::create();
nsresult rv;
rv = observerService->AddObserver(this, "xpcom-shutdown", PR_FALSE);
@@ -117,6 +115,6 @@ nsQtNetworkLinkService::Init(void)
nsresult
nsQtNetworkLinkService::Shutdown()
{
- gQtNetworkManager->closeSession();
+ nsQtNetworkManager::get()->closeSession();
return NS_OK;
}
diff --git a/netwerk/system/qt/nsQtNetworkManager.cpp b/netwerk/system/qt/nsQtNetworkManager.cpp
index 592dae238068..27cb5be8cc17 100644
--- a/netwerk/system/qt/nsQtNetworkManager.cpp
+++ b/netwerk/system/qt/nsQtNetworkManager.cpp
@@ -48,6 +48,27 @@
#include
#include
+nsQtNetworkManager* nsQtNetworkManager::gQtNetworkManager = nsnull;
+
+void nsQtNetworkManager::create()
+{
+ if (!gQtNetworkManager) {
+ gQtNetworkManager = new nsQtNetworkManager();
+ connect(gQtNetworkManager, SIGNAL(openConnectionSignal()),
+ gQtNetworkManager, SLOT(openSession()),
+ Qt::BlockingQueuedConnection);
+ connect(&gQtNetworkManager->networkConfigurationManager,
+ SIGNAL(onlineStateChanged(bool)), gQtNetworkManager,
+ SLOT(onlineStateChanged(bool)));
+ }
+}
+
+void nsQtNetworkManager::destroy()
+{
+ delete gQtNetworkManager;
+ gQtNetworkManager = nsnull;
+}
+
nsQtNetworkManager::nsQtNetworkManager(QObject* parent)
: QObject(parent), networkSession(0)
{
@@ -141,7 +162,7 @@ nsQtNetworkManager::openSession()
// this only means we did not shutdown before...
// renew Session every time
// fix/workaround for prestart bug
- if (!networkSession) {
+ if (networkSession) {
networkSession->close();
networkSession->deleteLater();
}
@@ -168,7 +189,7 @@ nsQtNetworkManager::openSession()
void
nsQtNetworkManager::closeSession()
{
- if (!networkSession) {
+ if (networkSession) {
networkSession->close();
}
}
diff --git a/netwerk/system/qt/nsQtNetworkManager.h b/netwerk/system/qt/nsQtNetworkManager.h
index 37e10e59f914..3e4acffdbb31 100644
--- a/netwerk/system/qt/nsQtNetworkManager.h
+++ b/netwerk/system/qt/nsQtNetworkManager.h
@@ -47,16 +47,18 @@
class nsQtNetworkManager;
-static nsQtNetworkManager* gQtNetworkManager = nsnull;
+
class nsQtNetworkManager : public QObject
{
Q_OBJECT
public:
- explicit nsQtNetworkManager(QObject* parent = 0);
-
+ static void create();
+ static void destroy();
virtual ~nsQtNetworkManager();
+ static nsQtNetworkManager* get() { return gQtNetworkManager; }
+
static PRBool IsConnected();
static PRBool GetLinkStatusKnown();
static void enableInstance();
@@ -73,6 +75,9 @@ class nsQtNetworkManager : public QObject
void openSession();
private:
+ explicit nsQtNetworkManager(QObject* parent = 0);
+
+ static nsQtNetworkManager* gQtNetworkManager;
QNetworkSession* networkSession;
QNetworkConfiguration networkConfiguration;
QNetworkConfigurationManager networkConfigurationManager;
From 37941b9b4ffe847146475b7eba85544f81e9885c Mon Sep 17 00:00:00 2001
From: Tatiana Meshkova
Date: Tue, 3 May 2011 19:40:02 +0300
Subject: [PATCH 003/314] Bug 641460 - Fennec stays minimized when closing
multiple tabs on Maemo / MeeGo
---
mobile/chrome/content/browser.js | 3 +++
widget/src/qt/nsWindow.cpp | 3 +++
2 files changed, 6 insertions(+)
diff --git a/mobile/chrome/content/browser.js b/mobile/chrome/content/browser.js
index 9ef09984311f..c0add50e621a 100644
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -409,6 +409,9 @@ var Browser = {
(prompt.BUTTON_TITLE_CANCEL * prompt.BUTTON_POS_1);
this._waitingToClose = true;
+#ifdef MOZ_PLATFORM_MAEMO
+ window.QueryInterface(Ci.nsIDOMChromeWindow).restore();
+#endif
let pressed = prompt.confirmEx(window, title, message, buttons, closeText, null, null, checkText, warnOnClose);
this._waitingToClose = false;
diff --git a/widget/src/qt/nsWindow.cpp b/widget/src/qt/nsWindow.cpp
index fda5e831a897..332f23c74c3b 100644
--- a/widget/src/qt/nsWindow.cpp
+++ b/widget/src/qt/nsWindow.cpp
@@ -588,6 +588,9 @@ nsWindow::SetSizeMode(PRInt32 aMode)
nsresult rv;
LOG(("nsWindow::SetSizeMode [%p] %d\n", (void *)this, aMode));
+ if (aMode != nsSizeMode_Minimized) {
+ GetViewWidget()->activateWindow();
+ }
// Save the requested state.
rv = nsBaseWidget::SetSizeMode(aMode);
From bddf873447be1fa33182b731d87284baa407598b Mon Sep 17 00:00:00 2001
From: ffxbld
Date: Sat, 7 May 2011 03:20:36 -0700
Subject: [PATCH 004/314] Automated blocklist update from host linux-ix-slave15
---
browser/app/blocklist.xml | 45 ---------------------------------------
1 file changed, 45 deletions(-)
diff --git a/browser/app/blocklist.xml b/browser/app/blocklist.xml
index 6b3f61b0dcff..57c60dfacaf2 100644
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -15,12 +15,6 @@
-
-
-
-
-
-
@@ -42,9 +36,6 @@
-
-
-
@@ -55,9 +46,6 @@
-
-
-
@@ -196,38 +184,5 @@
7.0.0.0
GREATER_THAN_OR_EQUAL
-
- MAC OS X 10.5.8
- 0x10de
- DIRECT3D_9_LAYERS
- 7.0.0.0
- GREATER_THAN_OR_EQUAL
-
-
- MAC OS X 10.5.8
- 0x10de
- DIRECT3D_9_LAYERS
- BLOCKED_DRIVER_VERSION
- 7.0.0.0
- GREATER_THAN_OR_EQUAL
-
-
- MAC OS X 10.6
- 0x10de
-
- 0x0a6c
-
- DIRECT3D_9_LAYERS
- BLOCKED_DRIVER_VERSION
- 8.17.12.5896
- LESS_THAN_OR_EQUAL
-
-
- MAC OS X 10.6
- 0x10de
- DIRECT3D_9_LAYERS
- 7.0.0.0
- GREATER_THAN_OR_EQUAL
-
From 3f57f9d8d45609c8054e5ee095faeea365e4a844 Mon Sep 17 00:00:00 2001
From: Olli Pettay
Date: Sat, 7 May 2011 12:42:45 +0300
Subject: [PATCH 005/314] Bug 652752 - Click event isn't fired if mousedown
event and mouseup event are fired on different textnode of same element,
r=masayuki
--HG--
extra : rebase_source : 90c84bb703c5e314dcdcf3b435b842e680d591ad
---
content/events/src/nsEventStateManager.cpp | 17 +++++++++--------
content/events/test/test_bug534833.html | 2 +-
.../events/test/test_clickevent_on_input.html | 6 ++++++
3 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp
index 2f95f42e5662..a80cbe35b800 100644
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -3964,21 +3964,22 @@ nsEventStateManager::UpdateDragDataTransfer(nsDragEvent* dragEvent)
}
}
-nsIContent* GetParentContentForMouseTarget(nsIContent* aContent)
-{
- return aContent && (aContent->IsInNativeAnonymousSubtree() ||
- aContent->IsNodeOfType(nsINode::eTEXT)) ?
- aContent->GetParent() : nsnull;
-}
-
nsresult
nsEventStateManager::SetClickCount(nsPresContext* aPresContext,
nsMouseEvent *aEvent,
nsEventStatus* aStatus)
{
nsCOMPtr mouseContent;
+ nsIContent* mouseContentParent = nsnull;
mCurrentTarget->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(mouseContent));
- nsIContent* mouseContentParent = GetParentContentForMouseTarget(mouseContent);
+ if (mouseContent) {
+ if (mouseContent->IsNodeOfType(nsINode::eTEXT)) {
+ mouseContent = mouseContent->GetParent();
+ }
+ if (mouseContent && mouseContent->IsRootOfNativeAnonymousSubtree()) {
+ mouseContentParent = mouseContent->GetParent();
+ }
+ }
switch (aEvent->button) {
case nsMouseEvent::eLeftButton:
diff --git a/content/events/test/test_bug534833.html b/content/events/test/test_bug534833.html
index 0b5bbeec22bc..3a63b405a8ed 100644
--- a/content/events/test/test_bug534833.html
+++ b/content/events/test/test_bug534833.html
@@ -134,7 +134,7 @@ function finishTests() {
is(input2GotClick, 1, "input element should have got a click! (2)");
is(textarea1GotClick, 1, "textarea element should have got a click!");
is(textarea2GotClick, 1, "textarea element should have got a click! (2)");
- is(div1GotClick, 1, "div element's content text was replaced, it should have got 1 click!");
+ is(div1GotClick, 2, "div element's content text was replaced, it should have got 2 click!");
is(div2GotClick, 2, "div element's content text was modified, it should have got 2 clicks!");
SimpleTest.finish();
}
diff --git a/content/events/test/test_clickevent_on_input.html b/content/events/test/test_clickevent_on_input.html
index 8299c422ac22..2cd3547aa932 100644
--- a/content/events/test/test_clickevent_on_input.html
+++ b/content/events/test/test_clickevent_on_input.html
@@ -31,6 +31,12 @@ function runTests()
for (var i = 0; i < 3; i++) {
doTest(i);
}
+
+ // Re-test left clicking when the input element has some text.
+ gClickCount = 0;
+ input.value = "Long text Long text Long text Long text Long text Long text";
+ doTest(0);
+
input.style.display = "none";
SimpleTest.finish();
}
From 83cedda246d149f487974cdf99a4d01e9cb7a992 Mon Sep 17 00:00:00 2001
From: Jonathan Kew
Date: Sat, 7 May 2011 16:01:01 +0100
Subject: [PATCH 006/314] bug 655198 - don't parse 'all' as a value for
-moz-hyphens. r=dbaron
---
layout/base/nsStyleConsts.h | 1 -
layout/style/nsCSSProps.cpp | 1 -
layout/style/test/property_database.js | 2 +-
3 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/layout/base/nsStyleConsts.h b/layout/base/nsStyleConsts.h
index 736fb2fde640..aa45b293e1d8 100644
--- a/layout/base/nsStyleConsts.h
+++ b/layout/base/nsStyleConsts.h
@@ -709,7 +709,6 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) {
#define NS_STYLE_HYPHENS_NONE 0
#define NS_STYLE_HYPHENS_MANUAL 1
#define NS_STYLE_HYPHENS_AUTO 2
-#define NS_STYLE_HYPHENS_ALL 3
// See nsStyleText
#define NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT 0
diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp
index e372be45482c..db1381058317 100644
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1332,7 +1332,6 @@ const PRInt32 nsCSSProps::kHyphensKTable[] = {
eCSSKeyword_none, NS_STYLE_HYPHENS_NONE,
eCSSKeyword_manual, NS_STYLE_HYPHENS_MANUAL,
eCSSKeyword_auto, NS_STYLE_HYPHENS_AUTO,
- eCSSKeyword_all, NS_STYLE_HYPHENS_ALL,
eCSSKeyword_UNKNOWN,-1
};
diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js
index 45940342fd7f..a5c30c9e9b4e 100644
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -2722,7 +2722,7 @@ var gCSSProperties = {
inherited: true,
type: CSS_TYPE_LONGHAND,
initial_values: [ "manual" ],
- other_values: [ "none", "auto", "all" ],
+ other_values: [ "none", "auto" ],
invalid_values: []
},
"z-index": {
From b8ad9e935d60bd65cc12e2f4a9b4b1914e591b98 Mon Sep 17 00:00:00 2001
From: Matt Woodrow
Date: Sun, 8 May 2011 13:19:08 +1200
Subject: [PATCH 007/314] Bug 648604 - Re-enable plugin transform test on linux
when GPU layers are enabled. r=joe
---
modules/plugin/test/reftest/reftest.list | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/plugin/test/reftest/reftest.list b/modules/plugin/test/reftest/reftest.list
index c716f1c7f39b..dffb2e93590f 100644
--- a/modules/plugin/test/reftest/reftest.list
+++ b/modules/plugin/test/reftest/reftest.list
@@ -18,4 +18,4 @@ random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) =
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == plugin-background-5-step.html plugin-background-ref.html
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == plugin-background-10-step.html plugin-background-ref.html
random-if(!haveTestPlugin) == plugin-transform-1.html plugin-transform-1-ref.html
-fails-if(!haveTestPlugin) fails-if(http.platform=="X11") == plugin-transform-2.html plugin-transform-2-ref.html # bug 468496
+fails-if(!haveTestPlugin) fails-if(http.platform=="X11" && !layersGPUAccelerated) == plugin-transform-2.html plugin-transform-2-ref.html # bug 468496
From cc174c5b895d7975b4110a92d9d558f9a4b98a11 Mon Sep 17 00:00:00 2001
From: Matt Woodrow
Date: Sun, 8 May 2011 13:19:11 +1200
Subject: [PATCH 008/314] Bug 651469 - Add FastMovePixels to gfxASurface and
use it where appropriate. r=roc
---
gfx/thebes/gfxASurface.cpp | 34 ++++++++++++++++++++++++++++------
gfx/thebes/gfxASurface.h | 8 ++++++++
gfx/thebes/gfxD2DSurface.h | 6 ++++++
gfx/thebes/gfxQuartzSurface.h | 6 ++++++
gfx/thebes/gfxWindowsSurface.h | 6 ++++++
5 files changed, 54 insertions(+), 6 deletions(-)
diff --git a/gfx/thebes/gfxASurface.cpp b/gfx/thebes/gfxASurface.cpp
index a9ab802de835..fe169e64e35e 100644
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -501,15 +501,13 @@ gfxASurface::BytePerPixelFromFormat(gfxImageFormat format)
}
void
-gfxASurface::MovePixels(const nsIntRect& aSourceRect,
- const nsIntPoint& aDestTopLeft)
+gfxASurface::FastMovePixels(const nsIntRect& aSourceRect,
+ const nsIntPoint& aDestTopLeft)
{
+ // Used when the backend can internally handle self copies.
gfxIntSize size = GetSize();
nsIntRect dest(aDestTopLeft, aSourceRect.Size());
- // Assume that our cairo backend already knows how to properly
- // self-copy. gfxASurface subtypes whose backend can't self-copy
- // need their own implementations, or their backends need to be
- // fixed.
+
nsRefPtr ctx = new gfxContext(this);
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
nsIntPoint srcOrigin = dest.TopLeft() - aSourceRect.TopLeft();
@@ -518,6 +516,30 @@ gfxASurface::MovePixels(const nsIntRect& aSourceRect,
ctx->Fill();
}
+void
+gfxASurface::MovePixels(const nsIntRect& aSourceRect,
+ const nsIntPoint& aDestTopLeft)
+{
+ // Assume the backend can't handle self copying well and allocate
+ // a temporary surface instead.
+ nsRefPtr tmp =
+ CreateSimilarSurface(GetContentType(),
+ gfxIntSize(aSourceRect.width, aSourceRect.height));
+ nsRefPtr ctx = new gfxContext(tmp);
+ ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+ ctx->SetSource(this, gfxPoint(-aSourceRect.x, -aSourceRect.y));
+ ctx->Paint();
+
+ ctx = new gfxContext(this);
+ ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+ ctx->SetSource(tmp, gfxPoint(aDestTopLeft.x, aDestTopLeft.y));
+ ctx->Rectangle(gfxRect(aDestTopLeft.x,
+ aDestTopLeft.y,
+ aSourceRect.width,
+ aSourceRect.height));
+ ctx->Fill();
+}
+
/** Memory reporting **/
static const char *sSurfaceNamesForSurfaceType[] = {
diff --git a/gfx/thebes/gfxASurface.h b/gfx/thebes/gfxASurface.h
index 56ab8e2fe070..e79a0e365386 100644
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -256,6 +256,14 @@ protected:
static gfxASurface* GetSurfaceWrapper(cairo_surface_t *csurf);
static void SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf);
+ /**
+ * An implementation of MovePixels that assumes the backend can
+ * internally handle this operation and doesn't allocate any
+ * temporary surfaces.
+ */
+ void FastMovePixels(const nsIntRect& aSourceRect,
+ const nsIntPoint& aDestTopLeft);
+
// NB: Init() *must* be called from within subclass's
// constructors. It's unsafe to call it after the ctor finishes;
// leaks and use-after-frees are possible.
diff --git a/gfx/thebes/gfxD2DSurface.h b/gfx/thebes/gfxD2DSurface.h
index f1b4d9e73a64..85aae2b32e72 100644
--- a/gfx/thebes/gfxD2DSurface.h
+++ b/gfx/thebes/gfxD2DSurface.h
@@ -59,6 +59,12 @@ public:
gfxD2DSurface(cairo_surface_t *csurf);
+ void MovePixels(const nsIntRect& aSourceRect,
+ const nsIntPoint& aDestTopLeft)
+ {
+ FastMovePixels(aSourceRect, aDestTopLeft);
+ }
+
virtual ~gfxD2DSurface();
void Present();
diff --git a/gfx/thebes/gfxQuartzSurface.h b/gfx/thebes/gfxQuartzSurface.h
index ed81c7a85698..d5075bf0e9cd 100644
--- a/gfx/thebes/gfxQuartzSurface.h
+++ b/gfx/thebes/gfxQuartzSurface.h
@@ -68,6 +68,12 @@ public:
already_AddRefed GetAsImageSurface();
+ void MovePixels(const nsIntRect& aSourceRect,
+ const nsIntPoint& aDestTopLeft)
+ {
+ FastMovePixels(aSourceRect, aDestTopLeft);
+ }
+
protected:
CGContextRef mCGContext;
gfxSize mSize;
diff --git a/gfx/thebes/gfxWindowsSurface.h b/gfx/thebes/gfxWindowsSurface.h
index 95fbfd3e16fe..59171daa4491 100644
--- a/gfx/thebes/gfxWindowsSurface.h
+++ b/gfx/thebes/gfxWindowsSurface.h
@@ -92,6 +92,12 @@ public:
virtual PRInt32 GetDefaultContextFlags() const;
+ void MovePixels(const nsIntRect& aSourceRect,
+ const nsIntPoint& aDestTopLeft)
+ {
+ FastMovePixels(aSourceRect, aDestTopLeft);
+ }
+
private:
PRPackedBool mOwnsDC;
PRPackedBool mForPrinting;
From 2abdc868d1d38d1a7dcde021e133afb5499e1252 Mon Sep 17 00:00:00 2001
From: Matt Woodrow
Date: Sun, 8 May 2011 13:19:17 +1200
Subject: [PATCH 009/314] Bug 651469 - Add backwards scrolling reftest to catch
self-copy bugs. r=roc
---
.../reftests/scrolling/fixed-opacity-2.html | 42 +++++++++++++++++++
layout/reftests/scrolling/reftest.list | 1 +
2 files changed, 43 insertions(+)
create mode 100644 layout/reftests/scrolling/fixed-opacity-2.html
diff --git a/layout/reftests/scrolling/fixed-opacity-2.html b/layout/reftests/scrolling/fixed-opacity-2.html
new file mode 100644
index 000000000000..91a95cbc9944
--- /dev/null
+++ b/layout/reftests/scrolling/fixed-opacity-2.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
diff --git a/layout/reftests/scrolling/reftest.list b/layout/reftests/scrolling/reftest.list
index 3c649ec9292c..720bfc151e5c 100644
--- a/layout/reftests/scrolling/reftest.list
+++ b/layout/reftests/scrolling/reftest.list
@@ -1,5 +1,6 @@
HTTP == fixed-1.html fixed-1.html?ref
HTTP == fixed-opacity-1.html fixed-opacity-1.html?ref
+HTTP == fixed-opacity-2.html fixed-opacity-2.html?ref
HTTP == fixed-text-1.html fixed-text-1.html?ref
HTTP == fixed-text-2.html fixed-text-2.html?ref
HTTP == opacity-mixed-scrolling-1.html opacity-mixed-scrolling-1.html?ref
From a0703b2a12887a20d26592e955a46521ea77510a Mon Sep 17 00:00:00 2001
From: Matt Woodrow
Date: Sun, 8 May 2011 14:01:20 +1200
Subject: [PATCH 010/314] Remove spaces from reftest manifest file. r=orange
---
modules/plugin/test/reftest/reftest.list | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/plugin/test/reftest/reftest.list b/modules/plugin/test/reftest/reftest.list
index dffb2e93590f..ea462f3245bc 100644
--- a/modules/plugin/test/reftest/reftest.list
+++ b/modules/plugin/test/reftest/reftest.list
@@ -18,4 +18,4 @@ random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) =
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == plugin-background-5-step.html plugin-background-ref.html
random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == plugin-background-10-step.html plugin-background-ref.html
random-if(!haveTestPlugin) == plugin-transform-1.html plugin-transform-1-ref.html
-fails-if(!haveTestPlugin) fails-if(http.platform=="X11" && !layersGPUAccelerated) == plugin-transform-2.html plugin-transform-2-ref.html # bug 468496
+fails-if(!haveTestPlugin) fails-if(http.platform=="X11"&&!layersGPUAccelerated) == plugin-transform-2.html plugin-transform-2-ref.html # bug 468496
From 0c662cc62d27edd963ef90c3fdf7b119fd9851da Mon Sep 17 00:00:00 2001
From: Chris Pearce
Date: Sun, 8 May 2011 18:24:09 +1200
Subject: [PATCH 011/314] Bug 650994 - Decode ogg packets lazily, timestamp
them at demux time. r=doublec
---
.../base/test/file_mozfiledataurl_inner.html | 2 +-
content/base/test/test_mozfiledataurl.html | 8 +-
.../media/nsBuiltinDecoderStateMachine.cpp | 21 +-
content/media/ogg/nsOggCodecState.cpp | 430 +++++++++++-
content/media/ogg/nsOggCodecState.h | 216 ++++--
content/media/ogg/nsOggReader.cpp | 633 +++++-------------
content/media/ogg/nsOggReader.h | 54 +-
content/media/raw/nsRawReader.cpp | 1 -
8 files changed, 778 insertions(+), 587 deletions(-)
diff --git a/content/base/test/file_mozfiledataurl_inner.html b/content/base/test/file_mozfiledataurl_inner.html
index 83df19efb80e..1b8104a1496e 100644
--- a/content/base/test/file_mozfiledataurl_inner.html
+++ b/content/base/test/file_mozfiledataurl_inner.html
@@ -62,7 +62,7 @@ onload = function() {
iframe = document.getElementById('iframe');
iframe.onerror = iframe.onload = iframeNotifyParent;
audio = document.getElementById('audio');
- audio.onerror = audio.oncanplay = audioNotifyParent;
+ audio.onerror = audio.onloadeddata = audioNotifyParent;
}
diff --git a/content/base/test/test_mozfiledataurl.html b/content/base/test/test_mozfiledataurl.html
index c32cf3547157..b9383f9db9de 100644
--- a/content/base/test/test_mozfiledataurl.html
+++ b/content/base/test/test_mozfiledataurl.html
@@ -12,7 +12,7 @@
-
@@ -90,11 +90,11 @@ function runTest() {
var fileurl = URL.createObjectURL(file);
audio.src = fileurl;
var e = (yield);
- is(e.type, "canplay", "loaded successfully");
+ is(e.type, "loadeddata", "loaded successfully");
// Revoke url and attempt to load a audio in this document
audio.src = "file_mozfiledataurl_audio.ogg";
- is((yield).type, "canplay", "successfully reset audio");
+ is((yield).type, "loadeddata", "successfully reset audio");
URL.revokeObjectURL(fileurl);
todo(false, "urls need to act like 404s, not fail to parse");
/* img.src = fileurl;
@@ -113,7 +113,7 @@ function runTest() {
yield;
inner.contentWindow.postMessage(JSON.stringify({audio:fileurl}), "*");
var res = (yield);
- is(res.type, "canplay", "loaded successfully");
+ is(res.type, "loadeddata", "loaded successfully");
// Attempt to load an audio in a different cross-origin document
inner.src = innerCrossSiteURI;
diff --git a/content/media/nsBuiltinDecoderStateMachine.cpp b/content/media/nsBuiltinDecoderStateMachine.cpp
index b7f280ecd13d..cdf4ff75bbfd 100644
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -310,7 +310,7 @@ void nsBuiltinDecoderStateMachine::DecodeLoop()
{
skipToNextKeyframe = PR_TRUE;
- LOG(PR_LOG_DEBUG, ("Skipping video decode to the next keyframe"));
+ LOG(PR_LOG_DEBUG, ("%p Skipping video decode to the next keyframe", mDecoder));
}
// Video decode.
@@ -390,7 +390,7 @@ void nsBuiltinDecoderStateMachine::DecodeLoop()
mDecoder->GetReentrantMonitor().NotifyAll();
}
- LOG(PR_LOG_DEBUG, ("Shutting down DecodeLoop this=%p", this));
+ LOG(PR_LOG_DEBUG, ("%p Shutting down DecodeLoop this=%p", mDecoder, this));
}
PRBool nsBuiltinDecoderStateMachine::IsPlaying()
@@ -403,7 +403,7 @@ PRBool nsBuiltinDecoderStateMachine::IsPlaying()
void nsBuiltinDecoderStateMachine::AudioLoop()
{
NS_ASSERTION(OnAudioThread(), "Should be on audio thread.");
- LOG(PR_LOG_DEBUG, ("Begun audio thread/loop"));
+ LOG(PR_LOG_DEBUG, ("%p Begun audio thread/loop", mDecoder));
PRInt64 audioDuration = 0;
PRInt64 audioStartTime = -1;
PRUint32 channels, rate;
@@ -586,7 +586,7 @@ void nsBuiltinDecoderStateMachine::AudioLoop()
// for this to finish.
mDecoder->GetReentrantMonitor().NotifyAll();
}
- LOG(PR_LOG_DEBUG, ("Audio stream finished playing, audio thread exit"));
+ LOG(PR_LOG_DEBUG, ("%p Audio stream finished playing, audio thread exit", mDecoder));
}
PRUint32 nsBuiltinDecoderStateMachine::PlaySilence(PRUint32 aSamples,
@@ -1212,7 +1212,7 @@ nsresult nsBuiltinDecoderStateMachine::Run()
continue;
// Try to decode another frame to detect if we're at the end...
- LOG(PR_LOG_DEBUG, ("Seek completed, mCurrentFrameTime=%lld\n", mCurrentFrameTime));
+ LOG(PR_LOG_DEBUG, ("%p Seek completed, mCurrentFrameTime=%lld\n", mDecoder, mCurrentFrameTime));
// Change state to DECODING or COMPLETED now. SeekingStopped will
// call nsBuiltinDecoderStateMachine::Seek to reset our state to SEEKING
@@ -1321,7 +1321,7 @@ nsresult nsBuiltinDecoderStateMachine::Run()
if (mState != DECODER_STATE_COMPLETED)
continue;
- LOG(PR_LOG_DEBUG, ("Shutting down the state machine thread"));
+ LOG(PR_LOG_DEBUG, ("%p Shutting down the state machine thread", mDecoder));
StopDecodeThreads();
if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) {
@@ -1631,7 +1631,7 @@ void nsBuiltinDecoderStateMachine::LoadMetadata()
"Should be on state machine thread.");
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
- LOG(PR_LOG_DEBUG, ("Loading Media Headers"));
+ LOG(PR_LOG_DEBUG, ("%p Loading Media Headers", mDecoder));
nsresult res;
nsVideoInfo info;
{
@@ -1682,10 +1682,11 @@ void nsBuiltinDecoderStateMachine::StartBuffering()
// the element we're buffering or not.
UpdateReadyState();
mState = DECODER_STATE_BUFFERING;
- LOG(PR_LOG_DEBUG, ("Changed state from DECODING to BUFFERING, decoded for %.3lfs",
- decodeDuration.ToSeconds()));
+ LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING to BUFFERING, decoded for %.3lfs",
+ mDecoder, decodeDuration.ToSeconds()));
nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
- LOG(PR_LOG_DEBUG, ("Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
+ LOG(PR_LOG_DEBUG, ("%p Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
+ mDecoder,
stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)"));
}
diff --git a/content/media/ogg/nsOggCodecState.cpp b/content/media/ogg/nsOggCodecState.cpp
index ef62b915ab0d..80d83a5964d2 100644
--- a/content/media/ogg/nsOggCodecState.cpp
+++ b/content/media/ogg/nsOggCodecState.cpp
@@ -54,6 +54,7 @@ extern PRLogModuleInfo* gBuiltinDecoderLog;
nsOggCodecState*
nsOggCodecState::Create(ogg_page* aPage)
{
+ NS_ASSERTION(ogg_page_bos(aPage), "Only call on BOS page!");
nsAutoPtr
codecState;
if (aPage->body_len > 6 && memcmp(aPage->body+1, "theora", 6) == 0) {
codecState = new nsTheoraState(aPage);
@@ -62,16 +63,16 @@ nsOggCodecState::Create(ogg_page* aPage)
} else if (aPage->body_len > 8 && memcmp(aPage->body, "fishead\0", 8) == 0) {
codecState = new nsSkeletonState(aPage);
} else {
- codecState = new nsOggCodecState(aPage);
+ codecState = new nsOggCodecState(aPage, PR_FALSE);
}
return codecState->nsOggCodecState::Init() ? codecState.forget() : nsnull;
}
-nsOggCodecState::nsOggCodecState(ogg_page* aBosPage) :
+nsOggCodecState::nsOggCodecState(ogg_page* aBosPage, PRBool aActive) :
mPacketCount(0),
mSerial(ogg_page_serialno(aBosPage)),
- mActive(PR_FALSE),
- mDoneReadingHeaders(PR_FALSE)
+ mActive(aActive),
+ mDoneReadingHeaders(!aActive)
{
MOZ_COUNT_CTOR(nsOggCodecState);
memset(&mState, 0, sizeof(ogg_stream_state));
@@ -79,10 +80,11 @@ nsOggCodecState::nsOggCodecState(ogg_page* aBosPage) :
nsOggCodecState::~nsOggCodecState() {
MOZ_COUNT_DTOR(nsOggCodecState);
+ Reset();
#ifdef DEBUG
int ret =
#endif
- ogg_stream_clear(&mState);
+ ogg_stream_clear(&mState);
NS_ASSERTION(ret == 0, "ogg_stream_clear failed");
}
@@ -90,40 +92,129 @@ nsresult nsOggCodecState::Reset() {
if (ogg_stream_reset(&mState) != 0) {
return NS_ERROR_FAILURE;
}
- mBuffer.Erase();
+ mPackets.Erase();
+ ClearUnstamped();
return NS_OK;
}
+void nsOggCodecState::ClearUnstamped()
+{
+ for (PRUint32 i = 0; i < mUnstamped.Length(); ++i) {
+ nsOggCodecState::ReleasePacket(mUnstamped[i]);
+ }
+ mUnstamped.Clear();
+}
+
PRBool nsOggCodecState::Init() {
int ret = ogg_stream_init(&mState, mSerial);
return ret == 0;
}
-void nsPageQueue::Append(ogg_page* aPage) {
- ogg_page* p = new ogg_page();
- p->header_len = aPage->header_len;
- p->body_len = aPage->body_len;
- p->header = new unsigned char[p->header_len + p->body_len];
- p->body = p->header + p->header_len;
- memcpy(p->header, aPage->header, p->header_len);
- memcpy(p->body, aPage->body, p->body_len);
- nsDeque::Push(p);
+void nsVorbisState::RecordVorbisPacketSamples(ogg_packet* aPacket,
+ long aSamples)
+{
+#ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
+ mVorbisPacketSamples[aPacket] = aSamples;
+#endif
}
-PRBool nsOggCodecState::PageInFromBuffer() {
- if (mBuffer.IsEmpty())
- return PR_FALSE;
- ogg_page *p = mBuffer.PeekFront();
- int ret = ogg_stream_pagein(&mState, p);
- NS_ENSURE_TRUE(ret == 0, PR_FALSE);
- mBuffer.PopFront();
- delete [] p->header;
- delete p;
- return PR_TRUE;
+void nsVorbisState::ValidateVorbisPacketSamples(ogg_packet* aPacket,
+ long aSamples)
+{
+#ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
+ NS_ASSERTION(mVorbisPacketSamples[aPacket] == aSamples,
+ "Decoded samples for Vorbis packet don't match expected!");
+ mVorbisPacketSamples.erase(aPacket);
+#endif
+}
+
+void nsVorbisState::AssertHasRecordedPacketSamples(ogg_packet* aPacket)
+{
+#ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
+ NS_ASSERTION(mVorbisPacketSamples.count(aPacket) == 1,
+ "Must have recorded packet samples");
+#endif
+}
+
+static ogg_packet* Clone(ogg_packet* aPacket) {
+ ogg_packet* p = new ogg_packet();
+ memcpy(p, aPacket, sizeof(ogg_packet));
+ p->packet = new unsigned char[p->bytes];
+ memcpy(p->packet, aPacket->packet, p->bytes);
+ return p;
+}
+
+void nsOggCodecState::ReleasePacket(ogg_packet* aPacket) {
+ if (aPacket)
+ delete [] aPacket->packet;
+ delete aPacket;
+}
+
+void nsPacketQueue::Append(ogg_packet* aPacket) {
+ nsDeque::Push(aPacket);
+}
+
+ogg_packet* nsOggCodecState::PacketOut() {
+ if (mPackets.IsEmpty()) {
+ return nsnull;
+ }
+ return mPackets.PopFront();
+}
+
+nsresult nsOggCodecState::PageIn(ogg_page* aPage) {
+ if (!mActive)
+ return NS_OK;
+ NS_ASSERTION(ogg_page_serialno(aPage) == mSerial, "Page must be for this stream!");
+ if (ogg_stream_pagein(&mState, aPage) == -1)
+ return NS_ERROR_FAILURE;
+ int r;
+ do {
+ ogg_packet packet;
+ r = ogg_stream_packetout(&mState, &packet);
+ if (r == 1) {
+ mPackets.Append(Clone(&packet));
+ }
+ } while (r != 0);
+ if (ogg_stream_check(&mState)) {
+ NS_WARNING("Unrecoverable error in ogg_stream_packetout");
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+PRBool
+nsOggCodecState::PacketOutUntilGranulepos()
+{
+ int r;
+ PRBool foundGp = PR_FALSE;
+ // Extract packets from the sync state until either no more packets
+ // come out, or we get a data packet with non -1 granulepos.
+ do {
+ ogg_packet packet;
+ r = ogg_stream_packetout(&mState, &packet);
+ if (r == 1) {
+ ogg_packet* clone = Clone(&packet);
+ if (IsHeader(&packet)) {
+ // Header packets go straight into the packet queue.
+ mPackets.Append(clone);
+ } else {
+ // We buffer data packets until we encounter a granulepos. We'll
+ // then use the granulepos to figure out the granulepos of the
+ // preceeding packets.
+ mUnstamped.AppendElement(clone);
+ foundGp = packet.granulepos != -1;
+ }
+ }
+ } while (r != 0 && !foundGp);
+ if (ogg_stream_check(&mState)) {
+ NS_WARNING("Unrecoverable error in ogg_stream_packetout");
+ return NS_ERROR_FAILURE;
+ }
+ return foundGp;
}
nsTheoraState::nsTheoraState(ogg_page* aBosPage) :
- nsOggCodecState(aBosPage),
+ nsOggCodecState(aBosPage, PR_TRUE),
mSetup(0),
mCtx(0),
mPixelAspectRatio(0)
@@ -211,6 +302,11 @@ nsTheoraState::Time(PRInt64 granulepos) {
return nsTheoraState::Time(&mInfo, granulepos);
}
+PRBool
+nsTheoraState::IsHeader(ogg_packet* aPacket) {
+ return th_packet_isheader(aPacket);
+}
+
# define TH_VERSION_CHECK(_info,_maj,_min,_sub) \
((_info)->version_major>(_maj)||(_info)->version_major==(_maj)&& \
((_info)->version_minor>(_min)||(_info)->version_minor==(_min)&& \
@@ -272,6 +368,127 @@ nsTheoraState::MaxKeyframeOffset()
return frameDuration * keyframeDiff;
}
+nsresult
+nsTheoraState::PageIn(ogg_page* aPage)
+{
+ if (!mActive)
+ return NS_OK;
+ NS_ASSERTION(static_cast(ogg_page_serialno(aPage)) == mSerial,
+ "Page must be for this stream!");
+ if (ogg_stream_pagein(&mState, aPage) == -1)
+ return NS_ERROR_FAILURE;
+ PRBool foundGp = PacketOutUntilGranulepos();
+ if (foundGp && mDoneReadingHeaders) {
+ // We've found a packet with a granulepos, and we've loaded our metadata
+ // and initialized our decoder. Determine granulepos of buffered packets.
+ ReconstructTheoraGranulepos();
+ for (PRUint32 i = 0; i < mUnstamped.Length(); ++i) {
+ ogg_packet* packet = mUnstamped[i];
+#ifdef DEBUG
+ NS_ASSERTION(!IsHeader(packet), "Don't try to recover header packet gp");
+ NS_ASSERTION(packet->granulepos != -1, "Packet must have gp by now");
+#endif
+ mPackets.Append(packet);
+ }
+ mUnstamped.Clear();
+ }
+ return NS_OK;
+}
+
+// Returns 1 if the Theora info struct is decoding a media of Theora
+// verion (maj,min,sub) or later, otherwise returns 0.
+int
+TheoraVersion(th_info* info,
+ unsigned char maj,
+ unsigned char min,
+ unsigned char sub)
+{
+ ogg_uint32_t ver = (maj << 16) + (min << 8) + sub;
+ ogg_uint32_t th_ver = (info->version_major << 16) +
+ (info->version_minor << 8) +
+ info->version_subminor;
+ return (th_ver >= ver) ? 1 : 0;
+}
+
+void nsTheoraState::ReconstructTheoraGranulepos()
+{
+ if (mUnstamped.Length() == 0) {
+ return;
+ }
+ ogg_int64_t lastGranulepos = mUnstamped[mUnstamped.Length() - 1]->granulepos;
+ NS_ASSERTION(lastGranulepos != -1, "Must know last granulepos");
+
+ // Reconstruct the granulepos (and thus timestamps) of the decoded
+ // frames. Granulepos are stored as ((keyframe<> shift;
+
+ // The lastFrame, firstFrame, keyframe variables, as well as the frame
+ // variable in the loop below, store the frame number for Theora
+ // version >= 3.2.1 streams, and store the frame index for Theora
+ // version < 3.2.1 streams.
+ for (PRUint32 i = 0; i < mUnstamped.Length() - 1; ++i) {
+ ogg_int64_t frame = firstFrame + i;
+ ogg_int64_t granulepos;
+ ogg_packet* packet = mUnstamped[i];
+ PRBool isKeyframe = th_packet_iskeyframe(packet) == 1;
+
+ if (isKeyframe) {
+ granulepos = frame << shift;
+ keyframe = frame;
+ } else if (frame >= keyframe &&
+ frame - keyframe < ((ogg_int64_t)1 << shift))
+ {
+ // (frame - keyframe) won't overflow the "offset" segment of the
+ // granulepos, so it's safe to calculate the granulepos.
+ granulepos = (keyframe << shift) + (frame - keyframe);
+ } else {
+ // (frame - keyframeno) will overflow the "offset" segment of the
+ // granulepos, so we take "keyframe" to be the max possible offset
+ // frame instead.
+ ogg_int64_t k = NS_MAX(frame - (((ogg_int64_t)1 << shift) - 1), version_3_2_1);
+ granulepos = (k << shift) + (frame - k);
+ }
+ // Theora 3.2.1+ granulepos store frame number [1..N], so granulepos
+ // should be > 0.
+ // Theora 3.2.0 granulepos store the frame index [0..(N-1)], so
+ // granulepos should be >= 0.
+ NS_ASSERTION(granulepos >= version_3_2_1,
+ "Invalid granulepos for Theora version");
+
+ // Check that the frame's granule number is one more than the
+ // previous frame's.
+ NS_ASSERTION(i == 0 ||
+ th_granule_frame(mCtx, granulepos) ==
+ th_granule_frame(mCtx, mUnstamped[i-1]->granulepos) + 1,
+ "Granulepos calculation is incorrect!");
+
+ packet->granulepos = granulepos;
+ }
+
+ // Check that the second to last frame's granule number is one less than
+ // the last frame's (the known granule number). If not our granulepos
+ // recovery missed a beat.
+ NS_ASSERTION(mUnstamped.Length() < 2 ||
+ th_granule_frame(mCtx, mUnstamped[mUnstamped.Length()-2]->granulepos) + 1 ==
+ th_granule_frame(mCtx, lastGranulepos),
+ "Granulepos recovery should catch up with packet->granulepos!");
+}
+
nsresult nsVorbisState::Reset()
{
nsresult res = NS_OK;
@@ -281,11 +498,17 @@ nsresult nsVorbisState::Reset()
if (NS_FAILED(nsOggCodecState::Reset())) {
return NS_ERROR_FAILURE;
}
+
+ mGranulepos = 0;
+ mPrevVorbisBlockSize = 0;
+
return res;
}
nsVorbisState::nsVorbisState(ogg_page* aBosPage) :
- nsOggCodecState(aBosPage)
+ nsOggCodecState(aBosPage, PR_TRUE),
+ mPrevVorbisBlockSize(0),
+ mGranulepos(0)
{
MOZ_COUNT_CTOR(nsVorbisState);
vorbis_info_init(&mInfo);
@@ -296,6 +519,7 @@ nsVorbisState::nsVorbisState(ogg_page* aBosPage) :
nsVorbisState::~nsVorbisState() {
MOZ_COUNT_DTOR(nsVorbisState);
+ Reset();
vorbis_block_clear(&mBlock);
vorbis_dsp_clear(&mDsp);
vorbis_info_clear(&mInfo);
@@ -327,12 +551,13 @@ PRBool nsVorbisState::DecodeHeader(ogg_packet* aPacket) {
if (ret < 0 || mPacketCount > 3) {
// We've received an error, or the first three packets weren't valid
- // header packets, assume bad input, and don't activate the bitstream.
+ // header packets, assume bad input, and deactivate the bitstream.
mDoneReadingHeaders = PR_TRUE;
+ mActive = PR_FALSE;
} else if (ret == 0 && isSetupHeader && mPacketCount == 3) {
- // Successfully read the three header packets, activate the bitstream.
+ // Successfully read the three header packets.
+ // The bitstream remains active.
mDoneReadingHeaders = PR_TRUE;
- mActive = PR_TRUE;
}
return mDoneReadingHeaders;
}
@@ -377,8 +602,151 @@ PRInt64 nsVorbisState::Time(vorbis_info* aInfo, PRInt64 aGranulepos)
return t / aInfo->rate;
}
+PRBool
+nsVorbisState::IsHeader(ogg_packet* aPacket)
+{
+ // The first byte in each Vorbis header packet is either 0x01, 0x03, or 0x05,
+ // i.e. the first bit is odd. Audio data packets have their first bit as 0x0.
+ // Any packet with its first bit set cannot be a data packet, it's a
+ // (possibly invalid) header packet.
+ // See: http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2.1
+ return aPacket->bytes > 0 ? (aPacket->packet[0] & 0x1) : PR_FALSE;
+}
+
+nsresult
+nsVorbisState::PageIn(ogg_page* aPage)
+{
+ if (!mActive)
+ return NS_OK;
+ NS_ASSERTION(static_cast(ogg_page_serialno(aPage)) == mSerial,
+ "Page must be for this stream!");
+ if (ogg_stream_pagein(&mState, aPage) == -1)
+ return NS_ERROR_FAILURE;
+ PRBool foundGp = PacketOutUntilGranulepos();
+ if (foundGp && mDoneReadingHeaders) {
+ // We've found a packet with a granulepos, and we've loaded our metadata
+ // and initialized our decoder. Determine granulepos of buffered packets.
+ ReconstructVorbisGranulepos();
+ for (PRUint32 i = 0; i < mUnstamped.Length(); ++i) {
+ ogg_packet* packet = mUnstamped[i];
+ AssertHasRecordedPacketSamples(packet);
+ NS_ASSERTION(!IsHeader(packet), "Don't try to recover header packet gp");
+ NS_ASSERTION(packet->granulepos != -1, "Packet must have gp by now");
+ mPackets.Append(packet);
+ }
+ mUnstamped.Clear();
+ }
+ return NS_OK;
+}
+
+nsresult nsVorbisState::ReconstructVorbisGranulepos()
+{
+ // The number of samples in a Vorbis packet is:
+ // window_blocksize(previous_packet)/4+window_blocksize(current_packet)/4
+ // See: http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-230001.3.2
+ // So we maintain mPrevVorbisBlockSize, the block size of the last packet
+ // encountered. We also maintain mGranulepos, which is the granulepos of
+ // the last encountered packet. This enables us to give granulepos to
+ // packets when the last packet in mUnstamped doesn't have a granulepos
+ // (for example if the stream was truncated).
+ //
+ // We validate our prediction of the number of samples decoded when
+ // VALIDATE_VORBIS_SAMPLE_CALCULATION is defined by recording the predicted
+ // number of samples, and verifing we extract that many when decoding
+ // each packet.
+
+ NS_ASSERTION(mUnstamped.Length() > 0, "Length must be > 0");
+ ogg_packet* last = mUnstamped[mUnstamped.Length()-1];
+ NS_ASSERTION(last->e_o_s || last->granulepos >= 0,
+ "Must know last granulepos!");
+ if (mUnstamped.Length() == 1) {
+ ogg_packet* packet = mUnstamped[0];
+ long blockSize = vorbis_packet_blocksize(&mInfo, packet);
+ if (blockSize < 0) {
+ // On failure vorbis_packet_blocksize returns < 0. If we've got
+ // a bad packet, we just assume that decode will have to skip this
+ // packet, i.e. assume 0 samples are decodable from this packet.
+ blockSize = 0;
+ mPrevVorbisBlockSize = 0;
+ }
+ long samples = mPrevVorbisBlockSize / 4 + blockSize / 4;
+ mPrevVorbisBlockSize = blockSize;
+ if (packet->granulepos == -1) {
+ packet->granulepos = mGranulepos + samples;
+ }
+ mGranulepos = packet->granulepos;
+ RecordVorbisPacketSamples(packet, samples);
+ return NS_OK;
+ }
+
+ PRBool unknownGranulepos = last->granulepos == -1;
+ int totalSamples = 0;
+ for (PRInt32 i = mUnstamped.Length() - 1; i > 0; i--) {
+ ogg_packet* packet = mUnstamped[i];
+ ogg_packet* prev = mUnstamped[i-1];
+ ogg_int64_t granulepos = packet->granulepos;
+ NS_ASSERTION(granulepos != -1, "Must know granulepos!");
+ long prevBlockSize = vorbis_packet_blocksize(&mInfo, prev);
+ long blockSize = vorbis_packet_blocksize(&mInfo, packet);
+
+ if (blockSize < 0 || prevBlockSize < 0) {
+ // On failure vorbis_packet_blocksize returns < 0. If we've got
+ // a bad packet, we just assume that decode will have to skip this
+ // packet, i.e. assume 0 samples are decodable from this packet.
+ blockSize = 0;
+ prevBlockSize = 0;
+ }
+
+ long samples = prevBlockSize / 4 + blockSize / 4;
+ totalSamples += samples;
+ prev->granulepos = granulepos - samples;
+ RecordVorbisPacketSamples(packet, samples);
+ }
+
+ if (unknownGranulepos) {
+ for (PRUint32 i = 0; i < mUnstamped.Length(); i++) {
+ ogg_packet* packet = mUnstamped[i];
+ packet->granulepos += mGranulepos + totalSamples + 1;
+ }
+ }
+
+ ogg_packet* first = mUnstamped[0];
+ long blockSize = vorbis_packet_blocksize(&mInfo, first);
+ if (blockSize < 0) {
+ mPrevVorbisBlockSize = 0;
+ blockSize = 0;
+ }
+
+ long samples = (mPrevVorbisBlockSize == 0) ? 0 :
+ mPrevVorbisBlockSize / 4 + blockSize / 4;
+ PRInt64 start = first->granulepos - samples;
+ RecordVorbisPacketSamples(first, samples);
+
+ if (last->e_o_s && start < mGranulepos) {
+ // We've calculated that there are more samples in this page than its
+ // granulepos claims, and it's the last page in the stream. This is legal,
+ // and we will need to prune the trailing samples when we come to decode it.
+ // We must correct the timestamps so that they follow the last Vorbis page's
+ // samples.
+ PRInt64 pruned = mGranulepos - start;
+ for (PRUint32 i = 0; i < mUnstamped.Length() - 1; i++) {
+ mUnstamped[i]->granulepos += pruned;
+ }
+#ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
+ mVorbisPacketSamples[last] -= pruned;
+#endif
+ }
+
+ mPrevVorbisBlockSize = vorbis_packet_blocksize(&mInfo, last);
+ mPrevVorbisBlockSize = NS_MAX(static_cast(0), mPrevVorbisBlockSize);
+ mGranulepos = last->granulepos;
+
+ return NS_OK;
+}
+
+
nsSkeletonState::nsSkeletonState(ogg_page* aBosPage)
- : nsOggCodecState(aBosPage),
+ : nsOggCodecState(aBosPage, PR_TRUE),
mVersion(0),
mPresentationTime(0),
mLength(0)
diff --git a/content/media/ogg/nsOggCodecState.h b/content/media/ogg/nsOggCodecState.h
index 20121cec57da..a31bb3dda1ba 100644
--- a/content/media/ogg/nsOggCodecState.h
+++ b/content/media/ogg/nsOggCodecState.h
@@ -51,37 +51,50 @@
#include
#include "VideoUtils.h"
-class OggPageDeallocator : public nsDequeFunctor {
- virtual void* operator() (void* aPage) {
- ogg_page* p = static_cast(aPage);
- delete [] p->header;
+// Uncomment the following to validate that we're predicting the number
+// of Vorbis samples in each packet correctly.
+#define VALIDATE_VORBIS_SAMPLE_CALCULATION
+#ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
+#include