Final merge and landing of bug 568691 and dependent bugs to mozilla-central on a CLOSED TREE.

* bug 568691, register XPCOM components statically using manifests. r=mossop, with some test and build stuff r=vlad,sdwilsh
* bug 573557, rename categories with spaces to use hypens. r=jst
* bug 573739, Don't get the private browsing service during layout module initialization, r=ehsan

This initial merge does not include some work planned to land imminently:
* The extension manager still restarts. I need to solve a problem re-reading default preferences from extensions.
* MOZ_OMNIJAR is broken: there is a patch which I need to update in bug 568691.
* I will concurrently land a mobile-browser fix for component registration, but it may require some additional packaging changes.
This commit is contained in:
Benjamin Smedberg 2010-07-01 14:26:25 -04:00
Родитель 9155836705 684ee6b525
Коммит a08857c778
48 изменённых файлов: 1400 добавлений и 516 удалений

Просмотреть файл

@ -135,7 +135,6 @@
margin-top: -1px; /* overlay the bottom border of the toolbar above us */
background-color: -moz-mac-chrome-active;
border-bottom: 1px solid rgba(0, 0, 0, 0.57);
min-height: 22px;
}
#PersonalToolbar:-moz-window-inactive {
@ -144,7 +143,7 @@
}
#personal-bookmarks {
-moz-box-align: center;
min-height: 17px; /* 16px button height + 1px margin-bottom */
}
toolbarbutton.chevron {
@ -785,7 +784,7 @@ toolbar[iconsize="small"][mode="icons"] #forward-button:-moz-locale-dir(rtl) {
}
#urlbar-display {
margin-top: -2px;
margin-top: -3px;
margin-bottom: -2px;
padding-top: 3px;
padding-bottom: 2px;

Просмотреть файл

@ -58,8 +58,8 @@ public:
: public NS_CYCLE_COLLECTION_CLASSNAME(nsDOMEventTargetHelper)
{
NS_IMETHOD RootAndUnlinkJSObjects(void *p);
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY(nsDOMEventTargetWrapperCache,
nsDOMEventTargetHelper)
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY_NO_UNLINK(nsDOMEventTargetWrapperCache,
nsDOMEventTargetHelper)
NS_IMETHOD_(void) Trace(void *p, TraceCallback cb, void *closure);
};
NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE

Просмотреть файл

@ -62,10 +62,6 @@ NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsDOMEventTargetWrapperCache)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_ROOT_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMEventTargetWrapperCache,
nsDOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMEventTargetWrapperCache)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)

Просмотреть файл

@ -180,10 +180,23 @@ nsImageLoadingContent::OnStartRequest(imgIRequest* aRequest)
NS_IMETHODIMP
nsImageLoadingContent::OnStartDecode(imgIRequest* aRequest)
{
// Block onload if it's the current request
nsresult rv;
// Onload blocking. This only applies for the current request.
if (aRequest == mCurrentRequest) {
NS_ABORT_IF_FALSE(!mBlockingOnload, "Shouldn't already be blocking");
SetBlockingOnload(PR_TRUE);
// Determine whether this is a background request (this can be the case
// with multipart/x-mixed-replace images, for example).
PRUint32 loadFlags;
rv = aRequest->GetLoadFlags(&loadFlags);
PRBool background =
(NS_SUCCEEDED(rv) && (loadFlags & nsIRequest::LOAD_BACKGROUND));
// Block onload for non-background requests
if (!background) {
NS_ABORT_IF_FALSE(!mBlockingOnload, "Shouldn't already be blocking");
SetBlockingOnload(PR_TRUE);
}
}
LOOP_OVER_OBSERVERS(OnStartDecode(aRequest));

Просмотреть файл

@ -1975,9 +1975,8 @@ IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(Close)
nsRefPtr<nsWebSocketEstablishedConnection> kungfuDeathGrip = this;
if (mOwner->mReadyState == nsIWebSocket::CONNECTING) {
// we must not convey any failure information to scripts, so we just
// disconnect and maintain the owner WebSocket object in the CONNECTING
// state.
mOwner->SetReadyState(nsIWebSocket::CLOSING);
mOwner->SetReadyState(nsIWebSocket::CLOSED);
Disconnect();
return;
}
@ -2035,13 +2034,6 @@ nsWebSocketEstablishedConnection::ForceClose()
// reference until the end of the method
nsRefPtr<nsWebSocketEstablishedConnection> kungfuDeathGrip = this;
if (mOwner->mReadyState == nsIWebSocket::CONNECTING) {
// we must not convey any failure information to scripts, so we just
// disconnect and maintain the owner WebSocket object in the CONNECTING
// state.
Disconnect();
return;
}
mOwner->SetReadyState(nsIWebSocket::CLOSING);
mOwner->SetReadyState(nsIWebSocket::CLOSED);
Disconnect();
@ -3444,12 +3436,6 @@ nsWebSocket::Close()
nsRefPtr<nsWebSocket> kungfuDeathGrip = this;
mConnection->FailConnection();
// We need to set the readyState here because mConnection would set it
// only if first connected. Also, let the two readyState changes here
// for future extensions (for instance an onreadystatechange event)
SetReadyState(nsIWebSocket::CLOSING);
SetReadyState(nsIWebSocket::CLOSED);
return NS_OK;
}

Просмотреть файл

@ -94,11 +94,10 @@ function CreateTestWS(ws_location, ws_protocol)
ok(false, "onerror called on test " + e.target._testNumber + "!");
};
ws._testNumber = current_test;
ws._receivedCloseEvent = false;
ws.addEventListener("close", function(e)
{
if (ws._receivedCloseEvent != undefined) {
ws._receivedCloseEvent = true;
}
ws._receivedCloseEvent = true;
}, false);
}
catch (e) {
@ -198,8 +197,11 @@ function test3()
{
var ws = CreateTestWS("ws://this.websocket.server.probably.does.not.exist");
ws.onopen = shouldNotOpen;
ws.onclose = shouldNotReceiveCloseEvent;
doTest(4);
ws.onclose = function(e)
{
shouldCloseNotCleanly(e);
doTest(4);
};
}
function test4()
@ -255,7 +257,6 @@ function test6()
}
}
ws.onclose = shouldCloseCleanly;
ws._receivedCloseEvent = false;
}
function test7()
@ -270,7 +271,6 @@ function test7()
shouldCloseNotCleanly(e);
doTest(8);
};
ws._receivedCloseEvent = false;
}
function test8()
@ -285,7 +285,6 @@ function test8()
shouldCloseCleanly(e);
doTest(9);
};
ws._receivedCloseEvent = false;
}
function test9()
@ -298,7 +297,6 @@ function test9()
doTest(10);
};
ws._receivedCloseEvent = false;
ws.close();
}
@ -306,7 +304,6 @@ function test10()
{
var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 10");
ws.onclose = shouldCloseCleanly;
ws._receivedCloseEvent = false;
try {
ws.send("client data");
@ -358,7 +355,6 @@ function test12()
ok(true, "couldn't send an unpaired surrogate!");
}
ws.close();
ws._receivedCloseEvent = false;
doTest(13);
};
}
@ -376,7 +372,6 @@ function test13()
}
}
ws.onclose = shouldCloseCleanly;
ws._receivedCloseEvent = false;
}
function test14()
@ -391,7 +386,6 @@ function test14()
shouldCloseCleanly(e);
doTest(15);
};
ws._receivedCloseEvent = false;
}
function test15()
@ -402,7 +396,6 @@ function test15()
shouldCloseNotCleanly(e);
doTest(16);
};
ws._receivedCloseEvent = false;
}
function test16()
@ -419,7 +412,6 @@ function test16()
ok(false, "shouldn't send message after calling close()");
}
ws.onclose = shouldCloseCleanly;
ws._receivedCloseEvent = false;
}
var status_test17 = "not started";
@ -427,6 +419,7 @@ var status_test17 = "not started";
window._test17 = function()
{
var local_ws = new WebSocket("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 17");
current_test++;
status_test17 = "started";
@ -477,21 +470,28 @@ function test18()
{
var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket_http_resource.txt");
ws.onopen = shouldNotOpen;
ws.onclose = shouldNotReceiveCloseEvent;
doTest(19);
ws.onclose = function(e)
{
shouldCloseNotCleanly(e);
doTest(19);
};
}
function test19()
{
var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 19");
ws.onopen = shouldNotOpen;
ws.onclose = shouldNotReceiveCloseEvent;
doTest(20);
ws.onclose = function(e)
{
shouldCloseNotCleanly(e);
doTest(20);
};
}
window._test20 = function()
{
var local_ws = new WebSocket("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 20");
current_test++;
local_ws.onerror = function()
{
@ -516,6 +516,7 @@ var timeoutTest21;
window._test21 = function()
{
var local_ws = new WebSocket("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 21");
current_test++;
local_ws.onopen = function(e)
{
@ -560,14 +561,18 @@ function test22()
{
var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", "test 22");
ws.onopen = shouldNotOpen;
ws.onclose = shouldNotReceiveCloseEvent;
doTest(23);
ws.onclose = function(e)
{
shouldCloseNotCleanly(e);
doTest(23);
};
}
function finishWSTest()
{
for (i = 0; i < all_ws.length; ++i) {
if (all_ws[i]._receivedCloseEvent === false) {
if (all_ws[i] != shouldNotReceiveCloseEvent &&
!all_ws[i]._receivedCloseEvent) {
ok(false, "didn't called close on test " + all_ws[i]._testNumber + "!");
}
}

Просмотреть файл

@ -49,6 +49,7 @@
#include "gfxContext.h"
#include "gfxPattern.h"
#include "gfxUtils.h"
#include "CanvasUtils.h"
#include "NativeJSContext.h"

Просмотреть файл

@ -1,9 +1,9 @@
<html>
<head>
<script>
window.addEventListener("focus", function() { window.close(); }, false);
function done() {
window.focus();
window.close();
}
</script>
</head>

Просмотреть файл

@ -100,6 +100,8 @@ CSRCS = \
# don't have to vacuum to make sure the data is not visible in the file.
# -DSQLITE_ENABLE_FTS3=1 enables the full-text index module.
# -DSQLITE_CORE=1 statically links that module into the SQLite library.
# -DSQLITE_DEFAULT_PAGE_SIZE=32768 and SQLITE_MAX_DEFAULT_PAGE_SIZE=32768
# increases the page size from 1k, see bug 416330.
# Note: Be sure to update the configure.in checks when these change!
DEFINES = \
-DSQLITE_SECURE_DELETE=1 \
@ -107,6 +109,8 @@ DEFINES = \
-DSQLITE_CORE=1 \
-DSQLITE_ENABLE_FTS3=1 \
-DSQLITE_ENABLE_UNLOCK_NOTIFY=1 \
-DSQLITE_DEFAULT_PAGE_SIZE=32768 \
-DSQLITE_MAX_DEFAULT_PAGE_SIZE=32768 \
$(NULL)
# -DSQLITE_ENABLE_LOCKING_STYLE=1 to help with AFP folders

Просмотреть файл

@ -384,16 +384,6 @@ protected:
LayerManagerOGL *mOGLManager;
};
#ifdef DEBUG
#define DEBUG_GL_ERROR_CHECK(cx) do { \
/*fprintf (stderr, "trace %s %d\n", __FILE__, __LINE__);*/ \
GLenum err = (cx)->fGetError(); \
if (err) { fprintf (stderr, "GL ERROR: 0x%04x at %s:%d\n", err, __FILE__, __LINE__); } \
} while (0)
#else
#define DEBUG_GL_ERROR_CHECK(cx) do { } while (0)
#endif
} /* layers */
} /* mozilla */

Просмотреть файл

@ -74,85 +74,43 @@ UseOpaqueSurface(Layer* aLayer)
ThebesLayerOGL::ThebesLayerOGL(LayerManagerOGL *aManager)
: ThebesLayer(aManager, NULL)
: ThebesLayer(aManager, nsnull)
, LayerOGL(aManager)
, mTexture(0)
, mOffscreenFormat(gfxASurface::ImageFormatUnknown)
, mOffscreenSize(-1,-1)
, mTexImage(nsnull)
{
mImplData = static_cast<LayerOGL*>(this);
}
ThebesLayerOGL::~ThebesLayerOGL()
{
mOGLManager->MakeCurrent();
if (mOffscreenSurfaceAsGLContext)
mOffscreenSurfaceAsGLContext->ReleaseTexImage();
if (mTexture) {
gl()->fDeleteTextures(1, &mTexture);
}
mTexImage = nsnull;
DEBUG_GL_ERROR_CHECK(gl());
}
PRBool
ThebesLayerOGL::EnsureSurface()
{
gfxASurface::gfxImageFormat imageFormat = gfxASurface::ImageFormatARGB32;
if (UseOpaqueSurface(this))
imageFormat = gfxASurface::ImageFormatRGB24;
if (mInvalidatedRect.IsEmpty())
return mOffScreenSurface ? PR_TRUE : PR_FALSE;
if ((mOffscreenSize == gfxIntSize(mInvalidatedRect.width, mInvalidatedRect.height)
&& imageFormat == mOffscreenFormat)
|| mInvalidatedRect.IsEmpty())
return mOffScreenSurface ? PR_TRUE : PR_FALSE;
mOffScreenSurface =
gfxPlatform::GetPlatform()->
CreateOffscreenSurface(gfxIntSize(mInvalidatedRect.width, mInvalidatedRect.height),
imageFormat);
if (!mOffScreenSurface)
return PR_FALSE;
if (mOffScreenSurface) {
mOffscreenSize.width = mInvalidatedRect.width;
mOffscreenSize.height = mInvalidatedRect.height;
mOffscreenFormat = imageFormat;
nsIntSize visibleSize = mVisibleRegion.GetBounds().Size();
TextureImage::ContentType contentType =
UseOpaqueSurface(this) ? gfxASurface::CONTENT_COLOR :
gfxASurface::CONTENT_COLOR_ALPHA;
if (!mTexImage ||
mTexImage->GetSize() != visibleSize ||
mTexImage->GetContentType() != contentType)
{
mValidRegion.SetEmpty();
mTexImage = nsnull;
DEBUG_GL_ERROR_CHECK(gl());
}
mOGLManager->MakeCurrent();
if (!mTexture)
gl()->fGenTextures(1, &mTexture);
gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
// Try bind our offscreen surface directly to texture
mOffscreenSurfaceAsGLContext = sGLContextProvider.CreateForNativePixmapSurface(mOffScreenSurface);
// Bind GL surface to the texture, and return
if (mOffscreenSurfaceAsGLContext)
return mOffscreenSurfaceAsGLContext->BindTexImage();
// Otherwise allocate new texture
gl()->fTexImage2D(LOCAL_GL_TEXTURE_2D,
0,
LOCAL_GL_RGBA,
mInvalidatedRect.width,
mInvalidatedRect.height,
0,
LOCAL_GL_RGBA,
LOCAL_GL_UNSIGNED_BYTE,
NULL);
DEBUG_GL_ERROR_CHECK(gl());
return PR_TRUE;
if (!mTexImage && !mVisibleRegion.IsEmpty())
{
mTexImage = gl()->CreateTextureImage(visibleSize,
contentType,
LOCAL_GL_CLAMP_TO_EDGE);
DEBUG_GL_ERROR_CHECK(gl());
}
return !!mTexImage;
}
void
@ -160,19 +118,15 @@ ThebesLayerOGL::SetVisibleRegion(const nsIntRegion &aRegion)
{
if (aRegion.IsEqual(mVisibleRegion))
return;
ThebesLayer::SetVisibleRegion(aRegion);
mInvalidatedRect = mVisibleRegion.GetBounds();
// FIXME/bug 573829: keep some of these pixels, if we can!
mValidRegion.SetEmpty();
}
void
ThebesLayerOGL::InvalidateRegion(const nsIntRegion &aRegion)
{
nsIntRegion invalidatedRegion;
invalidatedRegion.Or(aRegion, mInvalidatedRect);
invalidatedRegion.And(invalidatedRegion, mVisibleRegion);
mInvalidatedRect = invalidatedRegion.GetBounds();
mValidRegion.Sub(mValidRegion, aRegion);
}
LayerOGL::LayerType
@ -188,77 +142,44 @@ ThebesLayerOGL::RenderLayer(int aPreviousFrameBuffer,
if (!EnsureSurface())
return;
if (!mTexture)
return;
mOGLManager->MakeCurrent();
gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
bool needsTextureBind = true;
nsIntRect visibleRect = mVisibleRegion.GetBounds();
nsIntRegion rgnToPaint = mVisibleRegion;
rgnToPaint.Sub(rgnToPaint, mValidRegion);
PRBool textureBound = PR_FALSE;
if (!rgnToPaint.IsEmpty())
{
nsIntRect visibleRect = mVisibleRegion.GetBounds();
// translate repaint region to texture-buffer space
nsIntRegion bufRgnToPaint = rgnToPaint;
bufRgnToPaint.MoveBy(-visibleRect.x, -visibleRect.y);
nsRefPtr<gfxContext> ctx = mTexImage->BeginUpdate(bufRgnToPaint);
if (!ctx)
{
NS_WARNING("unable to get context for update");
return;
}
// and translate update context back to screen space
ctx->Translate(-gfxPoint(visibleRect.x, visibleRect.y));
nsRefPtr<gfxASurface> surface = mOffScreenSurface;
gfxASurface::gfxImageFormat imageFormat = mOffscreenFormat;
if (!mInvalidatedRect.IsEmpty()) {
nsRefPtr<gfxContext> ctx = new gfxContext(surface);
ctx->Translate(gfxPoint(-mInvalidatedRect.x, -mInvalidatedRect.y));
/* Call the thebes layer callback */
mOGLManager->CallThebesLayerDrawCallback(this, ctx, mInvalidatedRect);
}
// If draw callback happend and we don't have native surface
if (!mInvalidatedRect.IsEmpty() && !mOffscreenSurfaceAsGLContext) {
/* Then take its results and put it in an image surface,
* in preparation for a texture upload */
nsRefPtr<gfxImageSurface> imageSurface;
switch (surface->GetType()) {
case gfxASurface::SurfaceTypeImage:
imageSurface = static_cast<gfxImageSurface*>(surface.get());
break;
#ifdef XP_WIN
case gfxASurface::SurfaceTypeWin32:
imageSurface =
static_cast<gfxWindowsSurface*>(surface.get())->
GetImageSurface();
break;
#endif
default:
/**
* XXX - This is very undesirable. Implement this for other platforms in
* a more efficient way as well!
*/
{
imageSurface = new gfxImageSurface(gfxIntSize(mInvalidatedRect.width,
mInvalidatedRect.height),
imageFormat);
nsRefPtr<gfxContext> tmpContext = new gfxContext(imageSurface);
tmpContext->SetSource(surface);
tmpContext->SetOperator(gfxContext::OPERATOR_SOURCE);
tmpContext->Paint();
}
break;
TextureImage::ContentType contentType = mTexImage->GetContentType();
//ClipToRegion(ctx, rgnToDraw);
if (gfxASurface::CONTENT_COLOR_ALPHA == contentType)
{
ctx->SetOperator(gfxContext::OPERATOR_CLEAR);
ctx->Paint();
ctx->SetOperator(gfxContext::OPERATOR_OVER);
}
// Upload image to texture (slow)
gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
gl()->fTexSubImage2D(LOCAL_GL_TEXTURE_2D,
0,
mInvalidatedRect.x - visibleRect.x,
mInvalidatedRect.y - visibleRect.y,
mInvalidatedRect.width,
mInvalidatedRect.height,
LOCAL_GL_RGBA,
LOCAL_GL_UNSIGNED_BYTE,
imageSurface->Data());
mOGLManager->CallThebesLayerDrawCallback(this, ctx, rgnToPaint);
needsTextureBind = false;
textureBound = mTexImage->EndUpdate();
mValidRegion.Or(mValidRegion, rgnToPaint);
}
if (needsTextureBind)
gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
if (!textureBound)
gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexImage->Texture());
// Note BGR: Cairo's image surfaces are always in what
// OpenGL and our shaders consider BGR format.
@ -268,7 +189,7 @@ ThebesLayerOGL::RenderLayer(int aPreviousFrameBuffer,
: mOGLManager->GetBGRALayerProgram();
program->Activate();
program->SetLayerQuadRect(visibleRect);
program->SetLayerQuadRect(mVisibleRegion.GetBounds());
program->SetLayerOpacity(GetOpacity());
program->SetLayerTransform(mTransform);
program->SetRenderOffset(aOffset);
@ -279,12 +200,6 @@ ThebesLayerOGL::RenderLayer(int aPreviousFrameBuffer,
DEBUG_GL_ERROR_CHECK(gl());
}
const nsIntRect&
ThebesLayerOGL::GetInvalidatedRect()
{
return mInvalidatedRect;
}
Layer*
ThebesLayerOGL::GetLayer()
{
@ -294,7 +209,7 @@ ThebesLayerOGL::GetLayer()
PRBool
ThebesLayerOGL::IsEmpty()
{
return !mTexture;
return !mTexImage;
}
} /* layers */

Просмотреть файл

@ -50,6 +50,8 @@ namespace layers {
class ThebesLayerOGL : public ThebesLayer,
public LayerOGL
{
typedef gl::TextureImage TextureImage;
public:
typedef mozilla::gl::GLContext GLContext;
ThebesLayerOGL(LayerManagerOGL *aManager);
@ -68,25 +70,10 @@ public:
virtual void RenderLayer(int aPreviousFrameBuffer,
const nsIntPoint& aOffset);
/** ThebesLayerOGL */
nsIntRect GetVisibleRect() { return mVisibleRegion.GetBounds(); }
const nsIntRect &GetInvalidatedRect();
private:
PRBool EnsureSurface();
/**
* Currently invalidated rectangular area.
*/
nsIntRect mInvalidatedRect;
/**
* OpenGL Texture
*/
GLuint mTexture;
nsRefPtr<GLContext> mOffscreenSurfaceAsGLContext;
nsRefPtr<gfxASurface> mOffScreenSurface;
gfxASurface::gfxImageFormat mOffscreenFormat;
gfxIntSize mOffscreenSize;
nsRefPtr<TextureImage> mTexImage;
};
} /* layers */

Просмотреть файл

@ -44,6 +44,7 @@
#include "prlink.h"
#include "gfxImageSurface.h"
#include "GLContext.h"
#include "GLContextProvider.h"
@ -367,5 +368,110 @@ GLContext::IsExtensionSupported(const char *extension)
return PR_FALSE;
}
already_AddRefed<TextureImage>
GLContext::CreateTextureImage(const nsIntSize& aSize,
TextureImage::ContentType aContentType,
GLint aWrapMode,
PRBool aUseNearestFilter)
{
MakeCurrent();
GLuint texture;
fGenTextures(1, &texture);
fActiveTexture(LOCAL_GL_TEXTURE0);
fBindTexture(LOCAL_GL_TEXTURE_2D, texture);
GLint texfilter = aUseNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR;
fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter);
fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter);
fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, aWrapMode);
fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, aWrapMode);
DEBUG_GL_ERROR_CHECK(this);
return CreateBasicTextureImage(texture, aSize, aContentType, this);
}
BasicTextureImage::~BasicTextureImage()
{
mGLContext->MakeCurrent();
mGLContext->fDeleteTextures(1, &mTexture);
}
gfxContext*
BasicTextureImage::BeginUpdate(nsIntRegion& aRegion)
{
NS_ASSERTION(!mUpdateContext, "BeginUpdate() without EndUpdate()?");
// determine the region the client will need to repaint
if (!mTextureInited)
// if the texture hasn't been initialized yet, force the
// client to paint everything
mUpdateRect = nsIntRect(nsIntPoint(0, 0), mSize);
else
mUpdateRect = aRegion.GetBounds();
// the basic impl can't upload updates to disparate regions,
// only rects
aRegion = nsIntRegion(mUpdateRect);
nsIntSize rgnSize = mUpdateRect.Size();
if (!nsIntRect(nsIntPoint(0, 0), mSize).Contains(mUpdateRect))
{
NS_ERROR("update outside of image");
return NULL;
}
ImageFormat format =
(GetContentType() == gfxASurface::CONTENT_COLOR) ?
gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32;
nsRefPtr<gfxASurface> updateSurface =
CreateUpdateSurface(gfxIntSize(rgnSize.width, rgnSize.height),
format);
if (!updateSurface)
return NULL;
mUpdateContext = new gfxContext(updateSurface);
return mUpdateContext;
}
PRBool
BasicTextureImage::EndUpdate()
{
NS_ASSERTION(!!mUpdateContext, "EndUpdate() without BeginUpdate()?");
// FIXME: this is the slow boat. Make me fast (with GLXPixmap?).
nsRefPtr<gfxImageSurface> uploadImage =
GetImageForUpload(mUpdateContext->OriginalSurface());
if (!uploadImage)
return PR_FALSE;
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
if (!mTextureInited)
{
mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D,
0,
LOCAL_GL_RGBA,
mUpdateRect.width,
mUpdateRect.height,
0,
LOCAL_GL_RGBA,
LOCAL_GL_UNSIGNED_BYTE,
uploadImage->Data());
mTextureInited = PR_TRUE;
} else {
mGLContext->fTexSubImage2D(LOCAL_GL_TEXTURE_2D,
0,
mUpdateRect.x,
mUpdateRect.y,
mUpdateRect.width,
mUpdateRect.height,
LOCAL_GL_RGBA,
LOCAL_GL_UNSIGNED_BYTE,
uploadImage->Data());
}
mUpdateContext = NULL;
return PR_TRUE; // mTexture is bound
}
} /* namespace gl */
} /* namespace mozilla */

Просмотреть файл

@ -41,17 +41,22 @@
#ifndef GLCONTEXT_H_
#define GLCONTEXT_H_
#include <stdio.h>
#ifdef WIN32
#include <windows.h>
#endif
#include "GLDefs.h"
#include "gfxASurface.h"
#include "gfxContext.h"
#include "gfxRect.h"
#include "nsISupportsImpl.h"
#include "prlink.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsRegion.h"
#ifndef GLAPIENTRY
#ifdef XP_WIN
@ -71,6 +76,8 @@ typedef char realGLboolean;
namespace mozilla {
namespace gl {
class GLContext;
class LibrarySymbolLoader
{
public:
@ -113,6 +120,135 @@ protected:
};
/**
* A TextureImage encapsulates a surface that can be drawn to by a
* Thebes gfxContext and (hopefully efficiently!) synchronized to a
* texture in the server. TextureImages are associated with one and
* only one GLContext.
*
* Implementation note: TextureImages attempt to unify two categories
* of backends
*
* (1) proxy to server-side object that can be bound to a texture;
* e.g. Pixmap on X11.
*
* (2) efficient manager of texture memory; e.g. by having clients draw
* into a scratch buffer which is then uploaded with
* glTexSubImage2D().
*/
class TextureImage
{
NS_INLINE_DECL_REFCOUNTING(TextureImage)
public:
typedef gfxASurface::gfxContentType ContentType;
virtual ~TextureImage() {}
/**
* Return a gfxContext for updating |aRegion| of the client's
* image if successul, NULL if not. |aRegion|'s bounds must fit
* within Size(); its coordinate space (if any) is ignored. If
* the update begins successfully, the returned gfxContext is
* owned by this. Otherwise, NULL is returned.
*
* |aRegion| is an inout param: the returned region is what the
* client must repaint. Category (1) regions above can
* efficiently handle repaints to "scattered" regions, while (2)
* can only efficiently handle repaints to rects.
*
* The returned context is neither translated nor clipped: it's a
* context for rect(<0,0>, Size()). Painting the returned context
* outside of |aRegion| results in undefined behavior.
*
* BeginUpdate() calls cannot be "nested", and each successful
* BeginUpdate() must be followed by exactly one EndUpdate() (see
* below). Failure to do so can leave this in a possibly
* inconsistent state. Unsuccessful BeginUpdate()s must not be
* followed by EndUpdate().
*/
virtual gfxContext* BeginUpdate(nsIntRegion& aRegion) = 0;
/**
* Finish the active update and synchronize with the server, if
* necessary. Return PR_TRUE iff this's texture is already bound.
*
* BeginUpdate() must have been called exactly once before
* EndUpdate().
*/
virtual PRBool EndUpdate() = 0;
/**
* Return this TextureImage's texture ID for use with GL APIs.
* Callers are responsible for properly binding the texture etc.
*
* The effects of using a texture after BeginUpdate() but before
* EndUpdate() are undefined.
*/
GLuint Texture() { return mTexture; }
/** Can be called safely at any time. */
const nsIntSize& GetSize() const { return mSize; }
ContentType GetContentType() const { return mContentType; }
protected:
/**
* After the ctor, the TextureImage is invalid. Implementations
* must allocate resources successfully before returning the new
* TextureImage from GLContext::CreateTextureImage(). That is,
* clients must not be given partially-constructed TextureImages.
*/
TextureImage(GLuint aTexture, const nsIntSize& aSize, ContentType aContentType)
: mTexture(aTexture)
, mSize(aSize)
, mContentType(aContentType)
{}
GLuint mTexture;
nsIntSize mSize;
ContentType mContentType;
};
/**
* BasicTextureImage is the baseline TextureImage implementation ---
* it updates its texture by allocating a scratch buffer for the
* client to draw into, then using glTexSubImage2D() to upload the new
* pixels. Platforms must provide the code to create a new surface
* into which the updated pixels will be drawn, and the code to
* convert the update surface's pixels into an image on which we can
* glTexSubImage2D().
*/
class BasicTextureImage
: public TextureImage
{
public:
virtual ~BasicTextureImage();
virtual gfxContext* BeginUpdate(nsIntRegion& aRegion);
virtual PRBool EndUpdate();
protected:
typedef gfxASurface::gfxImageFormat ImageFormat;
BasicTextureImage(GLuint aTexture,
const nsIntSize& aSize,
ContentType aContentType,
GLContext* aContext)
: TextureImage(aTexture, aSize, aContentType)
, mTextureInited(PR_FALSE)
, mGLContext(aContext)
{}
virtual already_AddRefed<gfxASurface>
CreateUpdateSurface(const gfxIntSize& aSize, ImageFormat aFmt) = 0;
virtual already_AddRefed<gfxImageSurface>
GetImageForUpload(gfxASurface* aUpdateSurface) = 0;
PRBool mTextureInited;
GLContext* mGLContext;
nsRefPtr<gfxContext> mUpdateContext;
nsIntRect mUpdateRect;
};
class GLContext
: public LibrarySymbolLoader
{
@ -189,6 +325,26 @@ public:
MakeCurrent();
fDeleteTextures(1, &tex);
}
/**
* Return a valid, allocated TextureImage of |aSize| with
* |aContentType|. The TextureImage's texture is configured to
* use |aWrapMode| (usually GL_CLAMP_TO_EDGE or GL_REPEAT) and by
* default, GL_LINEAR filtering. Specify
* |aUseNearestFilter=PR_TRUE| for GL_NEAREST filtering. Return
* NULL if creating the TextureImage fails.
*
* The returned TextureImage may only be used with this GLContext.
* Attempting to use the returned TextureImage after this
* GLContext is destroyed will result in undefined (and likely
* crashy) behavior.
*/
virtual already_AddRefed<TextureImage>
CreateTextureImage(const nsIntSize& aSize,
TextureImage::ContentType aContentType,
GLint aWrapMode,
PRBool aUseNearestFilter=PR_FALSE);
protected:
PRBool mInitialized;
@ -198,6 +354,13 @@ protected:
PRBool IsExtensionSupported(const char *extension);
virtual already_AddRefed<TextureImage>
CreateBasicTextureImage(GLuint aTexture,
const nsIntSize& aSize,
TextureImage::ContentType aContentType,
GLContext* aContext)
{ return NULL; }
//
// the wrapped functions
//
@ -512,6 +675,21 @@ public:
};
inline void
GLDebugPrintError(GLContext* aCx, const char* const aFile, int aLine)
{
GLenum err = aCx->fGetError();
if (err) {
fprintf(stderr, "GL ERROR: 0x%04x at %s:%d\n", err, aFile, aLine);
}
}
#ifdef DEBUG
# define DEBUG_GL_ERROR_CHECK(cx) mozilla::gl::GLDebugPrintError(cx, __FILE__, __LINE__)
#else
# define DEBUG_GL_ERROR_CHECK(cx) do { } while (0)
#endif
} /* namespace gl */
} /* namespace mozilla */

Просмотреть файл

@ -41,6 +41,8 @@
#include <OpenGL/gl.h>
#include <AppKit/NSOpenGL.h>
#include "gfxASurface.h"
#include "gfxImageSurface.h"
#include "gfxPlatform.h"
namespace mozilla {
namespace gl {
@ -137,12 +139,73 @@ public:
return PR_FALSE;
}
virtual already_AddRefed<TextureImage>
CreateBasicTextureImage(GLuint aTexture,
const nsIntSize& aSize,
TextureImage::ContentType aContentType,
GLContext* aContext);
private:
NSOpenGLContext *mContext;
CGLContextObj mCGLContext;
CGLPBufferObj mPBuffer;
};
class TextureImageCGL : public BasicTextureImage
{
friend already_AddRefed<TextureImage>
GLContextCGL::CreateBasicTextureImage(GLuint,
const nsIntSize&,
TextureImage::ContentType,
GLContext*);
protected:
virtual already_AddRefed<gfxASurface>
CreateUpdateSurface(const gfxIntSize& aSize, ImageFormat aFmt)
{
mUpdateFormat = aFmt;
return gfxPlatform::GetPlatform()->CreateOffscreenSurface(aSize, aFmt);
}
virtual already_AddRefed<gfxImageSurface>
GetImageForUpload(gfxASurface* aUpdateSurface)
{
// FIXME/bug 575521: make me fast!
nsRefPtr<gfxImageSurface> image =
new gfxImageSurface(gfxIntSize(mUpdateRect.width,
mUpdateRect.height),
mUpdateFormat);
nsRefPtr<gfxContext> tmpContext = new gfxContext(image);
tmpContext->SetSource(aUpdateSurface);
tmpContext->SetOperator(gfxContext::OPERATOR_SOURCE);
tmpContext->Paint();
return image.forget();
}
private:
TextureImageCGL(GLuint aTexture,
const nsIntSize& aSize,
ContentType aContentType,
GLContext* aContext)
: BasicTextureImage(aTexture, aSize, aContentType, aContext)
{}
ImageFormat mUpdateFormat;
};
already_AddRefed<TextureImage>
GLContextCGL::CreateBasicTextureImage(GLuint aTexture,
const nsIntSize& aSize,
TextureImage::ContentType aContentType,
GLContext* aContext)
{
nsRefPtr<TextureImageCGL> teximage(
new TextureImageCGL(aTexture, aSize, aContentType, aContext));
return teximage.forget();
}
already_AddRefed<GLContext>
GLContextProvider::CreateForWindow(nsIWidget *aWidget)
{

Просмотреть файл

@ -85,6 +85,7 @@ typedef void *EGLNativeWindowType;
#endif
#include "gfxASurface.h"
#include "gfxPlatform.h"
#include "GLContextProvider.h"
#include "nsDebug.h"
@ -244,6 +245,8 @@ private:
class GLContextEGL : public GLContext
{
friend class TextureImageEGL;
public:
GLContextEGL(EGLDisplay aDisplay, EGLConfig aConfig,
EGLSurface aSurface, EGLContext aContext,
@ -361,6 +364,12 @@ public:
return sEGLLibrary.fSwapBuffers(mDisplay, mSurface);
}
virtual already_AddRefed<TextureImage>
CreateTextureImage(const nsIntSize& aSize,
TextureImage::ContentType aContentType,
GLint aWrapMode,
PRBool aUseNearestFilter=PR_FALSE);
private:
EGLDisplay mDisplay;
EGLConfig mConfig;
@ -371,6 +380,101 @@ private:
PRBool mBound;
};
class TextureImageEGL : public TextureImage
{
public:
TextureImageEGL(GLuint aTexture,
const nsIntSize& aSize,
ContentType aContentType,
GLContext* aContext,
GLContextEGL* aImpl)
: TextureImage(aTexture, aSize, aContentType)
, mGLContext(aContext)
, mImpl(aImpl)
{ }
virtual ~TextureImageEGL()
{
mGLContext->MakeCurrent();
mImpl->ReleaseTexImage();
mGLContext->fDeleteTextures(1, &mTexture);
mImpl = NULL;
}
virtual gfxContext* BeginUpdate(nsIntRegion& aRegion)
{
NS_ASSERTION(!mUpdateContext, "BeginUpdate() without EndUpdate()?");
mUpdateContext = new gfxContext(mImpl->mASurface);
// TextureImageEGL can handle updates to disparate regions
// aRegion = aRegion;
return mUpdateContext;
}
virtual PRBool EndUpdate()
{
NS_ASSERTION(mUpdateContext, "EndUpdate() without BeginUpdate()?");
#ifdef MOZ_X11
// FIXME: do we need an XSync() or XFlush() here?
//XSync(False);
#endif // MOZ_X11
// X has already uploaded the new pixels to our Pixmap, so
// there's nothing else we need to do here
mUpdateContext = NULL;
return PR_FALSE; // texture not bound
}
private:
GLContext* mGLContext;
nsRefPtr<GLContextEGL> mImpl;
nsRefPtr<gfxContext> mUpdateContext;
};
already_AddRefed<TextureImage>
GLContextEGL::CreateTextureImage(const nsIntSize& aSize,
TextureImage::ContentType aContentType,
GLint aWrapMode,
PRBool aUseNearestFilter)
{
gfxASurface::gfxImageFormat imageFormat =
(gfxASurface::CONTENT_COLOR == aContentType) ?
gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32;
nsRefPtr<gfxASurface> pixmap =
gfxPlatform::GetPlatform()->
CreateOffscreenSurface(gfxIntSize(aSize.width, aSize.height),
imageFormat);
nsRefPtr<GLContext> impl =
sGLContextProvider.CreateForNativePixmapSurface(pixmap);
if (!impl)
// FIXME: should fall back on BasicTextureImage here
return NULL;
MakeCurrent();
GLuint texture;
fGenTextures(1, &texture);
fActiveTexture(LOCAL_GL_TEXTURE0);
fBindTexture(LOCAL_GL_TEXTURE_2D, texture);
GLint texfilter = aUseNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR;
fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter);
fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter);
fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, aWrapMode);
fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, aWrapMode);
impl->BindTexImage();
nsRefPtr<TextureImageEGL> teximage =
new TextureImageEGL(texture, aSize, aContentType, this,
static_cast<GLContextEGL*>(impl.get()));
return teximage.forget();
}
already_AddRefed<GLContext>
GLContextProvider::CreateForWindow(nsIWidget *aWidget)
{

Просмотреть файл

@ -54,6 +54,9 @@
#include "nsIWidget.h"
#include "GLXLibrary.h"
#include "gfxASurface.h"
#include "gfxContext.h"
#include "gfxImageSurface.h"
#include "gfxPlatform.h"
namespace mozilla {
namespace gl {
@ -241,6 +244,12 @@ public:
}
}
virtual already_AddRefed<TextureImage>
CreateBasicTextureImage(GLuint aTexture,
const nsIntSize& aSize,
TextureImage::ContentType aContentType,
GLContext* aContext);
private:
GLContextGLX(Display *aDisplay, GLXDrawable aWindow, GLXContext aContext, PRBool aPBuffer = PR_FALSE, PRBool aDoubleBuffered=PR_FALSE)
: mContext(aContext),
@ -257,6 +266,63 @@ private:
nsTArray<GLuint> textures;
};
// FIXME/bug 575505: this is a (very slow!) placeholder
// implementation. Much better would be to create a Pixmap, wrap that
// in a GLXPixmap, and then glXBindTexImage() to our texture.
class TextureImageGLX : public BasicTextureImage
{
friend already_AddRefed<TextureImage>
GLContextGLX::CreateBasicTextureImage(GLuint,
const nsIntSize&,
TextureImage::ContentType,
GLContext*);
protected:
virtual already_AddRefed<gfxASurface>
CreateUpdateSurface(const gfxIntSize& aSize, ImageFormat aFmt)
{
mUpdateFormat = aFmt;
return gfxPlatform::GetPlatform()->CreateOffscreenSurface(aSize, aFmt);
}
virtual already_AddRefed<gfxImageSurface>
GetImageForUpload(gfxASurface* aUpdateSurface)
{
nsRefPtr<gfxImageSurface> image =
new gfxImageSurface(gfxIntSize(mUpdateRect.width,
mUpdateRect.height),
mUpdateFormat);
nsRefPtr<gfxContext> tmpContext = new gfxContext(image);
tmpContext->SetSource(aUpdateSurface);
tmpContext->SetOperator(gfxContext::OPERATOR_SOURCE);
tmpContext->Paint();
return image.forget();
}
private:
TextureImageGLX(GLuint aTexture,
const nsIntSize& aSize,
ContentType aContentType,
GLContext* aContext)
: BasicTextureImage(aTexture, aSize, aContentType, aContext)
{}
ImageFormat mUpdateFormat;
};
already_AddRefed<TextureImage>
GLContextGLX::CreateBasicTextureImage(GLuint aTexture,
const nsIntSize& aSize,
TextureImage::ContentType aContentType,
GLContext* aContext)
{
nsRefPtr<TextureImageGLX> teximage(
new TextureImageGLX(aTexture, aSize, aContentType, aContext));
return teximage.forget();
}
static PRBool AreCompatibleVisuals(XVisualInfo *one, XVisualInfo *two)
{
if (one->c_class != two->c_class) {

Просмотреть файл

@ -40,6 +40,9 @@
#include "nsIWidget.h"
#include "WGLLibrary.h"
#include "gfxASurface.h"
#include "gfxImageSurface.h"
#include "gfxPlatform.h"
#include "gfxWindowsSurface.h"
namespace mozilla {
namespace gl {
@ -284,6 +287,12 @@ public:
return PR_TRUE;
}
virtual already_AddRefed<TextureImage>
CreateBasicTextureImage(GLuint aTexture,
const nsIntSize& aSize,
TextureImage::ContentType aContentType,
GLContext* aContext);
private:
HGLRC mContext;
HDC mDC;
@ -291,6 +300,52 @@ private:
int mPixelFormat;
};
class TextureImageWGL : public BasicTextureImage
{
friend already_AddRefed<TextureImage>
GLContextWGL::CreateBasicTextureImage(GLuint,
const nsIntSize&,
TextureImage::ContentType,
GLContext*);
protected:
virtual already_AddRefed<gfxASurface>
CreateUpdateSurface(const gfxIntSize& aSize, ImageFormat aFmt)
{
return gfxPlatform::GetPlatform()->CreateOffscreenSurface(aSize, aFmt);
}
virtual already_AddRefed<gfxImageSurface>
GetImageForUpload(gfxASurface* aUpdateSurface)
{
NS_ASSERTION(gfxASurface::SurfaceTypeWin32 == aUpdateSurface->GetType(),
"unexpected surface type");
nsRefPtr<gfxImageSurface> uploadImage(
static_cast<gfxWindowsSurface*>(aUpdateSurface)->
GetImageSurface());
return uploadImage.forget();
}
private:
TextureImageWGL(GLuint aTexture,
const nsIntSize& aSize,
ContentType aContentType,
GLContext* aContext)
: BasicTextureImage(aTexture, aSize, aContentType, aContext)
{}
};
already_AddRefed<TextureImage>
GLContextWGL::CreateBasicTextureImage(GLuint aTexture,
const nsIntSize& aSize,
TextureImage::ContentType aContentType,
GLContext* aContext)
{
nsRefPtr<TextureImageWGL> teximage(
new TextureImageWGL(aTexture, aSize, aContentType, aContext));
return teximage.forget();
}
already_AddRefed<GLContext>
GLContextProvider::CreateForWindow(nsIWidget *aWidget)
{

Просмотреть файл

@ -41,7 +41,6 @@
#include "gfxPoint.h"
#include "gfxTypes.h"
#include "gfxRect.h"
#include "gfxUtils.h"
#include "nsMathUtils.h"
// XX - I don't think this class should use gfxFloat at all,
@ -189,8 +188,8 @@ public:
*/
PRBool HasNonIntegerTranslation() const {
return HasNonTranslation() ||
!gfxUtils::FuzzyEqual(x0, NS_floor(x0 + 0.5)) ||
!gfxUtils::FuzzyEqual(y0, NS_floor(y0 + 0.5));
!FuzzyEqual(x0, NS_floor(x0 + 0.5)) ||
!FuzzyEqual(y0, NS_floor(y0 + 0.5));
}
/**
@ -198,8 +197,8 @@ public:
* than a straight translation
*/
PRBool HasNonTranslation() const {
return !gfxUtils::FuzzyEqual(xx, 1.0) || !gfxUtils::FuzzyEqual(yy, 1.0) ||
!gfxUtils::FuzzyEqual(xy, 0.0) || !gfxUtils::FuzzyEqual(yx, 0.0);
return !FuzzyEqual(xx, 1.0) || !FuzzyEqual(yy, 1.0) ||
!FuzzyEqual(xy, 0.0) || !FuzzyEqual(yx, 0.0);
}
/**
@ -207,9 +206,9 @@ public:
* than a translation or a -1 y scale (y axis flip)
*/
PRBool HasNonTranslationOrFlip() const {
return !gfxUtils::FuzzyEqual(xx, 1.0) ||
(!gfxUtils::FuzzyEqual(yy, 1.0) && !gfxUtils::FuzzyEqual(yy, -1.0)) ||
!gfxUtils::FuzzyEqual(xy, 0.0) || !gfxUtils::FuzzyEqual(yx, 0.0);
return !FuzzyEqual(xx, 1.0) ||
(!FuzzyEqual(yy, 1.0) && !FuzzyEqual(yy, -1.0)) ||
!FuzzyEqual(xy, 0.0) || !FuzzyEqual(yx, 0.0);
}
/**
@ -218,7 +217,7 @@ public:
* no rotation.
*/
PRBool HasNonAxisAlignedTransform() const {
return !gfxUtils::FuzzyEqual(xy, 0.0) || !gfxUtils::FuzzyEqual(yx, 0.0);
return !FuzzyEqual(xy, 0.0) || !FuzzyEqual(yx, 0.0);
}
/**
@ -258,6 +257,11 @@ public:
return gfxSize(minor, major);
}
private:
static PRBool FuzzyEqual(gfxFloat aV1, gfxFloat aV2) {
return fabs(aV2 - aV1) < 1e-6;
}
};
#endif /* GFX_MATRIX_H */

Просмотреть файл

@ -44,10 +44,6 @@ class gfxImageSurface;
class THEBES_API gfxUtils {
public:
static PRBool FuzzyEqual(gfxFloat aV1, gfxFloat aV2) {
return fabs(aV2 - aV1) < 1e-6;
}
/*
* Premultiply or Unpremultiply aSourceSurface, writing the result
* to aDestSurface or back into aSourceSurface if aDestSurface is null.

Просмотреть файл

@ -42,6 +42,7 @@
#include "gfxWindowsNativeDrawing.h"
#include "gfxWindowsSurface.h"
#include "gfxAlphaRecovery.h"
#include "gfxPattern.h"
enum {
RENDER_STATE_INIT,

Просмотреть файл

@ -68,7 +68,7 @@ include $(topsrcdir)/config/rules.mk
EXTRA_LDOPS += $(TK_LIBS)
LIBS = $(HELPER_OBJS) \
$(call EXPAND_LIBNAME_PATH,thebes,../src) \
$(call EXPAND_LIBNAME_PATH,thebes,..) \
$(MOZ_UNICHARUTIL_LIBS) \
$(LIBS_DIR) \
$(XPCOM_LIBS) \

Просмотреть файл

@ -2921,47 +2921,79 @@ MapToFloatUserPixels(const gfxSize& aSize,
aPt.y*aDest.size.height/aSize.height + aDest.pos.y);
}
static nsresult
DrawImageInternal(nsIRenderingContext* aRenderingContext,
imgIContainer* aImage,
gfxPattern::GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
const nsIntSize& aImageSize,
PRUint32 aImageFlags)
// helper function to convert a nsRect to a gfxRect
// borrowed from nsCSSRendering.cpp
static gfxRect
RectToGfxRect(const nsRect& aRect, PRInt32 aAppUnitsPerDevPixel)
{
return gfxRect(gfxFloat(aRect.x) / aAppUnitsPerDevPixel,
gfxFloat(aRect.y) / aAppUnitsPerDevPixel,
gfxFloat(aRect.width) / aAppUnitsPerDevPixel,
gfxFloat(aRect.height) / aAppUnitsPerDevPixel);
}
struct SnappedImageDrawingParameters {
// A transform from either device space or user space (depending on mResetCTM)
// to image space
gfxMatrix mUserSpaceToImageSpace;
// A device-space, pixel-aligned rectangle to fill
gfxRect mFillRect;
// A pixel rectangle in tiled image space outside of which gfx should not
// sample (using EXTEND_PAD as necessary)
nsIntRect mSubimage;
// Whether there's anything to draw at all
PRPackedBool mShouldDraw;
// PR_TRUE iff the CTM of the rendering context needs to be reset to the
// identity matrix before drawing
PRPackedBool mResetCTM;
SnappedImageDrawingParameters()
: mShouldDraw(PR_FALSE)
, mResetCTM(PR_FALSE)
{}
SnappedImageDrawingParameters(const gfxMatrix& aUserSpaceToImageSpace,
const gfxRect& aFillRect,
const nsIntRect& aSubimage,
PRBool aResetCTM)
: mUserSpaceToImageSpace(aUserSpaceToImageSpace)
, mFillRect(aFillRect)
, mSubimage(aSubimage)
, mShouldDraw(PR_TRUE)
, mResetCTM(aResetCTM)
{}
};
/**
* Given a set of input parameters, compute certain output parameters
* for drawing an image with the image snapping algorithm.
* See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering
*
* @see nsLayoutUtils::DrawImage() for the descriptions of input parameters
*/
static SnappedImageDrawingParameters
ComputeSnappedImageDrawingParameters(gfxContext* aCtx,
PRInt32 aAppUnitsPerDevPixel,
const nsRect aDest,
const nsRect aFill,
const nsPoint aAnchor,
const nsRect aDirty,
const nsIntSize aImageSize)
{
if (aDest.IsEmpty() || aFill.IsEmpty())
return NS_OK;
return SnappedImageDrawingParameters();
nsCOMPtr<nsIDeviceContext> dc;
aRenderingContext->GetDeviceContext(*getter_AddRefs(dc));
gfxFloat appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
gfxContext *ctx = aRenderingContext->ThebesContext();
gfxRect devPixelDest = RectToGfxRect(aDest, aAppUnitsPerDevPixel);
gfxRect devPixelFill = RectToGfxRect(aFill, aAppUnitsPerDevPixel);
gfxRect devPixelDirty = RectToGfxRect(aDirty, aAppUnitsPerDevPixel);
gfxRect devPixelDest(aDest.x/appUnitsPerDevPixel,
aDest.y/appUnitsPerDevPixel,
aDest.width/appUnitsPerDevPixel,
aDest.height/appUnitsPerDevPixel);
// Compute the pixel-snapped area that should be drawn
gfxRect devPixelFill(aFill.x/appUnitsPerDevPixel,
aFill.y/appUnitsPerDevPixel,
aFill.width/appUnitsPerDevPixel,
aFill.height/appUnitsPerDevPixel);
PRBool ignoreScale = PR_FALSE;
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
ignoreScale = PR_TRUE;
#endif
gfxRect fill = devPixelFill;
PRBool didSnap = ctx->UserToDevicePixelSnapped(fill, ignoreScale);
// Compute dirty rect in gfx space
gfxRect dirty(aDirty.x/appUnitsPerDevPixel,
aDirty.y/appUnitsPerDevPixel,
aDirty.width/appUnitsPerDevPixel,
aDirty.height/appUnitsPerDevPixel);
PRBool didSnap = aCtx->UserToDevicePixelSnapped(fill, ignoreScale);
gfxSize imageSize(aImageSize.width, aImageSize.height);
@ -2979,35 +3011,33 @@ DrawImageInternal(nsIRenderingContext* aRenderingContext,
// Compute the anchor point and compute final fill rect.
// This code assumes that pixel-based devices have one pixel per
// device unit!
gfxPoint anchorPoint(aAnchor.x/appUnitsPerDevPixel,
aAnchor.y/appUnitsPerDevPixel);
gfxPoint anchorPoint(gfxFloat(aAnchor.x)/aAppUnitsPerDevPixel,
gfxFloat(aAnchor.y)/aAppUnitsPerDevPixel);
gfxPoint imageSpaceAnchorPoint =
MapToFloatImagePixels(imageSize, devPixelDest, anchorPoint);
gfxContextMatrixAutoSaveRestore saveMatrix(ctx);
gfxMatrix currentMatrix = aCtx->CurrentMatrix();
if (didSnap) {
NS_ASSERTION(!saveMatrix.Matrix().HasNonAxisAlignedTransform(),
NS_ASSERTION(!currentMatrix.HasNonAxisAlignedTransform(),
"How did we snap, then?");
imageSpaceAnchorPoint.Round();
anchorPoint = imageSpaceAnchorPoint;
anchorPoint = MapToFloatUserPixels(imageSize, devPixelDest, anchorPoint);
anchorPoint = saveMatrix.Matrix().Transform(anchorPoint);
anchorPoint = currentMatrix.Transform(anchorPoint);
anchorPoint.Round();
// This form of Transform is safe to call since non-axis-aligned
// transforms wouldn't be snapped.
dirty = saveMatrix.Matrix().Transform(dirty);
ctx->IdentityMatrix();
devPixelDirty = currentMatrix.Transform(devPixelDirty);
}
gfxFloat scaleX = imageSize.width*appUnitsPerDevPixel/aDest.width;
gfxFloat scaleY = imageSize.height*appUnitsPerDevPixel/aDest.height;
gfxFloat scaleX = imageSize.width*aAppUnitsPerDevPixel/aDest.width;
gfxFloat scaleY = imageSize.height*aAppUnitsPerDevPixel/aDest.height;
if (didSnap) {
// ctx now has the identity matrix, so we need to adjust our
// scales to match
scaleX /= saveMatrix.Matrix().xx;
scaleY /= saveMatrix.Matrix().yy;
// We'll reset aCTX to the identity matrix before drawing, so we need to
// adjust our scales to match.
scaleX /= currentMatrix.xx;
scaleY /= currentMatrix.yy;
}
gfxFloat translateX = imageSpaceAnchorPoint.x - anchorPoint.x*scaleX;
gfxFloat translateY = imageSpaceAnchorPoint.y - anchorPoint.y*scaleY;
@ -3018,18 +3048,51 @@ DrawImageInternal(nsIRenderingContext* aRenderingContext,
// translation by integers, then filtering will occur, and
// restricting the fill rect to the dirty rect would change the values
// computed for edge pixels, which we can't allow.
// Also, if didSnap is false then rounding out 'dirty' might not
// Also, if didSnap is false then rounding out 'devPixelDirty' might not
// produce pixel-aligned coordinates, which would also break the values
// computed for edge pixels.
if (didSnap && !transform.HasNonIntegerTranslation()) {
dirty.RoundOut();
finalFillRect = fill.Intersect(dirty);
devPixelDirty.RoundOut();
finalFillRect = fill.Intersect(devPixelDirty);
}
if (finalFillRect.IsEmpty())
return SnappedImageDrawingParameters();
return SnappedImageDrawingParameters(transform, finalFillRect, intSubimage,
didSnap);
}
static nsresult
DrawImageInternal(nsIRenderingContext* aRenderingContext,
imgIContainer* aImage,
gfxPattern::GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
const nsIntSize& aImageSize,
PRUint32 aImageFlags)
{
nsCOMPtr<nsIDeviceContext> dc;
aRenderingContext->GetDeviceContext(*getter_AddRefs(dc));
PRInt32 appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
gfxContext* ctx = aRenderingContext->ThebesContext();
SnappedImageDrawingParameters drawingParams =
ComputeSnappedImageDrawingParameters(ctx, appUnitsPerDevPixel, aDest, aFill,
aAnchor, aDirty, aImageSize);
if (!drawingParams.mShouldDraw)
return NS_OK;
aImage->Draw(ctx, aGraphicsFilter, transform, finalFillRect, intSubimage,
aImageFlags);
gfxContextMatrixAutoSaveRestore saveMatrix(ctx);
if (drawingParams.mResetCTM) {
ctx->IdentityMatrix();
}
aImage->Draw(ctx, aGraphicsFilter, drawingParams.mUserSpaceToImageSpace,
drawingParams.mFillRect, drawingParams.mSubimage, aImageFlags);
return NS_OK;
}

Просмотреть файл

@ -225,6 +225,9 @@
#include "nsContentCID.h"
static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
/* for NS_MEMORY_REPORTER_IMPLEMENT */
#include "nsIMemoryReporter.h"
using namespace mozilla::layers;
using namespace mozilla::dom;

Просмотреть файл

@ -0,0 +1,16 @@
<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-print">
<style>
tbody::first-letter {float: right; }
tbody::before { content:"before textbefore textbefore textbefore textbefore textbefore text"; float:right;}>
</style>
<th style="direction: rtl;">
m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m
<span style="position: absolute;">
<tbody style="float: right; page-break-before: right;">m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m m </tbody>
</span>
</th>
<style>
tbody::first-line { }
</style>
</html>

Просмотреть файл

@ -326,3 +326,4 @@ load 564368-1.xhtml
load 564968.xhtml
load 570160.html
load 571618-1.svg
load 574958.xhtml

Просмотреть файл

@ -1731,22 +1731,24 @@ public:
eSVG = 1 << 1,
eSVGForeignObject = 1 << 2,
eSVGContainer = 1 << 3,
eBidiInlineContainer = 1 << 4,
eSVGGeometry = 1 << 4,
eSVGPaintServer = 1 << 5,
eBidiInlineContainer = 1 << 6,
// the frame is for a replaced element, such as an image
eReplaced = 1 << 5,
eReplaced = 1 << 7,
// Frame that contains a block but looks like a replaced element
// from the outside
eReplacedContainsBlock = 1 << 6,
eReplacedContainsBlock = 1 << 8,
// A frame that participates in inline reflow, i.e., one that
// requires nsHTMLReflowState::mLineLayout.
eLineParticipant = 1 << 7,
eXULBox = 1 << 8,
eCanContainOverflowContainers = 1 << 9,
eBlockFrame = 1 << 10,
eLineParticipant = 1 << 9,
eXULBox = 1 << 10,
eCanContainOverflowContainers = 1 << 11,
eBlockFrame = 1 << 12,
// If this bit is set, the frame doesn't allow ignorable whitespace as
// children. For example, the whitespace between <table>\n<tr>\n<td>
// will be excluded during the construction of children.
eExcludesIgnorableWhitespace = 1 << 11,
eExcludesIgnorableWhitespace = 1 << 13,
// These are to allow nsFrame::Init to assert that IsFrameOfType
// implementations all call the base class method. They are only

Просмотреть файл

@ -1,3 +1,4 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -66,7 +67,7 @@ public:
virtual PRBool IsFrameOfType(PRUint32 aFlags) const
{
return nsSVGGeometryFrameBase::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
return nsSVGGeometryFrameBase::IsFrameOfType(aFlags & ~(nsIFrame::eSVG | nsIFrame::eSVGGeometry));
}
// nsSVGGeometryFrame methods:

Просмотреть файл

@ -145,30 +145,28 @@ nsSVGGradientFrame::GetStopInformation(PRInt32 aIndex,
}
gfxMatrix
nsSVGGradientFrame::GetGradientTransform(nsSVGGeometryFrame *aSource)
nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource,
const gfxRect *aOverrideBounds)
{
gfxMatrix bboxMatrix;
PRUint16 gradientUnits = GetGradientUnits();
nsIAtom *callerType = aSource->GetType();
if (gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
// If this gradient is applied to text, our caller
// will be the glyph, which is not a container, so we
// need to get the parent
if (callerType == nsGkAtoms::svgGlyphFrame)
mSourceContent = static_cast<nsSVGElement*>
(aSource->GetContent()->GetParent());
if (aSource->GetContent()->IsNodeOfType(nsINode::eTEXT))
mSource = aSource->GetParent();
else
mSourceContent = static_cast<nsSVGElement*>(aSource->GetContent());
NS_ASSERTION(mSourceContent, "Can't get content for gradient");
mSource = aSource;
} else {
NS_ASSERTION(gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
"Unknown gradientUnits type");
// objectBoundingBox is the default anyway
nsIFrame *frame = (callerType == nsGkAtoms::svgGlyphFrame) ?
nsIFrame *frame = aSource->GetContent()->IsNodeOfType(nsINode::eTEXT) ?
aSource->GetParent() : aSource;
gfxRect bbox = nsSVGUtils::GetBBox(frame);
gfxRect bbox = aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(frame);
bboxMatrix = gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y());
}
@ -201,31 +199,31 @@ nsSVGGradientFrame::GetSpreadMethod()
//----------------------------------------------------------------------
// nsSVGPaintServerFrame methods:
PRBool
nsSVGGradientFrame::SetupPaintServer(gfxContext *aContext,
nsSVGGeometryFrame *aSource,
float aGraphicOpacity)
already_AddRefed<gfxPattern>
nsSVGGradientFrame::GetPaintServerPattern(nsIFrame *aSource,
float aGraphicOpacity,
const gfxRect *aOverrideBounds)
{
// Get the transform list (if there is one)
gfxMatrix patternMatrix = GetGradientTransform(aSource);
gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds);
if (patternMatrix.IsSingular())
return PR_FALSE;
return nsnull;
PRUint32 nStops = GetStopCount();
// SVG specification says that no stops should be treated like
// the corresponding fill or stroke had "none" specified.
if (nStops == 0) {
aContext->SetColor(gfxRGBA(0, 0, 0, 0));
return PR_TRUE;
nsRefPtr<gfxPattern> pattern = new gfxPattern(gfxRGBA(0, 0, 0, 0));
return pattern.forget();
}
patternMatrix.Invert();
nsRefPtr<gfxPattern> gradient = CreateGradient();
if (!gradient || gradient->CairoStatus())
return PR_FALSE;
return nsnull;
PRUint16 aSpread = GetSpreadMethod();
if (aSpread == nsIDOMSVGGradientElement::SVG_SPREADMETHOD_PAD)
@ -259,9 +257,7 @@ nsSVGGradientFrame::SetupPaintServer(gfxContext *aContext,
stopOpacity * aGraphicOpacity));
}
aContext->SetPattern(gradient);
return PR_TRUE;
return gradient.forget();
}
// Private (helper) methods
@ -463,7 +459,7 @@ nsSVGLinearGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName,
PRUint16 gradientUnits = GetGradientUnits();
if (gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
return nsSVGUtils::UserSpace(mSourceContent,
return nsSVGUtils::UserSpace(mSource,
&element->mLengthAttributes[aEnumName]);
}
@ -551,7 +547,7 @@ nsSVGRadialGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName,
PRUint16 gradientUnits = GetGradientUnits();
if (gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
return nsSVGUtils::UserSpace(mSourceContent,
return nsSVGUtils::UserSpace(mSource,
&element->mLengthAttributes[aEnumName]);
}

Просмотреть файл

@ -61,9 +61,10 @@ public:
NS_DECL_FRAMEARENA_HELPERS
// nsSVGPaintServerFrame methods:
virtual PRBool SetupPaintServer(gfxContext *aContext,
nsSVGGeometryFrame *aSource,
float aGraphicOpacity);
virtual already_AddRefed<gfxPattern>
GetPaintServerPattern(nsIFrame *aSource,
float aGraphicOpacity,
const gfxRect *aOverrideBounds);
// nsIFrame interface:
virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext);
@ -106,7 +107,7 @@ private:
float *aOffset, nscolor *aColor, float *aStopOpacity);
// Will be singular for gradientUnits="objectBoundingBox" with an empty bbox.
gfxMatrix GetGradientTransform(nsSVGGeometryFrame *aSource);
gfxMatrix GetGradientTransform(nsIFrame *aSource, const gfxRect *aOverrideBounds);
protected:
virtual already_AddRefed<gfxPattern> CreateGradient() = 0;
@ -126,8 +127,8 @@ protected:
// Get the value of our gradientUnits attribute
PRUint16 GetGradientUnits();
// The graphic element our gradient is (currently) being applied to
nsRefPtr<nsSVGElement> mSourceContent;
// The frame our gradient is (currently) being applied to
nsIFrame* mSource;
private:
// Flag to mark this frame as "in use" during recursive calls along our

Просмотреть файл

@ -35,5 +35,19 @@
* ***** END LICENSE BLOCK ***** */
#include "nsSVGPaintServerFrame.h"
#include "nsSVGGeometryFrame.h"
NS_IMPL_FRAMEARENA_HELPERS(nsSVGPaintServerFrame)
PRBool
nsSVGPaintServerFrame::SetupPaintServer(gfxContext *aContext,
nsSVGGeometryFrame *aSource,
float aOpacity)
{
nsRefPtr<gfxPattern> pattern = GetPaintServerPattern(aSource, aOpacity);
if (!pattern)
return PR_FALSE;
aContext->SetPattern(pattern);
return PR_TRUE;
}

Просмотреть файл

@ -53,13 +53,26 @@ protected:
public:
NS_DECL_FRAMEARENA_HELPERS
/*
/**
* Constructs a gfxPattern of the paint server rendering.
*/
virtual already_AddRefed<gfxPattern>
GetPaintServerPattern(nsIFrame *aSource,
float aOpacity,
const gfxRect *aOverrideBounds = nsnull) = 0;
/**
* Configure paint server prior to rendering
* @return PR_FALSE to skip rendering
*/
virtual PRBool SetupPaintServer(gfxContext *aContext,
nsSVGGeometryFrame *aSource,
float aOpacity) = 0;
float aOpacity);
virtual PRBool IsFrameOfType(PRUint32 aFlags) const
{
return nsSVGPaintServerFrameBase::IsFrameOfType(aFlags & ~nsIFrame::eSVGPaintServer);
}
};
#endif // __NS_SVGPAINTSERVERFRAME_H__

Просмотреть файл

@ -156,8 +156,9 @@ nsSVGPatternFrame::GetCanvasTM()
nsresult
nsSVGPatternFrame::PaintPattern(gfxASurface** surface,
gfxMatrix* patternMatrix,
nsSVGGeometryFrame *aSource,
float aGraphicOpacity)
nsIFrame *aSource,
float aGraphicOpacity,
const gfxRect *aOverrideBounds)
{
/*
* General approach:
@ -198,17 +199,17 @@ nsSVGPatternFrame::PaintPattern(gfxASurface** surface,
// Get all of the information we need from our "caller" -- i.e.
// the geometry that is being rendered with a pattern
nsSVGElement *callerContent;
gfxRect callerBBox;
gfxMatrix callerCTM;
if (NS_FAILED(GetTargetGeometry(&callerCTM,
&callerBBox,
&callerContent, aSource)))
aSource,
aOverrideBounds)))
return NS_ERROR_FAILURE;
// Construct the CTM that we will provide to our children when we
// render them into the tile.
gfxMatrix ctm = ConstructCTM(callerBBox, callerCTM, callerContent);
gfxMatrix ctm = ConstructCTM(callerBBox, callerCTM, aSource);
if (ctm.IsSingular()) {
return NS_ERROR_FAILURE;
}
@ -221,7 +222,7 @@ nsSVGPatternFrame::PaintPattern(gfxASurface** surface,
// Get the bounding box of the pattern. This will be used to determine
// the size of the surface, and will also be used to define the bounding
// box for the pattern tile.
gfxRect bbox = GetPatternRect(callerBBox, callerCTM, callerContent);
gfxRect bbox = GetPatternRect(callerBBox, callerCTM, aSource);
// Get the transformation matrix that we will hand to the renderer's pattern
// routine.
@ -281,8 +282,10 @@ nsSVGPatternFrame::PaintPattern(gfxASurface** surface,
// we got at the beginning because it takes care of the
// referenced pattern situation for us
// Set the geometrical parent of the pattern we are rendering
patternFrame->mSource = aSource;
if (aSource->IsFrameOfType(nsIFrame::eSVGGeometry)) {
// Set the geometrical parent of the pattern we are rendering
patternFrame->mSource = static_cast<nsSVGGeometryFrame*>(aSource);
}
// Delay checking mPaintLoopFlag until here so we can give back a clear
// surface if there's a loop
@ -496,7 +499,7 @@ nsSVGPatternFrame::GetPatternWithAttr(nsIAtom *aAttrName, nsIContent *aDefault)
gfxRect
nsSVGPatternFrame::GetPatternRect(const gfxRect &aTargetBBox,
const gfxMatrix &aTargetCTM,
nsSVGElement *aTarget)
nsIFrame *aTarget)
{
// Get our type
PRUint16 type = GetPatternUnits();
@ -530,7 +533,7 @@ nsSVGPatternFrame::GetPatternRect(const gfxRect &aTargetBBox,
gfxMatrix
nsSVGPatternFrame::ConstructCTM(const gfxRect &callerBBox,
const gfxMatrix &callerCTM,
nsSVGElement *aTargetContent)
nsIFrame *aTarget)
{
gfxMatrix tCTM;
@ -549,9 +552,24 @@ nsSVGPatternFrame::ConstructCTM(const gfxRect &callerBBox,
const nsSVGViewBoxRect viewBox = GetViewBox().GetAnimValue();
if (viewBox.height > 0.0f && viewBox.width > 0.0f) {
nsSVGSVGElement *ctx = aTargetContent->GetCtx();
float viewportWidth = GetWidth()->GetAnimValue(ctx);
float viewportHeight = GetHeight()->GetAnimValue(ctx);
float viewportWidth, viewportHeight, refX, refY;
nsIContent* targetContent = aTarget->GetContent();
if (targetContent->IsSVG()) {
// If we're dealing with an SVG target only retrieve the context once.
// Calling the nsIFrame* variant of GetAnimValue would look it up on
// every call.
nsSVGSVGElement *ctx = static_cast<nsSVGElement*>(targetContent)->GetCtx();
viewportWidth = GetWidth()->GetAnimValue(ctx);
viewportHeight = GetHeight()->GetAnimValue(ctx);
refX = GetX()->GetAnimValue(ctx);
refY = GetY()->GetAnimValue(ctx);
} else {
// No SVG target, call the nsIFrame* variant of GetAnimValue.
viewportWidth = GetWidth()->GetAnimValue(aTarget);
viewportHeight = GetHeight()->GetAnimValue(aTarget);
refX = GetX()->GetAnimValue(aTarget);
refY = GetY()->GetAnimValue(aTarget);
}
gfxMatrix viewBoxTM = nsSVGUtils::GetViewBoxTransform(patternElement,
viewportWidth, viewportHeight,
viewBox.x, viewBox.y,
@ -559,8 +577,6 @@ nsSVGPatternFrame::ConstructCTM(const gfxRect &callerBBox,
GetPreserveAspectRatio(),
PR_TRUE);
float refX = GetX()->GetAnimValue(ctx);
float refY = GetY()->GetAnimValue(ctx);
gfxPoint ref = viewBoxTM.Transform(gfxPoint(refX, refY));
tm = viewBoxTM * gfxMatrix().Translate(gfxPoint(-ref.x, -ref.y));
@ -596,31 +612,20 @@ nsSVGPatternFrame::GetPatternMatrix(const gfxRect &bbox,
nsresult
nsSVGPatternFrame::GetTargetGeometry(gfxMatrix *aCTM,
gfxRect *aBBox,
nsSVGElement **aTargetContent,
nsSVGGeometryFrame *aTarget)
nsIFrame *aTarget,
const gfxRect *aOverrideBounds)
{
*aTargetContent = nsnull;
// Make sure the callerContent is an SVG element. If we are attempting
// to paint a pattern for text, then the content will be the #text, so we
// actually want the parent, which should be the <svg:text> or <svg:tspan>
// element.
nsIAtom *callerType = aTarget->GetType();
if (callerType == nsGkAtoms::svgGlyphFrame) {
*aTargetContent = static_cast<nsSVGElement*>
(aTarget->GetContent()->GetParent());
} else {
*aTargetContent = static_cast<nsSVGElement*>(aTarget->GetContent());
}
NS_ASSERTION(*aTargetContent,"Caller does not have any content!");
if (!*aTargetContent)
return NS_ERROR_FAILURE;
if (callerType == nsGkAtoms::svgGlyphFrame) {
// If we are attempting to paint a pattern for text, then the content will be
// the #text, so we actually want the parent, which should be the <svg:text>
// or <svg:tspan> element.
if (aTarget->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
*aBBox = nsSVGUtils::GetBBox(aTarget->GetParent());
} else {
*aBBox = nsSVGUtils::GetBBox(aTarget);
}
if (aOverrideBounds) {
*aBBox = *aOverrideBounds;
}
// Sanity check
PRUint16 type = GetPatternUnits();
@ -631,7 +636,7 @@ nsSVGPatternFrame::GetTargetGeometry(gfxMatrix *aCTM,
}
// Get the transformation matrix from our calling geometry
*aCTM = aTarget->GetCanvasTM();
*aCTM = nsSVGUtils::GetCanvasTM(aTarget);
// OK, now fix up the bounding box to reflect user coordinates
// We handle device unit scaling in pattern matrix
@ -648,32 +653,28 @@ nsSVGPatternFrame::GetTargetGeometry(gfxMatrix *aCTM,
//----------------------------------------------------------------------
// nsSVGPaintServerFrame methods:
PRBool
nsSVGPatternFrame::SetupPaintServer(gfxContext *aContext,
nsSVGGeometryFrame *aSource,
float aGraphicOpacity)
already_AddRefed<gfxPattern>
nsSVGPatternFrame::GetPaintServerPattern(nsIFrame *aSource,
float aGraphicOpacity,
const gfxRect *aOverrideBounds)
{
if (aGraphicOpacity == 0.0f) {
aContext->SetColor(gfxRGBA(0, 0, 0, 0));
return PR_TRUE;
nsRefPtr<gfxPattern> pattern = new gfxPattern(gfxRGBA(0, 0, 0, 0));
return pattern.forget();
}
gfxMatrix matrix = aContext->CurrentMatrix();
// Paint it!
nsRefPtr<gfxASurface> surface;
gfxMatrix pMatrix;
aContext->IdentityMatrix();
nsresult rv = PaintPattern(getter_AddRefs(surface), &pMatrix,
aSource, aGraphicOpacity);
aSource, aGraphicOpacity, aOverrideBounds);
aContext->SetMatrix(matrix);
if (NS_FAILED(rv)) {
return PR_FALSE;
return nsnull;
}
if (pMatrix.IsSingular()) {
return PR_FALSE;
return nsnull;
}
pMatrix.Invert();
@ -685,10 +686,7 @@ nsSVGPatternFrame::SetupPaintServer(gfxContext *aContext,
pattern->SetMatrix(pMatrix);
pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
aContext->SetPattern(pattern);
return PR_TRUE;
return pattern.forget();
}
// -------------------------------------------------------------------------

Просмотреть файл

@ -66,15 +66,11 @@ public:
nsSVGPatternFrame(nsStyleContext* aContext);
nsresult PaintPattern(gfxASurface **surface,
gfxMatrix *patternMatrix,
nsSVGGeometryFrame *aSource,
float aGraphicOpacity);
// nsSVGPaintServerFrame methods:
virtual PRBool SetupPaintServer(gfxContext *aContext,
nsSVGGeometryFrame *aSource,
float aGraphicOpacity);
virtual already_AddRefed<gfxPattern>
GetPaintServerPattern(nsIFrame *aSource,
float aOpacity,
const gfxRect *aOverrideBounds);
public:
// nsSVGContainerFrame methods:
@ -128,20 +124,26 @@ protected:
const nsSVGViewBox &GetViewBox();
const nsSVGPreserveAspectRatio &GetPreserveAspectRatio();
nsresult PaintPattern(gfxASurface **surface,
gfxMatrix *patternMatrix,
nsIFrame *aSource,
float aGraphicOpacity,
const gfxRect *aOverrideBounds);
NS_IMETHOD GetPatternFirstChild(nsIFrame **kid);
gfxRect GetPatternRect(const gfxRect &bbox,
const gfxMatrix &callerCTM,
nsSVGElement *content);
nsIFrame *aTarget);
gfxMatrix GetPatternMatrix(const gfxRect &bbox,
const gfxRect &callerBBox,
const gfxMatrix &callerCTM);
gfxMatrix ConstructCTM(const gfxRect &callerBBox,
const gfxMatrix &callerCTM,
nsSVGElement *aTargetContent);
nsIFrame *aTarget);
nsresult GetTargetGeometry(gfxMatrix *aCTM,
gfxRect *aBBox,
nsSVGElement **aTargetContent,
nsSVGGeometryFrame *aTarget);
nsIFrame *aTarget,
const gfxRect *aOverrideBounds);
private:
// this is a *temporary* reference to the frame of the element currently

Просмотреть файл

@ -56,6 +56,7 @@
#include "imgRequest.h"
#include "imgRequestProxy.h"
#include "imgTools.h"
#include "imgDiscardTracker.h"
#ifdef IMG_BUILD_DECODER_gif
// gif
@ -249,6 +250,7 @@ static void
imglib_Shutdown()
{
imgLoader::Shutdown();
imgDiscardTracker::Shutdown();
}
static const mozilla::Module kImageModule = {

Просмотреть файл

@ -58,6 +58,7 @@ CPPSRCS = \
imgRequestProxy.cpp \
imgTools.cpp \
imgContainerRequest.cpp \
imgDiscardTracker.cpp \
$(NULL)
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -144,7 +144,6 @@ imgContainer::imgContainer() :
mLoopCount(-1),
mObserver(nsnull),
mLockCount(0),
mDiscardTimer(nsnull),
mDecoder(nsnull),
mWorker(nsnull),
mBytesDecoded(0),
@ -161,6 +160,10 @@ imgContainer::imgContainer() :
mInDecoder(PR_FALSE),
mError(PR_FALSE)
{
// Set up the discard tracker node.
mDiscardTrackerNode.curr = this;
mDiscardTrackerNode.prev = mDiscardTrackerNode.next = nsnull;
// Statistics
num_containers++;
}
@ -190,10 +193,7 @@ imgContainer::~imgContainer()
discardable_source_bytes));
}
if (mDiscardTimer) {
mDiscardTimer->Cancel();
mDiscardTimer = nsnull;
}
imgDiscardTracker::Remove(&mDiscardTrackerNode);
// If we have a decoder open, shut it down
if (mDecoder) {
@ -1026,9 +1026,9 @@ NS_IMETHODIMP imgContainer::DecodingComplete(void)
// We now have one of the qualifications for discarding. Re-evaluate.
if (CanDiscard()) {
NS_ABORT_IF_FALSE(!mDiscardTimer,
NS_ABORT_IF_FALSE(!DiscardingActive(),
"We shouldn't have been discardable before this");
rv = ResetDiscardTimer();
rv = imgDiscardTracker::Reset(&mDiscardTrackerNode);
CONTAINER_ENSURE_SUCCESS(rv);
}
@ -1345,7 +1345,7 @@ NS_IMETHODIMP imgContainer::SourceDataComplete()
// We now have one of the qualifications for discarding. Re-evaluate.
if (CanDiscard()) {
nsresult rv = ResetDiscardTimer();
nsresult rv = imgDiscardTracker::Reset(&mDiscardTrackerNode);
CONTAINER_ENSURE_SUCCESS(rv);
}
return NS_OK;
@ -2014,46 +2014,32 @@ NS_IMETHODIMP imgContainer::GetKeys(PRUint32 *count, char ***keys)
return mProperties->GetKeys(count, keys);
}
static int
get_discard_timer_ms (void)
{
/* FIXME: don't hardcode this */
return 15000; /* 15 seconds */
}
void
imgContainer::sDiscardTimerCallback(nsITimer *aTimer, void *aClosure)
imgContainer::Discard()
{
// Retrieve self pointer and null out the expired timer
imgContainer *self = (imgContainer *) aClosure;
NS_ABORT_IF_FALSE(aTimer == self->mDiscardTimer,
"imgContainer::DiscardTimerCallback() got a callback "
"for an unknown timer");
self->mDiscardTimer = nsnull;
// We should be ok for discard
NS_ABORT_IF_FALSE(self->CanDiscard(), "Hit discard callback but can't discard!");
NS_ABORT_IF_FALSE(CanDiscard(), "Asked to discard but can't!");
// We should never discard when we have an active decoder
NS_ABORT_IF_FALSE(!self->mDecoder, "Discard callback fired with open decoder!");
NS_ABORT_IF_FALSE(!mDecoder, "Asked to discard with open decoder!");
// As soon as an image becomes animated, it becomes non-discardable and any
// timers are cancelled.
NS_ABORT_IF_FALSE(!self->mAnim, "Discard callback fired for animated image!");
NS_ABORT_IF_FALSE(!mAnim, "Asked to discard for animated image!");
// For post-operation logging
int old_frame_count = self->mFrames.Length();
int old_frame_count = mFrames.Length();
// Delete all the decoded frames, then clear the array.
for (int i = 0; i < old_frame_count; ++i)
delete self->mFrames[i];
self->mFrames.Clear();
delete mFrames[i];
mFrames.Clear();
// Flag that we no longer have decoded frames for this image
self->mDecoded = PR_FALSE;
mDecoded = PR_FALSE;
// Notify that we discarded
nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(self->mObserver));
nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
if (observer)
observer->OnDiscard(nsnull);
@ -2063,43 +2049,16 @@ imgContainer::sDiscardTimerCallback(nsITimer *aTimer, void *aClosure)
"data from imgContainer %p (%s) - %d frames (cached count: %d); "
"Total Containers: %d, Discardable containers: %d, "
"Total source bytes: %lld, Source bytes for discardable containers %lld",
self,
self->mSourceDataMimeType.get(),
this,
mSourceDataMimeType.get(),
old_frame_count,
self->mFrames.Length(),
mFrames.Length(),
num_containers,
num_discardable_containers,
total_source_bytes,
discardable_source_bytes));
}
nsresult
imgContainer::ResetDiscardTimer()
{
// We should not call this function if we can't discard
NS_ABORT_IF_FALSE(CanDiscard(), "Calling ResetDiscardTimer but can't discard!");
// As soon as an image becomes animated it is set non-discardable
NS_ABORT_IF_FALSE(!mAnim, "Trying to reset discard timer on animated image!");
// If we have a timer already ticking, cancel it
if (mDiscardTimer) {
nsresult rv = mDiscardTimer->Cancel();
CONTAINER_ENSURE_SUCCESS(rv);
mDiscardTimer = nsnull;
}
// Create a new timer
mDiscardTimer = do_CreateInstance("@mozilla.org/timer;1");
CONTAINER_ENSURE_TRUE(mDiscardTimer, NS_ERROR_OUT_OF_MEMORY);
// Activate the timer
return mDiscardTimer->InitWithFuncCallback(sDiscardTimerCallback,
(void *) this,
get_discard_timer_ms (),
nsITimer::TYPE_ONE_SHOT);
}
// Helper method to determine if we can discard an image
PRBool
imgContainer::CanDiscard() {
@ -2110,6 +2069,13 @@ imgContainer::CanDiscard() {
mDecoded); // ...and have something to discard.
}
// Helper method to tell us whether the clock is currently running for
// discarding this image. Mainly for assertions.
PRBool
imgContainer::DiscardingActive() {
return !!(mDiscardTrackerNode.prev || mDiscardTrackerNode.next);
}
// Helper method to determine if we're storing the source data in a buffer
// or just writing it directly to the decoder
PRBool
@ -2130,7 +2096,7 @@ imgContainer::InitDecoder (PRUint32 dFlags)
NS_ABORT_IF_FALSE(!mDecoded, "Calling InitDecoder() but already decoded!");
// Since we're not decoded, we should not have a discard timer active
NS_ABORT_IF_FALSE(!mDiscardTimer, "Discard Timer active in InitDecoder()!");
NS_ABORT_IF_FALSE(!DiscardingActive(), "Discard Timer active in InitDecoder()!");
// Find and instantiate the decoder
nsCAutoString decoderCID(NS_LITERAL_CSTRING("@mozilla.org/image/decoder;3?type=") +
@ -2277,10 +2243,11 @@ imgContainer::WantDecodedFrames()
{
nsresult rv;
// If we can discard, we should have a timer already. reset it.
// If we can discard, the clock should be running. Reset it.
if (CanDiscard()) {
NS_ABORT_IF_FALSE(mDiscardTimer, "Decoded and discardable but timer not set!");
rv = ResetDiscardTimer();
NS_ABORT_IF_FALSE(DiscardingActive(),
"Decoded and discardable but discarding not activated!");
rv = imgDiscardTracker::Reset(&mDiscardTrackerNode);
CONTAINER_ENSURE_SUCCESS(rv);
}
@ -2445,11 +2412,7 @@ imgContainer::LockImage()
return NS_ERROR_FAILURE;
// Cancel the discard timer if it's there
if (mDiscardTimer) {
mDiscardTimer->Cancel();
mDiscardTimer = nsnull; // It's wasteful to null out the discard timers each
// time, but we'll wait to fix that until bug 502694.
}
imgDiscardTracker::Remove(&mDiscardTrackerNode);
// Increment the lock count
mLockCount++;
@ -2471,15 +2434,15 @@ imgContainer::UnlockImage()
if (mLockCount == 0)
return NS_ERROR_ABORT;
// We're locked, so we shouldn't have a discard timer set
NS_ABORT_IF_FALSE(!mDiscardTimer, "Locked, but discard timer set!");
// We're locked, so discarding should not be active
NS_ABORT_IF_FALSE(!DiscardingActive(), "Locked, but discarding activated");
// Decrement our lock count
mLockCount--;
// We now _might_ have one of the qualifications for discarding. Re-evaluate.
if (CanDiscard()) {
nsresult rv = ResetDiscardTimer();
nsresult rv = imgDiscardTracker::Reset(&mDiscardTrackerNode);
CONTAINER_ENSURE_SUCCESS(rv);
}

Просмотреть файл

@ -63,6 +63,7 @@
#include "nsTArray.h"
#include "imgFrame.h"
#include "nsThreadUtils.h"
#include "imgDiscardTracker.h"
#define NS_IMGCONTAINER_CID \
{ /* c76ff2c1-9bf6-418a-b143-3340c00112f7 */ \
@ -157,6 +158,9 @@ public:
PRUint32 GetDecodedDataSize();
PRUint32 GetSourceDataSize();
/* Triggers discarding. */
void Discard();
private:
struct Anim
{
@ -321,13 +325,14 @@ private: // data
// Discard members
PRUint32 mLockCount;
nsCOMPtr<nsITimer> mDiscardTimer;
imgDiscardTrackerNode mDiscardTrackerNode;
// Source data members
nsTArray<char> mSourceData;
nsCString mSourceDataMimeType;
friend class imgDecodeWorker;
friend class imgDiscardTracker;
// Decoder and friends
nsCOMPtr<imgIDecoder> mDecoder;
@ -353,10 +358,6 @@ private: // data
PRPackedBool mError:1; // Error handling
// Discard code
nsresult ResetDiscardTimer();
static void sDiscardTimerCallback(nsITimer *aTimer, void *aClosure);
// Decoding
nsresult WantDecodedFrames();
nsresult SyncDecode();
@ -377,6 +378,7 @@ private: // data
// Helpers
void DoError();
PRBool CanDiscard();
PRBool DiscardingActive();
PRBool StoringSourceData();
};

Просмотреть файл

@ -0,0 +1,215 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsComponentManagerUtils.h"
#include "nsITimer.h"
#include "imgContainer.h"
#include "imgDiscardTracker.h"
static PRBool sInitialized = PR_FALSE;
static PRBool sTimerOn = PR_FALSE;
/* TODO - don't hardcode. See bug 478398. */
static PRUint32 sMinDiscardTimeoutMs = 10000;
static nsITimer *sTimer = nsnull;
static struct imgDiscardTrackerNode sHead, sSentinel, sTail;
/*
* Puts an image in the back of the tracker queue. If the image is already
* in the tracker, this removes it first.
*/
nsresult
imgDiscardTracker::Reset(imgDiscardTrackerNode *node)
{
nsresult rv;
PRBool isSentinel = (node == &sSentinel);
// Sanity check the node.
NS_ABORT_IF_FALSE(isSentinel || node->curr, "Node doesn't point to anything!");
// We should not call this function if we can't discard
NS_ABORT_IF_FALSE(isSentinel || node->curr->CanDiscard(),
"trying to reset discarding but can't discard!");
// As soon as an image becomes animated it is set non-discardable
NS_ABORT_IF_FALSE(isSentinel || !node->curr->mAnim,
"Trying to reset discarding on animated image!");
// Initialize the first time through
if (NS_UNLIKELY(!sInitialized)) {
rv = Initialize();
NS_ENSURE_SUCCESS(rv, rv);
}
// Remove the node if it's in the list.
Remove(node);
// Append it to the list.
node->prev = sTail.prev;
node->next = &sTail;
node->prev->next = sTail.prev = node;
// Make sure the timer is running
rv = TimerOn();
NS_ENSURE_SUCCESS(rv,rv);
return NS_OK;
}
/*
* Removes a node from the tracker. No-op if the node is currently untracked.
*/
void
imgDiscardTracker::Remove(imgDiscardTrackerNode *node)
{
NS_ABORT_IF_FALSE(node != nsnull, "Can't pass null node");
// If we're not in a list, we have nothing to do.
if ((node->prev == nsnull) || (node->next == nsnull)) {
NS_ABORT_IF_FALSE(node->prev == node->next,
"Node is half in a list!");
return;
}
// Connect around ourselves
node->prev->next = node->next;
node->next->prev = node->prev;
// Clean up the node we removed.
node->prev = node->next = nsnull;
}
/**
* Initialize the tracker.
*/
nsresult
imgDiscardTracker::Initialize()
{
nsresult rv;
// Set up the list. Head<->Sentinel<->Tail
sHead.curr = sTail.curr = sSentinel.curr = nsnull;
sHead.prev = sTail.next = nsnull;
sHead.next = sTail.prev = &sSentinel;
sSentinel.prev = &sHead;
sSentinel.next = &sTail;
// Create and start the timer
nsCOMPtr<nsITimer> t = do_CreateInstance("@mozilla.org/timer;1");
NS_ENSURE_TRUE(t, NS_ERROR_OUT_OF_MEMORY);
t.forget(&sTimer);
rv = TimerOn();
NS_ENSURE_SUCCESS(rv, rv);
// Mark us as initialized
sInitialized = PR_TRUE;
return NS_OK;
}
/**
* Shut down the tracker, deallocating the timer.
*/
void
imgDiscardTracker::Shutdown()
{
if (sTimer) {
sTimer->Cancel();
NS_RELEASE(sTimer);
sTimer = nsnull;
}
}
/**
* Enables the timer. No-op if the timer is already running.
*/
nsresult
imgDiscardTracker::TimerOn()
{
// Nothing to do if the timer's already on.
if (sTimerOn)
return NS_OK;
sTimerOn = PR_TRUE;
// Activate
return sTimer->InitWithFuncCallback(TimerCallback,
nsnull,
sMinDiscardTimeoutMs,
nsITimer::TYPE_REPEATING_SLACK);
}
/*
* Disables the timer. No-op if the timer isn't running.
*/
void
imgDiscardTracker::TimerOff()
{
// Nothing to do if the timer's already off.
if (!sTimerOn)
return;
sTimerOn = PR_FALSE;
// Deactivate
sTimer->Cancel();
}
/**
* Routine activated when the timer fires. This discards everything
* in front of sentinel, and resets the sentinel to the back of the
* list.
*/
void
imgDiscardTracker::TimerCallback(nsITimer *aTimer, void *aClosure)
{
imgDiscardTrackerNode *node;
// Remove and discard everything before the sentinel
for (node = sSentinel.prev; node != &sHead; node = sSentinel.prev) {
NS_ABORT_IF_FALSE(node->curr, "empty node!");
Remove(node);
node->curr->Discard();
}
// Append the sentinel to the back of the list
Reset(&sSentinel);
// If there's nothing in front of the sentinel, the next callback
// is guaranteed to be a no-op. Disable the timer as an optimization.
if (sSentinel.prev == &sHead)
TimerOff();
}

Просмотреть файл

@ -0,0 +1,83 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bobby Holley <bobbyholley@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __imgDiscardTracker_h__
#define __imgDiscardTracker_h__
class imgContainer;
class nsITimer;
// Struct to make an imgContainer insertable into the tracker list. This
// is embedded within each imgContainer object, and we do 'this->curr = this'
// on imgContainer construction. Thus, an imgContainer must always call
// imgDiscardTracker::Remove() in its destructor to avoid having the tracker
// point to bogus memory.
struct imgDiscardTrackerNode
{
// Pointer to the imgContainer that this node tracks
imgContainer *curr;
// Pointers to the previous and next nodes in the list
imgDiscardTrackerNode *prev, *next;
};
/**
* This static class maintains a linked list of imgContainer nodes. When Reset()
* is called, the node is removed from its position in the list (if it was there
* before) and appended to the end. When Remove() is called, the node is removed
* from the list. The timer fires once every MIN_DISCARD_TIMEOUT_MS ms. When it
* does, it calls Discard() on each container preceding it, and then appends
* itself to the end of the list. Thus, the discard timeout varies between
* MIN_DISCARD_TIMEOUT_MS and 2*MIN_DISCARD_TIMEOUT_MS.
*/
class imgDiscardTracker
{
public:
static nsresult Reset(struct imgDiscardTrackerNode *node);
static void Remove(struct imgDiscardTrackerNode *node);
static void Shutdown();
private:
static nsresult Initialize();
static nsresult TimerOn();
static void TimerOff();
static void TimerCallback(nsITimer *aTimer, void *aClosure);
};
#endif /* __imgDiscardTracker_h__ */

Просмотреть файл

@ -694,7 +694,8 @@ pref("network.http.redirection-limit", 20);
// Enable http compression: comment this out in case of problems with 1.1
// NOTE: support for "compress" has been disabled per bug 196406.
pref("network.http.accept-encoding" ,"gzip,deflate");
// NOTE: separate values with comma+space (", "): see bug 576033
pref("network.http.accept-encoding", "gzip, deflate");
pref("network.http.pipelining" , false);
pref("network.http.pipelining.ssl" , false); // disable pipelining over SSL

Просмотреть файл

@ -393,6 +393,14 @@ Connection::initialize(nsIFile *aDatabaseFile)
PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s' (%p)",
leafName.get(), this));
#endif
// Switch db to preferred page size in case the user vacuums.
sqlite3_stmt *stmt;
srv = prepareStmt(mDBConn, NS_LITERAL_CSTRING("PRAGMA page_size = 32768"),
&stmt);
if (srv == SQLITE_OK) {
(void)stepStmt(stmt);
(void)::sqlite3_finalize(stmt);
}
// Register our built-in SQL functions.
srv = registerFunctions(mDBConn);
@ -412,7 +420,6 @@ Connection::initialize(nsIFile *aDatabaseFile)
// Execute a dummy statement to force the db open, and to verify if it is
// valid or not.
sqlite3_stmt *stmt;
srv = prepareStmt(mDBConn, NS_LITERAL_CSTRING("SELECT * FROM sqlite_master"),
&stmt);
if (srv == SQLITE_OK) {

Просмотреть файл

@ -0,0 +1,28 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// This file tests that dbs are using 32k pagesize
function check_size(db)
{
var stmt = db.createStatement("PRAGMA page_size");
stmt.executeStep();
const expected_block_size = 32768; // 32K
do_check_eq(stmt.getInt32(0), expected_block_size);
stmt.finalize();
}
function new_file(name)
{
var file = dirSvc.get("ProfD", Ci.nsIFile);
file.append(name + ".sqlite");
do_check_false(file.exists());
}
function run_test()
{
check_size(getDatabase(new_file("shared32k.sqlite")));
check_size(getService().openUnsharedDatabase(new_file("unshared32k.sqlite")));
}

Просмотреть файл

@ -140,13 +140,6 @@ using namespace mozilla::places;
// corresponding migrateVxx method below.
#define DATABASE_SCHEMA_VERSION 10
// We set the default database page size to be larger. sqlite's default is 1K.
// This gives good performance when many small parts of the file have to be
// loaded for each statement. Because we try to keep large chunks of the file
// in memory, a larger page size should give better I/O performance. 32K is
// sqlite's default max page size.
#define DATABASE_PAGE_SIZE 4096
// Filename of the database.
#define DATABASE_FILENAME NS_LITERAL_STRING("places.sqlite")
@ -636,37 +629,23 @@ nsNavHistory::InitDBFile(PRBool aForceInit)
nsresult
nsNavHistory::InitDB()
{
PRInt32 pageSize = DATABASE_PAGE_SIZE;
// Get the database schema version.
PRInt32 currentSchemaVersion = 0;
nsresult rv = mDBConn->GetSchemaVersion(&currentSchemaVersion);
NS_ENSURE_SUCCESS(rv, rv);
bool databaseInitialized = (currentSchemaVersion > 0);
if (!databaseInitialized) {
// First of all we must set page_size since it will only have effect on
// empty files. For existing databases we could get a different page size,
// trying to change it would be uneffective.
// See bug 401985 for details.
nsCAutoString pageSizePragma("PRAGMA page_size = ");
pageSizePragma.AppendInt(pageSize);
rv = mDBConn->ExecuteSimpleSQL(pageSizePragma);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
// Get the page size. This may be different than the default if the
// database file already existed with a different page size.
nsCOMPtr<mozIStorageStatement> statement;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("PRAGMA page_size"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
// Get the page size. This may be different than the default if the
// database file already existed with a different page size.
nsCOMPtr<mozIStorageStatement> statement;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("PRAGMA page_size"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
PRBool hasResult;
rv = statement->ExecuteStep(&hasResult);
NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasResult, NS_ERROR_FAILURE);
pageSize = statement->AsInt32(0);
}
PRBool hasResult;
mozStorageStatementScoper scoper(statement);
rv = statement->ExecuteStep(&hasResult);
NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasResult, NS_ERROR_FAILURE);
PRInt32 pageSize = statement->AsInt32(0);
// Ensure that temp tables are held in memory, not on disk. We use temp
// tables mainly for fsync and I/O reduction.
@ -728,6 +707,7 @@ nsNavHistory::InitDB()
rv = nsAnnotationService::InitTables(mDBConn);
NS_ENSURE_SUCCESS(rv, rv);
bool databaseInitialized = (currentSchemaVersion > 0);
if (!databaseInitialized) {
// This is the first run, so we set schema version to the latest one, since
// we don't need to migrate anything. We will create tables from scratch.

Просмотреть файл

@ -96,6 +96,11 @@ function run_test()
fh.addEntry("name-C", "value-C");
do_check_true(fh.entryExists("name-C", "value-C"));
// Check the original db size.
// Do a vacuum to make sure the db has current page size.
fh.DBConnection.executeSimpleSQL("VACUUM");
var oldSize = dbFile.clone().fileSize;
// Update some existing entries to have ages relative to when the test runs.
var now = 1000 * Date.now();
var age181 = now - 181 * 24 * PR_HOURS;
@ -168,8 +173,6 @@ function run_test()
do_check_true(fh.entryExists("9DaysOld", "foo"));
do_check_eq(505, countAllEntries());
do_check_true(dbFile.fileSize > 70000);
triggerExpiration();
do_check_false(fh.entryExists("bar", "29days"));
@ -181,7 +184,7 @@ function run_test()
// Check that the file size was reduced.
// Need to clone the nsIFile because the size is being cached on Windows.
dbFile = dbFile.clone();
do_check_true(dbFile.fileSize < 6000);
do_check_true(dbFile.fileSize < oldSize);
} catch (e) {

Просмотреть файл

@ -173,8 +173,6 @@ static const PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull;
#define UPDATE_DELAY_TIME "urlclassifier.updatetime"
#define UPDATE_DELAY_TIME_DEFAULT 60
#define PAGE_SIZE 4096
class nsUrlClassifierDBServiceWorker;
// Singleton instance.
@ -581,7 +579,6 @@ nsUrlClassifierStore::Close()
mPartialEntriesAfterStatement = nsnull;
mPartialEntriesBeforeStatement = nsnull;
mLastPartialEntriesStatement = nsnull;
mRandomStatement = nsnull;
mConnection = nsnull;
@ -1223,6 +1220,7 @@ private:
nsCOMPtr<mozIStorageStatement> mGetTableIdStatement;
nsCOMPtr<mozIStorageStatement> mGetTableNameStatement;
nsCOMPtr<mozIStorageStatement> mInsertTableIdStatement;
nsCOMPtr<mozIStorageStatement> mGetPageSizeStatement;
// Stores the last time a given table was updated.
nsDataHashtable<nsCStringHashKey, PRInt64> mTableFreshness;
@ -3164,7 +3162,13 @@ nsUrlClassifierDBServiceWorker::SetupUpdate()
NS_ENSURE_SUCCESS(rv, rv);
if (gUpdateCacheSize > 0) {
PRUint32 cachePages = gUpdateCacheSize / PAGE_SIZE;
PRBool hasResult;
rv = mGetPageSizeStatement->ExecuteStep(&hasResult);
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(hasResult, "Should always be able to get page size from sqlite");
PRUint32 pageSize = mGetPageSizeStatement->AsInt32(0);
PRUint32 cachePages = gUpdateCacheSize / pageSize;
nsCAutoString cacheSizePragma("PRAGMA cache_size=");
cacheSizePragma.AppendInt(cachePages);
rv = mConnection->ExecuteSimpleSQL(cacheSizePragma);
@ -3321,6 +3325,7 @@ nsUrlClassifierDBServiceWorker::CloseDb()
mGetTableIdStatement = nsnull;
mGetTableNameStatement = nsnull;
mInsertTableIdStatement = nsnull;
mGetPageSizeStatement = nsnull;
mConnection = nsnull;
LOG(("urlclassifier db closed\n"));
@ -3413,11 +3418,6 @@ nsUrlClassifierDBServiceWorker::OpenDb()
}
}
nsCAutoString cacheSizePragma("PRAGMA page_size=");
cacheSizePragma.AppendInt(PAGE_SIZE);
rv = connection->ExecuteSimpleSQL(cacheSizePragma);
NS_ENSURE_SUCCESS(rv, rv);
rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous=OFF"));
NS_ENSURE_SUCCESS(rv, rv);
@ -3475,6 +3475,11 @@ nsUrlClassifierDBServiceWorker::OpenDb()
getter_AddRefs(mInsertTableIdStatement));
NS_ENSURE_SUCCESS(rv, rv);
rv = connection->CreateStatement
(NS_LITERAL_CSTRING("PRAGMA page_size"),
getter_AddRefs(mGetPageSizeStatement));
NS_ENSURE_SUCCESS(rv, rv);
mConnection = connection;
mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);

Просмотреть файл

@ -49,8 +49,17 @@ function start() {
is(window.outerHeight, oldOuterHeight, "wrong outerHeight after removing drawintitlebar");
is(window.innerWidth, oldInnerWidth, "wrong innerWidth after removing drawintitlebar");
is(window.innerHeight, oldInnerHeight, "wrong innerHeight after removing drawintitlebar");
window.opener.wrappedJSObject.SimpleTest.finish();
window.close();
// Test whether having drawintitlebar also works when it's set on the window
// from the beginning.
var win = open('data:application/vnd.mozilla.xul+xml,<?xml version="1.0"?><?xml-stylesheet href="chrome://global/skin" type="text/css"?><window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" drawintitlebar="true" width="200" height="200"/>', '_blank', 'chrome');
win.onfocus = function () {
is(win.innerWidth, win.outerWidth, "if drawintitlebar is set, innerWidth and outerWidth should be the same");
is(win.innerHeight, win.outerHeight, "if drawintitlebar is set, innerHeight and outerHeight should be the same");
win.close();
window.opener.wrappedJSObject.SimpleTest.finish();
window.close();
}
}
}

Просмотреть файл

@ -1410,6 +1410,12 @@ void nsXULWindow::SyncAttributesToWidget()
if (NS_SUCCEEDED(rv)) {
mWindow->SetShowsToolbarButton(attr.LowerCaseEqualsLiteral("true"));
}
// "drawintitlebar" attribute
rv = windowElement->GetAttribute(NS_LITERAL_STRING("drawintitlebar"), attr);
if (NS_SUCCEEDED(rv)) {
mWindow->SetDrawsInTitlebar(attr.EqualsLiteral("true"));
}
}
NS_IMETHODIMP nsXULWindow::SavePersistentAttributes()