зеркало из https://github.com/mozilla/gecko-dev.git
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:
Коммит
a08857c778
|
@ -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,7 +58,7 @@ public:
|
|||
: public NS_CYCLE_COLLECTION_CLASSNAME(nsDOMEventTargetHelper)
|
||||
{
|
||||
NS_IMETHOD RootAndUnlinkJSObjects(void *p);
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY(nsDOMEventTargetWrapperCache,
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_BODY_NO_UNLINK(nsDOMEventTargetWrapperCache,
|
||||
nsDOMEventTargetHelper)
|
||||
NS_IMETHOD_(void) Trace(void *p, TraceCallback cb, void *closure);
|
||||
};
|
||||
|
|
|
@ -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,11 +180,24 @@ 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) {
|
||||
|
||||
// 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));
|
||||
return NS_OK;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}, 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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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);
|
||||
|
||||
if (!mTexImage && !mVisibleRegion.IsEmpty())
|
||||
{
|
||||
mTexImage = gl()->CreateTextureImage(visibleSize,
|
||||
contentType,
|
||||
LOCAL_GL_CLAMP_TO_EDGE);
|
||||
DEBUG_GL_ERROR_CHECK(gl());
|
||||
return PR_TRUE;
|
||||
}
|
||||
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();
|
||||
|
||||
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!
|
||||
*/
|
||||
nsIntRegion rgnToPaint = mVisibleRegion;
|
||||
rgnToPaint.Sub(rgnToPaint, mValidRegion);
|
||||
PRBool textureBound = PR_FALSE;
|
||||
if (!rgnToPaint.IsEmpty())
|
||||
{
|
||||
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();
|
||||
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;
|
||||
}
|
||||
break;
|
||||
// and translate update context back to screen space
|
||||
ctx->Translate(-gfxPoint(visibleRect.x, visibleRect.y));
|
||||
|
||||
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
|
||||
|
||||
if (aSource->IsFrameOfType(nsIFrame::eSVGGeometry)) {
|
||||
// Set the geometrical parent of the pattern we are rendering
|
||||
patternFrame->mSource = aSource;
|
||||
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,25 +629,11 @@ nsNavHistory::InitDBFile(PRBool aForceInit)
|
|||
nsresult
|
||||
nsNavHistory::InitDB()
|
||||
{
|
||||
PRInt32 pageSize = DATABASE_PAGE_SIZE;
|
||||
|
||||
// Get the database schema version.
|
||||
PRInt32 currentSchemaVersion = 0;
|
||||
nsresult rv = mDBConn->GetSchemaVersion(¤tSchemaVersion);
|
||||
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;
|
||||
|
@ -663,10 +642,10 @@ nsNavHistory::InitDB()
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool hasResult;
|
||||
mozStorageStatementScoper scoper(statement);
|
||||
rv = statement->ExecuteStep(&hasResult);
|
||||
NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasResult, NS_ERROR_FAILURE);
|
||||
pageSize = statement->AsInt32(0);
|
||||
}
|
||||
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,9 +49,18 @@ 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");
|
||||
|
||||
// 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()
|
||||
|
|
Загрузка…
Ссылка в новой задаче