mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1177013 - Don't allow prio messages while dispatching sync messages (r=dvander) (33130e842) - Bug 1177013 - Add-on install test changes (r=mrbkap) (adc25d1d7) - Bug 1177013 - Modernize test_cpows.xul (r=dvander) (77d7a5230) - Bug 1142109 - Process incoming urgent messages before sending (r=dvander) (261ab15a7) - Bug 1177013 - CancelCurrentTransaction IPC support (r=dvander) (ef63723f8) - Bug 1177013 - Fix big IPC comment (r=dvander) (3628041b4) - Bug 1144745 - Scale GTK widgets properly on HiDPI r=karlt (7923df05d) - Bug 1127752 - fixes for using Skia and OMTC with GTK3. r=jrmuizel (1e22d442d) - Bug 1165515 - Part 2: Add MOZ_LOG_TEST. r=froydnj (4a5a1e4be) - Bug 1171833 - Remove PL_DHashTableEnumerator use from nsPropertyTable. r=smaug. (7bcb19947)
This commit is contained in:
@@ -133,30 +133,16 @@ nsPropertyTable::Enumerate(nsPropertyOwner aObject,
|
||||
}
|
||||
}
|
||||
|
||||
struct PropertyEnumeratorData
|
||||
{
|
||||
nsIAtom* mName;
|
||||
NSPropertyFunc mCallBack;
|
||||
void* mData;
|
||||
};
|
||||
|
||||
static PLDHashOperator
|
||||
PropertyEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
|
||||
uint32_t aNumber, void* aArg)
|
||||
{
|
||||
PropertyListMapEntry* entry = static_cast<PropertyListMapEntry*>(aHdr);
|
||||
PropertyEnumeratorData* data = static_cast<PropertyEnumeratorData*>(aArg);
|
||||
data->mCallBack(const_cast<void*>(entry->key), data->mName, entry->value,
|
||||
data->mData);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsPropertyTable::EnumerateAll(NSPropertyFunc aCallBack, void* aData)
|
||||
{
|
||||
for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) {
|
||||
PropertyEnumeratorData data = { prop->mName, aCallBack, aData };
|
||||
PL_DHashTableEnumerate(&prop->mObjectValueMap, PropertyEnumerator, &data);
|
||||
PLDHashTable::Iterator iter(&prop->mObjectValueMap);
|
||||
while (iter.HasMoreEntries()) {
|
||||
auto entry = static_cast<PropertyListMapEntry*>(iter.NextEntry());
|
||||
aCallBack(const_cast<void*>(entry->key), prop->mName, entry->value,
|
||||
aData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,25 +280,17 @@ nsPropertyTable::PropertyList::~PropertyList()
|
||||
{
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
DestroyPropertyEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
|
||||
uint32_t number, void *arg)
|
||||
{
|
||||
nsPropertyTable::PropertyList *propList =
|
||||
static_cast<nsPropertyTable::PropertyList*>(arg);
|
||||
PropertyListMapEntry* entry = static_cast<PropertyListMapEntry*>(hdr);
|
||||
|
||||
propList->mDtorFunc(const_cast<void*>(entry->key), propList->mName,
|
||||
entry->value, propList->mDtorData);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsPropertyTable::PropertyList::Destroy()
|
||||
{
|
||||
// Enumerate any remaining object/value pairs and destroy the value object
|
||||
if (mDtorFunc)
|
||||
PL_DHashTableEnumerate(&mObjectValueMap, DestroyPropertyEnumerator, this);
|
||||
// Enumerate any remaining object/value pairs and destroy the value object.
|
||||
if (mDtorFunc) {
|
||||
PLDHashTable::Iterator iter(&mObjectValueMap);
|
||||
while (iter.HasMoreEntries()) {
|
||||
auto entry = static_cast<PropertyListMapEntry*>(iter.NextEntry());
|
||||
mDtorFunc(const_cast<void*>(entry->key), mName, entry->value, mDtorData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -1,38 +1,40 @@
|
||||
dump('loaded child cpow test\n');
|
||||
|
||||
content.document.title = "Hello, Kitty";
|
||||
const Cu = Components.utils;
|
||||
|
||||
var done_count = 0;
|
||||
var is_remote;
|
||||
|
||||
(function start() {
|
||||
[is_remote] = sendSyncMessage("cpows:is_remote");
|
||||
parent_test();
|
||||
error_reporting_test();
|
||||
dom_test();
|
||||
xray_test();
|
||||
if (typeof Symbol === "function") {
|
||||
symbol_test();
|
||||
[is_remote] = sendRpcMessage("cpows:is_remote");
|
||||
|
||||
var tests = [
|
||||
parent_test,
|
||||
error_reporting_test,
|
||||
dom_test,
|
||||
xray_test,
|
||||
symbol_test,
|
||||
compartment_test,
|
||||
regexp_test,
|
||||
postmessage_test,
|
||||
sync_test,
|
||||
async_test,
|
||||
rpc_test,
|
||||
lifetime_test
|
||||
];
|
||||
|
||||
function go() {
|
||||
if (tests.length == 0) {
|
||||
sendRpcMessage("cpows:done", {});
|
||||
return;
|
||||
}
|
||||
compartment_test();
|
||||
regexp_test();
|
||||
postmessage_test();
|
||||
sync_test();
|
||||
async_test();
|
||||
rpc_test();
|
||||
nested_sync_test();
|
||||
// The sync-ness of this call is important, because otherwise
|
||||
// we tear down the child's document while we are
|
||||
// still in the async test in the parent.
|
||||
// This test races with itself to be the final test.
|
||||
lifetime_test(function() {
|
||||
done_count++;
|
||||
if (done_count == 2)
|
||||
sendSyncMessage("cpows:done", {});
|
||||
|
||||
var test = tests[0];
|
||||
tests.shift();
|
||||
test(function() {
|
||||
go();
|
||||
});
|
||||
}
|
||||
)();
|
||||
|
||||
go();
|
||||
})();
|
||||
|
||||
function ok(condition, message) {
|
||||
dump('condition: ' + condition + ', ' + message + '\n');
|
||||
@@ -69,6 +71,7 @@ function make_object()
|
||||
|
||||
let with_null_proto = Object.create(null);
|
||||
|
||||
content.document.title = "Hello, Kitty";
|
||||
return { "data": o,
|
||||
"throwing": throwing,
|
||||
"document": content.document,
|
||||
@@ -84,7 +87,7 @@ function make_json()
|
||||
return { check: "ok" };
|
||||
}
|
||||
|
||||
function parent_test()
|
||||
function parent_test(finish)
|
||||
{
|
||||
function f(check_func) {
|
||||
let result = check_func(10);
|
||||
@@ -104,38 +107,39 @@ function parent_test()
|
||||
sb.func = func;
|
||||
ok(sb.eval('func()') == 101, "can call parent's function in child");
|
||||
|
||||
done_count++;
|
||||
if (done_count == 2)
|
||||
sendSyncMessage("cpows:done", {});
|
||||
finish();
|
||||
});
|
||||
sendSyncMessage("cpows:parent_test", {}, {func: f});
|
||||
sendRpcMessage("cpows:parent_test", {}, {func: f});
|
||||
}
|
||||
|
||||
function error_reporting_test() {
|
||||
sendSyncMessage("cpows:error_reporting_test", {}, {});
|
||||
function error_reporting_test(finish) {
|
||||
sendRpcMessage("cpows:error_reporting_test", {}, {});
|
||||
finish();
|
||||
}
|
||||
|
||||
function dom_test()
|
||||
function dom_test(finish)
|
||||
{
|
||||
let element = content.document.createElement("div");
|
||||
element.id = "it_works";
|
||||
content.document.body.appendChild(element);
|
||||
|
||||
sendAsyncMessage("cpows:dom_test", {}, {element: element});
|
||||
sendRpcMessage("cpows:dom_test", {}, {element: element});
|
||||
Components.utils.schedulePreciseGC(function() {
|
||||
sendSyncMessage("cpows:dom_test_after_gc");
|
||||
sendRpcMessage("cpows:dom_test_after_gc");
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
function xray_test()
|
||||
function xray_test(finish)
|
||||
{
|
||||
let element = content.document.createElement("div");
|
||||
element.wrappedJSObject.foo = "hello";
|
||||
|
||||
sendSyncMessage("cpows:xray_test", {}, {element: element});
|
||||
sendRpcMessage("cpows:xray_test", {}, {element: element});
|
||||
finish();
|
||||
}
|
||||
|
||||
function symbol_test()
|
||||
function symbol_test(finish)
|
||||
{
|
||||
let iterator = Symbol.iterator;
|
||||
let named = Symbol.for("cpow-test");
|
||||
@@ -145,16 +149,18 @@ function symbol_test()
|
||||
[named]: named,
|
||||
};
|
||||
let test = ['a'];
|
||||
sendSyncMessage("cpows:symbol_test", {}, {object: object, test: test});
|
||||
sendRpcMessage("cpows:symbol_test", {}, {object: object, test: test});
|
||||
finish();
|
||||
}
|
||||
|
||||
// Parent->Child references should go X->parent.privilegedJunkScope->child.privilegedJunkScope->Y
|
||||
// Child->Parent references should go X->child.privilegedJunkScope->parent.unprivilegedJunkScope->Y
|
||||
function compartment_test()
|
||||
function compartment_test(finish)
|
||||
{
|
||||
// This test primarily checks various compartment invariants for CPOWs, and
|
||||
// doesn't make sense to run in-process.
|
||||
if (!is_remote) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -178,41 +184,47 @@ function compartment_test()
|
||||
|
||||
return results;
|
||||
}
|
||||
sendSyncMessage("cpows:compartment_test", {}, { getUnprivilegedObject: sb.getUnprivilegedObject,
|
||||
testParentObject: testParentObject });
|
||||
sendRpcMessage("cpows:compartment_test", {}, { getUnprivilegedObject: sb.getUnprivilegedObject,
|
||||
testParentObject: testParentObject });
|
||||
finish();
|
||||
}
|
||||
|
||||
function regexp_test()
|
||||
function regexp_test(finish)
|
||||
{
|
||||
sendSyncMessage("cpows:regexp_test", {}, { regexp: /myRegExp/g });
|
||||
sendRpcMessage("cpows:regexp_test", {}, { regexp: /myRegExp/g });
|
||||
finish();
|
||||
}
|
||||
|
||||
function postmessage_test()
|
||||
function postmessage_test(finish)
|
||||
{
|
||||
sendSyncMessage("cpows:postmessage_test", {}, { win: content.window });
|
||||
sendRpcMessage("cpows:postmessage_test", {}, { win: content.window });
|
||||
finish();
|
||||
}
|
||||
|
||||
function sync_test()
|
||||
function sync_test(finish)
|
||||
{
|
||||
dump('beginning cpow sync test\n');
|
||||
sync_obj = make_object();
|
||||
sendSyncMessage("cpows:sync",
|
||||
sendRpcMessage("cpows:sync",
|
||||
make_json(),
|
||||
make_object());
|
||||
finish();
|
||||
}
|
||||
|
||||
function async_test()
|
||||
function async_test(finish)
|
||||
{
|
||||
dump('beginning cpow async test\n');
|
||||
async_obj = make_object();
|
||||
sendAsyncMessage("cpows:async",
|
||||
make_json(),
|
||||
async_obj);
|
||||
|
||||
addMessageListener("cpows:async_done", finish);
|
||||
}
|
||||
|
||||
var rpc_obj;
|
||||
|
||||
function rpc_test()
|
||||
function rpc_test(finish)
|
||||
{
|
||||
dump('beginning cpow rpc test\n');
|
||||
rpc_obj = make_object();
|
||||
@@ -223,26 +235,7 @@ function rpc_test()
|
||||
sendRpcMessage("cpows:rpc",
|
||||
make_json(),
|
||||
rpc_obj);
|
||||
}
|
||||
|
||||
function nested_sync_test()
|
||||
{
|
||||
dump('beginning cpow nested sync test\n');
|
||||
sync_obj = make_object();
|
||||
sync_obj.data.reenter = function () {
|
||||
let caught = false;
|
||||
try {
|
||||
sendSyncMessage("cpows:reenter_sync", { }, { });
|
||||
} catch (e) {
|
||||
caught = true;
|
||||
}
|
||||
if (!ok(caught, "should not allow nested sync"))
|
||||
return "fail";
|
||||
return "ok";
|
||||
}
|
||||
sendSyncMessage("cpows:nested_sync",
|
||||
make_json(),
|
||||
rpc_obj);
|
||||
finish();
|
||||
}
|
||||
|
||||
function lifetime_test(finish)
|
||||
@@ -257,7 +250,7 @@ function lifetime_test(finish)
|
||||
|
||||
dump("beginning lifetime test\n");
|
||||
var obj = {"will_die": {"f": 1}};
|
||||
let [result] = sendSyncMessage("cpows:lifetime_test_1", {}, {obj: obj});
|
||||
let [result] = sendRpcMessage("cpows:lifetime_test_1", {}, {obj: obj});
|
||||
ok(result == 10, "got sync result");
|
||||
ok(obj.wont_die.f == 2, "got reverse CPOW");
|
||||
obj.will_die = null;
|
||||
@@ -266,6 +259,6 @@ function lifetime_test(finish)
|
||||
ok(obj.wont_die.f == 2, "reverse CPOW still works");
|
||||
finish();
|
||||
});
|
||||
sendSyncMessage("cpows:lifetime_test_2");
|
||||
sendRpcMessage("cpows:lifetime_test_2");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"getOwnPropertyDescriptor.value works");
|
||||
let obj = new data.ctor();
|
||||
ok(obj.a === 3, "constructor call");
|
||||
ok(document.title === "Hello, Kitty", "document node");
|
||||
is(document.title, "Hello, Kitty", "document node");
|
||||
is(typeof document.cookie, "string", "can get document.cookie");
|
||||
is(typeof document.defaultView.navigator.userAgent, "string", "can get navigator.userAgent");
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
|
||||
function recvAsyncMessage(message) {
|
||||
testCpowMessage(message);
|
||||
savedMM.sendAsyncMessage("cpows:async_done");
|
||||
}
|
||||
|
||||
function recvSyncMessage(message) {
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
|
||||
#include "2D.h"
|
||||
|
||||
#ifdef MOZ_X11
|
||||
#include <X11/extensions/Xrender.h>
|
||||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
struct _cairo;
|
||||
typedef struct _cairo cairo_t;
|
||||
|
||||
@@ -69,6 +74,69 @@ private:
|
||||
DrawTarget *mDT;
|
||||
};
|
||||
|
||||
#ifdef MOZ_X11
|
||||
/* This is a helper class that let's you borrow an Xlib drawable from
|
||||
* a DrawTarget. This is used for drawing themed widgets.
|
||||
*
|
||||
* Callers should check the Xlib drawable after constructing the object
|
||||
* to see if it succeeded. The DrawTarget should not be used while
|
||||
* the drawable is borrowed. */
|
||||
class BorrowedXlibDrawable
|
||||
{
|
||||
public:
|
||||
BorrowedXlibDrawable()
|
||||
: mDT(nullptr),
|
||||
mDisplay(nullptr),
|
||||
mDrawable(None),
|
||||
mScreen(nullptr),
|
||||
mVisual(nullptr),
|
||||
mXRenderFormat(nullptr)
|
||||
{}
|
||||
|
||||
explicit BorrowedXlibDrawable(DrawTarget *aDT)
|
||||
: mDT(nullptr),
|
||||
mDisplay(nullptr),
|
||||
mDrawable(None),
|
||||
mScreen(nullptr),
|
||||
mVisual(nullptr),
|
||||
mXRenderFormat(nullptr)
|
||||
{
|
||||
Init(aDT);
|
||||
}
|
||||
|
||||
// We can optionally Init after construction in
|
||||
// case we don't know what the DT will be at construction
|
||||
// time.
|
||||
bool Init(DrawTarget *aDT);
|
||||
|
||||
// The caller needs to call Finish if drawable is non-zero when
|
||||
// they are done with the context. This is currently explicit
|
||||
// instead of happening implicitly in the destructor to make
|
||||
// what's happening in the caller more clear. It also
|
||||
// let's you resume using the DrawTarget in the same scope.
|
||||
void Finish();
|
||||
|
||||
~BorrowedXlibDrawable() {
|
||||
MOZ_ASSERT(!mDrawable);
|
||||
}
|
||||
|
||||
Display *GetDisplay() const { return mDisplay; }
|
||||
Drawable GetDrawable() const { return mDrawable; }
|
||||
Screen *GetScreen() const { return mScreen; }
|
||||
Visual *GetVisual() const { return mVisual; }
|
||||
|
||||
XRenderPictFormat* GetXRenderFormat() const { return mXRenderFormat; }
|
||||
|
||||
private:
|
||||
DrawTarget *mDT;
|
||||
Display *mDisplay;
|
||||
Drawable mDrawable;
|
||||
Screen *mScreen;
|
||||
Visual *mVisual;
|
||||
XRenderPictFormat *mXRenderFormat;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
/* This is a helper class that let's you borrow a CGContextRef from a
|
||||
* DrawTargetCG. This is used for drawing themed widgets.
|
||||
|
||||
@@ -1715,5 +1715,50 @@ BorrowedCairoContext::ReturnCairoContextToDrawTarget(DrawTarget* aDT,
|
||||
cairoDT->mContext = aCairo;
|
||||
}
|
||||
|
||||
#ifdef MOZ_X11
|
||||
bool
|
||||
BorrowedXlibDrawable::Init(DrawTarget* aDT)
|
||||
{
|
||||
MOZ_ASSERT(aDT, "Caller should check for nullptr");
|
||||
MOZ_ASSERT(!mDT, "Can't initialize twice!");
|
||||
mDT = aDT;
|
||||
mDrawable = None;
|
||||
|
||||
#ifdef CAIRO_HAS_XLIB_SURFACE
|
||||
if (aDT->GetBackendType() != BackendType::CAIRO ||
|
||||
aDT->IsDualDrawTarget() ||
|
||||
aDT->IsTiledDrawTarget()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DrawTargetCairo* cairoDT = static_cast<DrawTargetCairo*>(aDT);
|
||||
cairo_surface_t* surf = cairoDT->mSurface;
|
||||
if (cairo_surface_get_type(surf) != CAIRO_SURFACE_TYPE_XLIB) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cairoDT->WillChange();
|
||||
|
||||
mDisplay = cairo_xlib_surface_get_display(surf);
|
||||
mDrawable = cairo_xlib_surface_get_drawable(surf);
|
||||
mScreen = cairo_xlib_surface_get_screen(surf);
|
||||
mVisual = cairo_xlib_surface_get_visual(surf);
|
||||
mXRenderFormat = cairo_xlib_surface_get_xrender_format(surf);
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
BorrowedXlibDrawable::Finish()
|
||||
{
|
||||
if (mDrawable) {
|
||||
mDrawable = None;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -54,6 +54,7 @@ class DrawTargetCairo final : public DrawTarget
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetCairo, override)
|
||||
friend class BorrowedCairoContext;
|
||||
friend class BorrowedXlibDrawable;
|
||||
|
||||
DrawTargetCairo();
|
||||
virtual ~DrawTargetCairo();
|
||||
|
||||
@@ -147,6 +147,35 @@ DrawTargetSkia::Snapshot()
|
||||
return snapshot.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize,
|
||||
int32_t* aStride, SurfaceFormat* aFormat)
|
||||
{
|
||||
const SkBitmap &bitmap = mCanvas->getDevice()->accessBitmap(false);
|
||||
if (!bitmap.lockPixelsAreWritable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MarkChanged();
|
||||
|
||||
bitmap.lockPixels();
|
||||
*aData = reinterpret_cast<uint8_t*>(bitmap.getPixels());
|
||||
*aSize = IntSize(bitmap.width(), bitmap.height());
|
||||
*aStride = int32_t(bitmap.rowBytes());
|
||||
*aFormat = SkiaColorTypeToGfxFormat(bitmap.colorType());
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetSkia::ReleaseBits(uint8_t* aData)
|
||||
{
|
||||
const SkBitmap &bitmap = mCanvas->getDevice()->accessBitmap(false);
|
||||
MOZ_ASSERT(bitmap.lockPixelsAreWritable());
|
||||
|
||||
bitmap.unlockPixels();
|
||||
bitmap.notifyPixelsChanged();
|
||||
}
|
||||
|
||||
static void
|
||||
SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, TempBitmap& aTmpBitmap,
|
||||
Float aAlpha = 1.0)
|
||||
@@ -688,10 +717,10 @@ DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurfa
|
||||
cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface);
|
||||
return MakeAndAddRef<SourceSurfaceCairo>(surf, aSurface.mSize, aSurface.mFormat);
|
||||
#if USE_SKIA_GPU
|
||||
} else if (aSurface.mType == NativeSurfaceType::OPENGL_TEXTURE) {
|
||||
} else if (aSurface.mType == NativeSurfaceType::OPENGL_TEXTURE && UsingSkiaGPU()) {
|
||||
RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
|
||||
unsigned int texture = (unsigned int)((uintptr_t)aSurface.mSurface);
|
||||
if (UsingSkiaGPU() && newSurf->InitFromTexture((DrawTargetSkia*)this, texture, aSurface.mSize, aSurface.mFormat)) {
|
||||
if (newSurf->InitFromTexture((DrawTargetSkia*)this, texture, aSurface.mSize, aSurface.mFormat)) {
|
||||
return newSurf.forget();
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
@@ -36,6 +36,9 @@ public:
|
||||
virtual BackendType GetBackendType() const override { return BackendType::SKIA; }
|
||||
virtual already_AddRefed<SourceSurface> Snapshot() override;
|
||||
virtual IntSize GetSize() override { return mSize; }
|
||||
virtual bool LockBits(uint8_t** aData, IntSize* aSize,
|
||||
int32_t* aStride, SurfaceFormat* aFormat) override;
|
||||
virtual void ReleaseBits(uint8_t* aData) override;
|
||||
virtual void Flush() override;
|
||||
virtual void DrawSurface(SourceSurface *aSurface,
|
||||
const Rect &aDest,
|
||||
|
||||
@@ -79,6 +79,7 @@ if CONFIG['MOZ_ENABLE_SKIA']:
|
||||
'image_operations.cpp', # Uses _USE_MATH_DEFINES
|
||||
]
|
||||
EXPORTS.mozilla.gfx += [
|
||||
'HelpersCairo.h',
|
||||
'HelpersSkia.h',
|
||||
]
|
||||
|
||||
|
||||
@@ -522,7 +522,7 @@ BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion,
|
||||
RefPtr<CompositingRenderTarget> target = CreateRenderTarget(mInvalidRect, INIT_MODE_CLEAR);
|
||||
if (!target) {
|
||||
if (!mTarget) {
|
||||
mWidget->EndRemoteDrawing();
|
||||
mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -584,7 +584,7 @@ BasicCompositor::EndFrame()
|
||||
IntPoint(r->x - offset.x, r->y - offset.y));
|
||||
}
|
||||
if (!mTarget) {
|
||||
mWidget->EndRemoteDrawing();
|
||||
mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
|
||||
}
|
||||
|
||||
mDrawTarget = nullptr;
|
||||
|
||||
@@ -715,6 +715,12 @@ public:
|
||||
return This();
|
||||
}
|
||||
|
||||
Derived& ScaleInverseRoundOut (float aXScale, float aYScale)
|
||||
{
|
||||
mImpl.ScaleInverseRoundOut(aXScale, aYScale);
|
||||
return This();
|
||||
}
|
||||
|
||||
Derived& Transform (const gfx3DMatrix &aTransform)
|
||||
{
|
||||
mImpl.Transform(aTransform);
|
||||
|
||||
@@ -619,6 +619,23 @@ gfxContext::GetClipExtents()
|
||||
return ThebesRect(rect);
|
||||
}
|
||||
|
||||
bool
|
||||
gfxContext::HasComplexClip() const
|
||||
{
|
||||
for (int i = mStateStack.Length() - 1; i >= 0; i--) {
|
||||
for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
|
||||
const AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
|
||||
if (clip.path || !clip.transform.IsRectilinear()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (mStateStack[i].clipWasReset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
gfxContext::ClipContainsRect(const gfxRect& aRect)
|
||||
{
|
||||
|
||||
@@ -445,6 +445,11 @@ public:
|
||||
*/
|
||||
gfxRect GetClipExtents();
|
||||
|
||||
/**
|
||||
* Whether the current clip is not a simple rectangle.
|
||||
*/
|
||||
bool HasComplexClip() const;
|
||||
|
||||
/**
|
||||
* Returns true if the given rectangle is fully contained in the current clip.
|
||||
* This is conservative; it may return false even when the given rectangle is
|
||||
|
||||
+138
-48
@@ -32,31 +32,31 @@
|
||||
* Terminology: To dispatch a message Foo is to run the RecvFoo code for
|
||||
* it. This is also called "handling" the message.
|
||||
*
|
||||
* Sync messages have priorities while async and intr messages always have
|
||||
* Sync and async messages have priorities while intr messages always have
|
||||
* normal priority. The three possible priorities are normal, high, and urgent.
|
||||
* The intended uses of these priorities are:
|
||||
* NORMAL - most messages.
|
||||
* HIGH - CPOW-related messages, which can go in either direction.
|
||||
* URGENT - messages where we don't want to dispatch
|
||||
* incoming CPOWs while waiting for the response.
|
||||
* Async messages cannot have HIGH priority.
|
||||
*
|
||||
* To avoid jank, the parent process is not allowed to send sync messages of
|
||||
* normal priority. The parent also is not allowed to send urgent messages at
|
||||
* all. When a process is waiting for a response to a sync message M0, it will
|
||||
* dispatch an incoming message M if:
|
||||
* normal priority. When a process is waiting for a response to a sync message
|
||||
* M0, it will dispatch an incoming message M if:
|
||||
* 1. M has a higher priority than M0, or
|
||||
* 2. if M has the same priority as M0 and we're in the child, or
|
||||
* 3. if M has the same priority as M0 and it was sent by the other side
|
||||
while dispatching M0 (nesting).
|
||||
* while dispatching M0 (nesting).
|
||||
* The idea is that higher priority messages should take precendence, and we
|
||||
* also want to allow nesting. The purpose of rule 2 is to handle a race where
|
||||
* both processes send to each other simultaneously. In this case, we resolve
|
||||
* the race in favor of the parent (so the child dispatches first).
|
||||
*
|
||||
* Sync messages satisfy the following properties:
|
||||
* Messages satisfy the following properties:
|
||||
* A. When waiting for a response to a sync message, we won't dispatch any
|
||||
* messages of lower priority.
|
||||
* B. Sync messages of the same priority will be dispatched roughly in the
|
||||
* B. Messages of the same priority will be dispatched roughly in the
|
||||
* order they were sent. The exception is when the parent and child send
|
||||
* sync messages to each other simulataneously. In this case, the parent's
|
||||
* message is dispatched first. While it is dispatched, the child may send
|
||||
@@ -65,6 +65,11 @@
|
||||
* because we pretend that the child's original message wasn't sent until
|
||||
* after the parent's message is finished being dispatched.
|
||||
*
|
||||
* When waiting for a sync message reply, we dispatch an async message only if
|
||||
* it has URGENT priority. Normally URGENT async messages are sent only from the
|
||||
* child. However, the parent can send URGENT async messages when it is creating
|
||||
* a bridged protocol.
|
||||
*
|
||||
* Intr messages are blocking but not prioritized. While waiting for an intr
|
||||
* response, all incoming messages are dispatched until a response is
|
||||
* received. Intr messages also can be nested. When two intr messages race with
|
||||
@@ -558,17 +563,21 @@ MessageChannel::MaybeInterceptSpecialIOMessage(const Message& aMsg)
|
||||
AssertLinkThread();
|
||||
mMonitor->AssertCurrentThreadOwns();
|
||||
|
||||
if (MSG_ROUTING_NONE == aMsg.routing_id() &&
|
||||
GOODBYE_MESSAGE_TYPE == aMsg.type())
|
||||
{
|
||||
// :TODO: Sort out Close() on this side racing with Close() on the
|
||||
// other side
|
||||
mChannelState = ChannelClosing;
|
||||
if (LoggingEnabled()) {
|
||||
printf("NOTE: %s process received `Goodbye', closing down\n",
|
||||
(mSide == ChildSide) ? "child" : "parent");
|
||||
if (MSG_ROUTING_NONE == aMsg.routing_id()) {
|
||||
if (GOODBYE_MESSAGE_TYPE == aMsg.type()) {
|
||||
// :TODO: Sort out Close() on this side racing with Close() on the
|
||||
// other side
|
||||
mChannelState = ChannelClosing;
|
||||
if (LoggingEnabled()) {
|
||||
printf("NOTE: %s process received `Goodbye', closing down\n",
|
||||
(mSide == ChildSide) ? "child" : "parent");
|
||||
}
|
||||
return true;
|
||||
} else if (CANCEL_MESSAGE_TYPE == aMsg.type()) {
|
||||
CancelCurrentTransactionInternal();
|
||||
NotifyWorkerThread();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -630,6 +639,7 @@ MessageChannel::OnMessageReceivedFromLink(const Message& aMsg)
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aMsg.transaction_id() == mCurrentTransaction);
|
||||
MOZ_ASSERT(AwaitingSyncReply());
|
||||
MOZ_ASSERT(!mRecvd);
|
||||
|
||||
@@ -708,6 +718,33 @@ MessageChannel::OnMessageReceivedFromLink(const Message& aMsg)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MessageChannel::ProcessPendingRequests()
|
||||
{
|
||||
// Loop until there aren't any more priority messages to process.
|
||||
for (;;) {
|
||||
mozilla::Vector<Message> toProcess;
|
||||
|
||||
for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); ) {
|
||||
Message &msg = *it;
|
||||
if (!ShouldDeferMessage(msg)) {
|
||||
toProcess.append(Move(msg));
|
||||
it = mPending.erase(it);
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
if (toProcess.empty())
|
||||
break;
|
||||
|
||||
// Processing these messages could result in more messages, so we
|
||||
// loop around to check for more afterwards.
|
||||
for (auto it = toProcess.begin(); it != toProcess.end(); it++)
|
||||
ProcessPendingRequest(*it);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MessageChannel::Send(Message* aMsg, Message* aReply)
|
||||
{
|
||||
@@ -737,10 +774,18 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_NORMAL &&
|
||||
aMsg->priority() > IPC::Message::PRIORITY_NORMAL)
|
||||
{
|
||||
// Don't allow sending CPOWs while we're dispatching a sync message.
|
||||
// If you want to do that, use sendRpcMessage instead.
|
||||
return false;
|
||||
}
|
||||
|
||||
IPC_ASSERT(aMsg->is_sync(), "can only Send() sync messages here");
|
||||
IPC_ASSERT(aMsg->priority() >= DispatchingSyncMessagePriority(),
|
||||
"can't send sync message of a lesser priority than what's being dispatched");
|
||||
IPC_ASSERT(mAwaitingSyncReplyPriority <= aMsg->priority(),
|
||||
IPC_ASSERT(AwaitingSyncReplyPriority() <= aMsg->priority(),
|
||||
"nested sync message sends must be of increasing priority");
|
||||
|
||||
IPC_ASSERT(DispatchingSyncMessagePriority() != IPC::Message::PRIORITY_URGENT,
|
||||
@@ -768,30 +813,19 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
|
||||
int32_t transaction = mCurrentTransaction;
|
||||
msg->set_transaction_id(transaction);
|
||||
|
||||
ProcessPendingRequests();
|
||||
if (mCurrentTransaction != transaction) {
|
||||
// Transaction was canceled when dispatching.
|
||||
return false;
|
||||
}
|
||||
|
||||
mLink->SendMessage(msg.forget());
|
||||
|
||||
while (true) {
|
||||
// Loop until there aren't any more priority messages to process.
|
||||
for (;;) {
|
||||
mozilla::Vector<Message> toProcess;
|
||||
|
||||
for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); ) {
|
||||
Message &msg = *it;
|
||||
if (!ShouldDeferMessage(msg)) {
|
||||
toProcess.append(Move(msg));
|
||||
it = mPending.erase(it);
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
if (toProcess.empty())
|
||||
break;
|
||||
|
||||
// Processing these messages could result in more messages, so we
|
||||
// loop around to check for more afterwards.
|
||||
for (auto it = toProcess.begin(); it != toProcess.end(); it++)
|
||||
ProcessPendingRequest(*it);
|
||||
ProcessPendingRequests();
|
||||
if (mCurrentTransaction != transaction) {
|
||||
// Transaction was canceled when dispatching.
|
||||
return false;
|
||||
}
|
||||
|
||||
// See if we've received a reply.
|
||||
@@ -813,6 +847,11 @@ MessageChannel::Send(Message* aMsg, Message* aReply)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mCurrentTransaction != transaction) {
|
||||
// Transaction was canceled by other side.
|
||||
return false;
|
||||
}
|
||||
|
||||
// We only time out a message if it initiated a new transaction (i.e.,
|
||||
// if neither side has any other message Sends on the stack).
|
||||
bool canTimeOut = transaction == seqno;
|
||||
@@ -1144,15 +1183,26 @@ MessageChannel::DispatchMessage(const Message &aMsg)
|
||||
|
||||
{
|
||||
AutoEnterTransaction transaction(this, aMsg);
|
||||
MonitorAutoUnlock unlock(*mMonitor);
|
||||
CxxStackFrame frame(*this, IN_MESSAGE, &aMsg);
|
||||
|
||||
if (aMsg.is_sync())
|
||||
DispatchSyncMessage(aMsg, *getter_Transfers(reply));
|
||||
else if (aMsg.is_interrupt())
|
||||
DispatchInterruptMessage(aMsg, 0);
|
||||
else
|
||||
DispatchAsyncMessage(aMsg);
|
||||
int id = aMsg.transaction_id();
|
||||
MOZ_ASSERT_IF(aMsg.is_sync(), id == mCurrentTransaction);
|
||||
|
||||
{
|
||||
MonitorAutoUnlock unlock(*mMonitor);
|
||||
CxxStackFrame frame(*this, IN_MESSAGE, &aMsg);
|
||||
|
||||
if (aMsg.is_sync())
|
||||
DispatchSyncMessage(aMsg, *getter_Transfers(reply));
|
||||
else if (aMsg.is_interrupt())
|
||||
DispatchInterruptMessage(aMsg, 0);
|
||||
else
|
||||
DispatchAsyncMessage(aMsg);
|
||||
}
|
||||
|
||||
if (mCurrentTransaction != id) {
|
||||
// The transaction has been canceled. Don't send a reply.
|
||||
reply = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (reply && ChannelConnected == mChannelState) {
|
||||
@@ -1174,7 +1224,7 @@ MessageChannel::DispatchSyncMessage(const Message& aMsg, Message*& aReply)
|
||||
MOZ_ASSERT_IF(prio > IPC::Message::PRIORITY_NORMAL, NS_IsMainThread());
|
||||
MaybeScriptBlocker scriptBlocker(this, prio > IPC::Message::PRIORITY_NORMAL);
|
||||
|
||||
IPC_ASSERT(prio >= mDispatchingSyncMessagePriority,
|
||||
IPC_ASSERT(prio >= DispatchingSyncMessagePriority(),
|
||||
"priority inversion while dispatching sync message");
|
||||
IPC_ASSERT(prio >= mAwaitingSyncReplyPriority,
|
||||
"dispatching a message of lower priority while waiting for a response");
|
||||
@@ -1860,6 +1910,46 @@ MessageChannel::GetTopmostMessageRoutingId() const
|
||||
return frame.GetRoutingId();
|
||||
}
|
||||
|
||||
class CancelMessage : public IPC::Message
|
||||
{
|
||||
public:
|
||||
CancelMessage() :
|
||||
IPC::Message(MSG_ROUTING_NONE, CANCEL_MESSAGE_TYPE, PRIORITY_NORMAL)
|
||||
{
|
||||
}
|
||||
static bool Read(const Message* msg) {
|
||||
return true;
|
||||
}
|
||||
void Log(const std::string& aPrefix, FILE* aOutf) const {
|
||||
fputs("(special `Cancel' message)", aOutf);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
MessageChannel::CancelCurrentTransactionInternal()
|
||||
{
|
||||
// When we cancel a transaction, we need to behave as if there's no longer
|
||||
// any IPC on the stack. Anything we were dispatching or sending will get
|
||||
// canceled. Consequently, we have to update the state variables below.
|
||||
//
|
||||
// We also need to ensure that when any IPC functions on the stack return,
|
||||
// they don't reset these values using an RAII class like AutoSetValue. To
|
||||
// avoid that, these RAII classes check if the variable they set has been
|
||||
// tampered with (by us). If so, they don't reset the variable to the old
|
||||
// value.
|
||||
|
||||
MOZ_ASSERT(!mCurrentTransaction);
|
||||
mCurrentTransaction = 0;
|
||||
}
|
||||
|
||||
void
|
||||
MessageChannel::CancelCurrentTransaction()
|
||||
{
|
||||
MonitorAutoLock lock(*mMonitor);
|
||||
CancelCurrentTransactionInternal();
|
||||
mLink->SendMessage(new CancelMessage());
|
||||
}
|
||||
|
||||
bool
|
||||
ParentProcessIsBlocked()
|
||||
{
|
||||
|
||||
@@ -135,6 +135,8 @@ class MessageChannel : HasResultCodes
|
||||
return !mCxxStackFrames.empty();
|
||||
}
|
||||
|
||||
void CancelCurrentTransaction();
|
||||
|
||||
/**
|
||||
* This function is used by hang annotation code to determine which IPDL
|
||||
* actor is highest in the call stack at the time of the hang. It should
|
||||
@@ -227,6 +229,7 @@ class MessageChannel : HasResultCodes
|
||||
bool InterruptEventOccurred();
|
||||
bool HasPendingEvents();
|
||||
|
||||
void ProcessPendingRequests();
|
||||
bool ProcessPendingRequest(const Message &aUrgent);
|
||||
|
||||
void MaybeUndeferIncall();
|
||||
@@ -264,6 +267,8 @@ class MessageChannel : HasResultCodes
|
||||
|
||||
bool ShouldContinueFromTimeout();
|
||||
|
||||
void CancelCurrentTransactionInternal();
|
||||
|
||||
// The "remote view of stack depth" can be different than the
|
||||
// actual stack depth when there are out-of-turn replies. When we
|
||||
// receive one, our actual Interrupt stack depth doesn't decrease, but
|
||||
@@ -545,17 +550,21 @@ class MessageChannel : HasResultCodes
|
||||
|
||||
class AutoEnterTransaction
|
||||
{
|
||||
public:
|
||||
public:
|
||||
explicit AutoEnterTransaction(MessageChannel *aChan, int32_t aMsgSeqno)
|
||||
: mChan(aChan),
|
||||
mNewTransaction(0),
|
||||
mOldTransaction(mChan->mCurrentTransaction)
|
||||
{
|
||||
mChan->mMonitor->AssertCurrentThreadOwns();
|
||||
if (mChan->mCurrentTransaction == 0)
|
||||
if (mChan->mCurrentTransaction == 0) {
|
||||
mNewTransaction = aMsgSeqno;
|
||||
mChan->mCurrentTransaction = aMsgSeqno;
|
||||
}
|
||||
}
|
||||
explicit AutoEnterTransaction(MessageChannel *aChan, const Message &aMessage)
|
||||
: mChan(aChan),
|
||||
mNewTransaction(aMessage.transaction_id()),
|
||||
mOldTransaction(mChan->mCurrentTransaction)
|
||||
{
|
||||
mChan->mMonitor->AssertCurrentThreadOwns();
|
||||
@@ -569,12 +578,14 @@ class MessageChannel : HasResultCodes
|
||||
}
|
||||
~AutoEnterTransaction() {
|
||||
mChan->mMonitor->AssertCurrentThreadOwns();
|
||||
mChan->mCurrentTransaction = mOldTransaction;
|
||||
if (mChan->mCurrentTransaction == mNewTransaction) {
|
||||
mChan->mCurrentTransaction = mOldTransaction;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
MessageChannel *mChan;
|
||||
int32_t mOldTransaction;
|
||||
int32_t mNewTransaction, mOldTransaction;
|
||||
};
|
||||
|
||||
// If a sync message times out, we store its sequence number here. Any
|
||||
|
||||
@@ -38,10 +38,11 @@ namespace {
|
||||
// protocol 0. Oops! We can get away with this until protocol 0
|
||||
// starts approaching its 65,536th message.
|
||||
enum {
|
||||
CHANNEL_OPENED_MESSAGE_TYPE = kuint16max - 5,
|
||||
SHMEM_DESTROYED_MESSAGE_TYPE = kuint16max - 4,
|
||||
SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 3,
|
||||
GOODBYE_MESSAGE_TYPE = kuint16max - 2
|
||||
CHANNEL_OPENED_MESSAGE_TYPE = kuint16max - 6,
|
||||
SHMEM_DESTROYED_MESSAGE_TYPE = kuint16max - 5,
|
||||
SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 4,
|
||||
GOODBYE_MESSAGE_TYPE = kuint16max - 3,
|
||||
CANCEL_MESSAGE_TYPE = kuint16max - 2,
|
||||
|
||||
// kuint16max - 1 is used by ipc_channel.h.
|
||||
};
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
prio(normal upto high) sync protocol PTestCancel
|
||||
{
|
||||
// Test1
|
||||
child:
|
||||
prio(high) sync Test1_1();
|
||||
parent:
|
||||
async Done1();
|
||||
|
||||
// Test2
|
||||
child:
|
||||
async Start2();
|
||||
prio(high) sync Test2_2();
|
||||
parent:
|
||||
sync Test2_1();
|
||||
|
||||
// Test3
|
||||
child:
|
||||
prio(high) sync Test3_1();
|
||||
parent:
|
||||
async Start3();
|
||||
prio(high) sync Test3_2();
|
||||
|
||||
parent:
|
||||
async Done();
|
||||
|
||||
child:
|
||||
prio(high) sync CheckChild() returns (uint32_t reply);
|
||||
parent:
|
||||
prio(high) sync CheckParent() returns (uint32_t reply);
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,175 @@
|
||||
#include "TestCancel.h"
|
||||
|
||||
#include "IPDLUnitTests.h" // fail etc.
|
||||
|
||||
template<>
|
||||
struct RunnableMethodTraits<mozilla::_ipdltest::TestCancelParent>
|
||||
{
|
||||
static void RetainCallee(mozilla::_ipdltest::TestCancelParent* obj) { }
|
||||
static void ReleaseCallee(mozilla::_ipdltest::TestCancelParent* obj) { }
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// parent
|
||||
|
||||
TestCancelParent::TestCancelParent()
|
||||
{
|
||||
MOZ_COUNT_CTOR(TestCancelParent);
|
||||
}
|
||||
|
||||
TestCancelParent::~TestCancelParent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(TestCancelParent);
|
||||
}
|
||||
|
||||
void
|
||||
TestCancelParent::Main()
|
||||
{
|
||||
if (SendTest1_1())
|
||||
fail("sending Test1_1");
|
||||
|
||||
uint32_t value = 0;
|
||||
if (!SendCheckChild(&value))
|
||||
fail("Test1 CheckChild");
|
||||
|
||||
if (value != 12)
|
||||
fail("Test1 CheckChild reply");
|
||||
}
|
||||
|
||||
bool
|
||||
TestCancelParent::RecvDone1()
|
||||
{
|
||||
if (!SendStart2())
|
||||
fail("sending Start2");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TestCancelParent::RecvTest2_1()
|
||||
{
|
||||
if (SendTest2_2())
|
||||
fail("sending Test2_2");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TestCancelParent::RecvStart3()
|
||||
{
|
||||
if (SendTest3_1())
|
||||
fail("sending Test3_1");
|
||||
|
||||
uint32_t value = 0;
|
||||
if (!SendCheckChild(&value))
|
||||
fail("Test1 CheckChild");
|
||||
|
||||
if (value != 12)
|
||||
fail("Test1 CheckChild reply");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TestCancelParent::RecvTest3_2()
|
||||
{
|
||||
GetIPCChannel()->CancelCurrentTransaction();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TestCancelParent::RecvDone()
|
||||
{
|
||||
MessageLoop::current()->PostTask(
|
||||
FROM_HERE, NewRunnableMethod(this, &TestCancelParent::Close));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TestCancelParent::RecvCheckParent(uint32_t *reply)
|
||||
{
|
||||
*reply = 12;
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// child
|
||||
|
||||
bool
|
||||
TestCancelChild::RecvTest1_1()
|
||||
{
|
||||
GetIPCChannel()->CancelCurrentTransaction();
|
||||
|
||||
uint32_t value = 0;
|
||||
if (!SendCheckParent(&value))
|
||||
fail("Test1 CheckParent");
|
||||
|
||||
if (value != 12)
|
||||
fail("Test1 CheckParent reply");
|
||||
|
||||
if (!SendDone1())
|
||||
fail("Test1 CheckParent");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TestCancelChild::RecvStart2()
|
||||
{
|
||||
if (!SendTest2_1())
|
||||
fail("sending Test2_1");
|
||||
|
||||
if (!SendStart3())
|
||||
fail("sending Start3");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TestCancelChild::RecvTest2_2()
|
||||
{
|
||||
GetIPCChannel()->CancelCurrentTransaction();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TestCancelChild::RecvTest3_1()
|
||||
{
|
||||
if (SendTest3_2())
|
||||
fail("sending Test3_2");
|
||||
|
||||
uint32_t value = 0;
|
||||
if (!SendCheckParent(&value))
|
||||
fail("Test1 CheckParent");
|
||||
|
||||
if (value != 12)
|
||||
fail("Test1 CheckParent reply");
|
||||
|
||||
if (!SendDone())
|
||||
fail("sending Done");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TestCancelChild::RecvCheckChild(uint32_t *reply)
|
||||
{
|
||||
*reply = 12;
|
||||
return true;
|
||||
}
|
||||
|
||||
TestCancelChild::TestCancelChild()
|
||||
{
|
||||
MOZ_COUNT_CTOR(TestCancelChild);
|
||||
}
|
||||
|
||||
TestCancelChild::~TestCancelChild()
|
||||
{
|
||||
MOZ_COUNT_DTOR(TestCancelChild);
|
||||
}
|
||||
|
||||
} // namespace _ipdltest
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef mozilla__ipdltest_TestCancel_h
|
||||
#define mozilla__ipdltest_TestCancel_h 1
|
||||
|
||||
#include "mozilla/_ipdltest/IPDLUnitTests.h"
|
||||
|
||||
#include "mozilla/_ipdltest/PTestCancelParent.h"
|
||||
#include "mozilla/_ipdltest/PTestCancelChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace _ipdltest {
|
||||
|
||||
|
||||
class TestCancelParent :
|
||||
public PTestCancelParent
|
||||
{
|
||||
public:
|
||||
TestCancelParent();
|
||||
virtual ~TestCancelParent();
|
||||
|
||||
static bool RunTestInProcesses() { return true; }
|
||||
static bool RunTestInThreads() { return false; }
|
||||
|
||||
void Main();
|
||||
|
||||
virtual bool RecvDone1() override;
|
||||
virtual bool RecvTest2_1() override;
|
||||
virtual bool RecvStart3() override;
|
||||
virtual bool RecvTest3_2() override;
|
||||
virtual bool RecvDone() override;
|
||||
|
||||
virtual bool RecvCheckParent(uint32_t *reply) override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override
|
||||
{
|
||||
passed("ok");
|
||||
QuitParent();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class TestCancelChild :
|
||||
public PTestCancelChild
|
||||
{
|
||||
public:
|
||||
TestCancelChild();
|
||||
virtual ~TestCancelChild();
|
||||
|
||||
virtual bool RecvTest1_1() override;
|
||||
virtual bool RecvStart2() override;
|
||||
virtual bool RecvTest2_2() override;
|
||||
virtual bool RecvTest3_1() override;
|
||||
|
||||
virtual bool RecvCheckChild(uint32_t *reply) override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override
|
||||
{
|
||||
QuitChild();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace _ipdltest
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
#endif // ifndef mozilla__ipdltest_TestCancel_h
|
||||
@@ -170,9 +170,9 @@ TestUrgentHangsChild::RecvTest4()
|
||||
{
|
||||
PR_Sleep(PR_SecondsToInterval(2));
|
||||
|
||||
// This should fail because Test4_1 timed out and hasn't gotten a response
|
||||
// yet.
|
||||
if (SendTestInner())
|
||||
// This won't fail because we should handle Test4_1 here before actually
|
||||
// sending TestInner to the parent.
|
||||
if (!SendTestInner())
|
||||
fail("sending TestInner");
|
||||
|
||||
return true;
|
||||
|
||||
@@ -17,6 +17,7 @@ SOURCES += [
|
||||
'TestActorPunning.cpp',
|
||||
'TestBadActor.cpp',
|
||||
'TestBridgeMain.cpp',
|
||||
'TestCancel.cpp',
|
||||
'TestCrashCleanup.cpp',
|
||||
'TestDataStructures.cpp',
|
||||
'TestDesc.cpp',
|
||||
@@ -69,6 +70,7 @@ IPDL_SOURCES += [
|
||||
'PTestBridgeMain.ipdl',
|
||||
'PTestBridgeMainSub.ipdl',
|
||||
'PTestBridgeSub.ipdl',
|
||||
'PTestCancel.ipdl',
|
||||
'PTestCrashCleanup.ipdl',
|
||||
'PTestDataStructures.ipdl',
|
||||
'PTestDataStructuresCommon.ipdlh',
|
||||
|
||||
@@ -299,15 +299,17 @@ var Harness = {
|
||||
if (this.finalContentEvent && !this.waitingForEvent) {
|
||||
this.waitingForEvent = true;
|
||||
info("Waiting for " + this.finalContentEvent);
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
mm.loadFrameScript(`data:,content.addEventListener("${this.finalContentEvent}", () => { sendAsyncMessage("Test:GotNewInstallEvent"); });`, false);
|
||||
let win = gBrowser.contentWindow;
|
||||
let listener = () => {
|
||||
info("Saw " + this.finalContentEvent);
|
||||
win.removeEventListener(this.finalContentEvent, listener, false);
|
||||
mm.removeMessageListener("Test:GotNewInstallEvent", listener);
|
||||
this.waitingForEvent = false;
|
||||
if (this.pendingCount == 0)
|
||||
this.endTest();
|
||||
}
|
||||
win.addEventListener(this.finalContentEvent, listener, false);
|
||||
mm.addMessageListener("Test:GotNewInstallEvent", listener);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
+229
-31
@@ -32,7 +32,20 @@
|
||||
#include "gfxContext.h"
|
||||
#include "gfxPlatformGtk.h"
|
||||
#include "gfxGdkNativeRenderer.h"
|
||||
#include "mozilla/gfx/BorrowedContext.h"
|
||||
#include "mozilla/gfx/HelpersCairo.h"
|
||||
|
||||
#ifdef MOZ_X11
|
||||
# ifdef CAIRO_HAS_XLIB_SURFACE
|
||||
# include "cairo-xlib.h"
|
||||
# endif
|
||||
# ifdef CAIRO_HAS_XLIB_XRENDER_SURFACE
|
||||
# include "cairo-xlib-xrender.h"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <dlfcn.h>
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
@@ -89,6 +102,24 @@ nsNativeThemeGTK::RefreshWidgetWindow(nsIFrame* aFrame)
|
||||
vm->InvalidateAllViews();
|
||||
}
|
||||
|
||||
gint
|
||||
nsNativeThemeGTK::GdkScaleFactor()
|
||||
{
|
||||
#if (MOZ_WIDGET_GTK >= 3)
|
||||
// Since GDK 3.10
|
||||
static auto sGdkScreenGetMonitorScaleFactorPtr = (gint (*)(GdkScreen*, gint))
|
||||
dlsym(RTLD_DEFAULT, "gdk_screen_get_monitor_scale_factor");
|
||||
if (sGdkScreenGetMonitorScaleFactorPtr) {
|
||||
// FIXME: In the future, we'll want to fix this for GTK on Wayland which
|
||||
// supports a variable scale factor per display.
|
||||
GdkScreen *screen = gdk_screen_get_default();
|
||||
return sGdkScreenGetMonitorScaleFactorPtr(screen, 0);
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static bool IsFrameContentNodeInNamespace(nsIFrame *aFrame, uint32_t aNamespace)
|
||||
{
|
||||
nsIContent *content = aFrame ? aFrame->GetContent() : nullptr;
|
||||
@@ -699,6 +730,158 @@ ThemeRenderer::DrawWithGDK(GdkDrawable * drawable, gint offsetX,
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
#else
|
||||
static void
|
||||
DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget,
|
||||
GtkWidgetState aState, GtkThemeWidgetType aGTKWidgetType,
|
||||
gint aFlags, GtkTextDirection aDirection, gint aScaleFactor,
|
||||
bool aSnapped, const Point& aDrawOrigin, const nsIntSize& aDrawSize,
|
||||
GdkRectangle& aGDKRect, nsITheme::Transparency aTransparency)
|
||||
{
|
||||
#ifndef MOZ_TREE_CAIRO
|
||||
// Directly use the Cairo draw target to render the widget if using system Cairo everywhere.
|
||||
BorrowedCairoContext borrow(aDrawTarget);
|
||||
if (borrow.mCairo) {
|
||||
if (aSnapped) {
|
||||
cairo_identity_matrix(borrow.mCairo);
|
||||
}
|
||||
if (aDrawOrigin != Point(0, 0)) {
|
||||
cairo_translate(borrow.mCairo, aDrawOrigin.x, aDrawOrigin.y);
|
||||
}
|
||||
if (aScaleFactor != 1) {
|
||||
cairo_scale(borrow.mCairo, aScaleFactor, aScaleFactor);
|
||||
}
|
||||
|
||||
moz_gtk_widget_paint(aGTKWidgetType, borrow.mCairo, &aGDKRect, &aState, aFlags, aDirection);
|
||||
|
||||
borrow.Finish();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// A direct Cairo draw target is not available, so we need to create a temporary one.
|
||||
bool needClip = !aSnapped || aContext->HasComplexClip();
|
||||
#if defined(MOZ_X11) && defined(CAIRO_HAS_XLIB_SURFACE)
|
||||
if (!needClip) {
|
||||
// If using a Cairo xlib surface, then try to reuse it.
|
||||
BorrowedXlibDrawable borrow(aDrawTarget);
|
||||
if (borrow.GetDrawable()) {
|
||||
nsIntSize size = aDrawTarget->GetSize();
|
||||
cairo_surface_t* surf = nullptr;
|
||||
// Check if the surface is using XRender.
|
||||
#ifdef CAIRO_HAS_XLIB_XRENDER_SURFACE
|
||||
if (borrow.GetXRenderFormat()) {
|
||||
surf = cairo_xlib_surface_create_with_xrender_format(
|
||||
borrow.GetDisplay(), borrow.GetDrawable(), borrow.GetScreen(),
|
||||
borrow.GetXRenderFormat(), size.width, size.height);
|
||||
} else {
|
||||
#else
|
||||
if (! borrow.GetXRenderFormat()) {
|
||||
#endif
|
||||
surf = cairo_xlib_surface_create(
|
||||
borrow.GetDisplay(), borrow.GetDrawable(), borrow.GetVisual(),
|
||||
size.width, size.height);
|
||||
}
|
||||
if (!NS_WARN_IF(!surf)) {
|
||||
cairo_t* cr = cairo_create(surf);
|
||||
if (!NS_WARN_IF(!cr)) {
|
||||
cairo_new_path(cr);
|
||||
cairo_rectangle(cr, aDrawOrigin.x, aDrawOrigin.y, aDrawSize.width, aDrawSize.height);
|
||||
cairo_clip(cr);
|
||||
if (aDrawOrigin != Point(0, 0)) {
|
||||
cairo_translate(cr, aDrawOrigin.x, aDrawOrigin.y);
|
||||
}
|
||||
if (aScaleFactor != 1) {
|
||||
cairo_scale(cr, aScaleFactor, aScaleFactor);
|
||||
}
|
||||
|
||||
moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
|
||||
|
||||
cairo_destroy(cr);
|
||||
}
|
||||
cairo_surface_destroy(surf);
|
||||
}
|
||||
borrow.Finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check if the widget requires complex masking that must be composited.
|
||||
// Try to directly write to the draw target's pixels if possible.
|
||||
uint8_t* data;
|
||||
nsIntSize size;
|
||||
int32_t stride;
|
||||
SurfaceFormat format;
|
||||
if (!needClip && aDrawTarget->LockBits(&data, &size, &stride, &format)) {
|
||||
// Create a Cairo image surface context the device rectangle.
|
||||
cairo_surface_t* surf =
|
||||
cairo_image_surface_create_for_data(
|
||||
data + int32_t(aDrawOrigin.y) * stride + int32_t(aDrawOrigin.x) * BytesPerPixel(format),
|
||||
GfxFormatToCairoFormat(format), aDrawSize.width, aDrawSize.height, stride);
|
||||
if (!NS_WARN_IF(!surf)) {
|
||||
cairo_t* cr = cairo_create(surf);
|
||||
if (!NS_WARN_IF(!cr)) {
|
||||
if (aScaleFactor != 1) {
|
||||
cairo_scale(cr, aScaleFactor, aScaleFactor);
|
||||
}
|
||||
|
||||
moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
|
||||
|
||||
cairo_destroy(cr);
|
||||
}
|
||||
cairo_surface_destroy(surf);
|
||||
}
|
||||
aDrawTarget->ReleaseBits(data);
|
||||
} else {
|
||||
// If the widget has any transparency, make sure to choose an alpha format.
|
||||
format = aTransparency != nsITheme::eOpaque ? SurfaceFormat::B8G8R8A8 : aDrawTarget->GetFormat();
|
||||
// Create a temporary data surface to render the widget into.
|
||||
RefPtr<DataSourceSurface> dataSurface =
|
||||
Factory::CreateDataSourceSurface(aDrawSize, format, aTransparency != nsITheme::eOpaque);
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!NS_WARN_IF(!(dataSurface && dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)))) {
|
||||
// Create a Cairo image surface wrapping the data surface.
|
||||
cairo_surface_t* surf =
|
||||
cairo_image_surface_create_for_data(map.mData, GfxFormatToCairoFormat(format),
|
||||
aDrawSize.width, aDrawSize.height, map.mStride);
|
||||
cairo_t* cr = nullptr;
|
||||
if (!NS_WARN_IF(!surf)) {
|
||||
cr = cairo_create(surf);
|
||||
if (!NS_WARN_IF(!cr)) {
|
||||
if (aScaleFactor != 1) {
|
||||
cairo_scale(cr, aScaleFactor, aScaleFactor);
|
||||
}
|
||||
|
||||
moz_gtk_widget_paint(aGTKWidgetType, cr, &aGDKRect, &aState, aFlags, aDirection);
|
||||
}
|
||||
}
|
||||
|
||||
// Unmap the surface before using it as a source
|
||||
dataSurface->Unmap();
|
||||
|
||||
if (cr) {
|
||||
if (needClip || aTransparency != nsITheme::eOpaque) {
|
||||
// The widget either needs to be masked or has transparency, so use the slower drawing path.
|
||||
aDrawTarget->DrawSurface(dataSurface,
|
||||
Rect(aDrawOrigin, Size(aDrawSize)),
|
||||
Rect(0, 0, aDrawSize.width, aDrawSize.height));
|
||||
} else {
|
||||
// The widget is a simple opaque rectangle, so just copy it out.
|
||||
aDrawTarget->CopySurface(dataSurface,
|
||||
IntRect(0, 0, aDrawSize.width, aDrawSize.height),
|
||||
TruncatedToInt(aDrawOrigin));
|
||||
}
|
||||
|
||||
cairo_destroy(cr);
|
||||
}
|
||||
|
||||
if (surf) {
|
||||
cairo_surface_destroy(surf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
@@ -712,10 +895,10 @@ nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t aWidgetType,
|
||||
switch (aWidgetType) {
|
||||
case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
|
||||
aExtra->top = aExtra->bottom = 1;
|
||||
return true;
|
||||
break;
|
||||
case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
|
||||
aExtra->left = aExtra->right = 1;
|
||||
return true;
|
||||
break;
|
||||
|
||||
// Include the indicator spacing (the padding around the control).
|
||||
case NS_THEME_CHECKBOX:
|
||||
@@ -733,7 +916,7 @@ nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t aWidgetType,
|
||||
aExtra->right = indicator_spacing;
|
||||
aExtra->bottom = indicator_spacing;
|
||||
aExtra->left = indicator_spacing;
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case NS_THEME_BUTTON :
|
||||
{
|
||||
@@ -746,7 +929,7 @@ nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t aWidgetType,
|
||||
aExtra->right = right;
|
||||
aExtra->bottom = bottom;
|
||||
aExtra->left = left;
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
case NS_THEME_FOCUS_OUTLINE:
|
||||
@@ -754,7 +937,7 @@ nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t aWidgetType,
|
||||
moz_gtk_get_focus_outline_size(&aExtra->left, &aExtra->top);
|
||||
aExtra->right = aExtra->left;
|
||||
aExtra->bottom = aExtra->top;
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case NS_THEME_TAB :
|
||||
{
|
||||
@@ -776,6 +959,11 @@ nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t aWidgetType,
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
aExtra->top *= GdkScaleFactor();
|
||||
aExtra->right *= GdkScaleFactor();
|
||||
aExtra->bottom *= GdkScaleFactor();
|
||||
aExtra->left *= GdkScaleFactor();
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -785,10 +973,6 @@ nsNativeThemeGTK::DrawWidgetBackground(nsRenderingContext* aContext,
|
||||
const nsRect& aRect,
|
||||
const nsRect& aDirtyRect)
|
||||
{
|
||||
#if (MOZ_WIDGET_GTK != 2)
|
||||
DrawTarget& aDrawTarget = *aContext->GetDrawTarget();
|
||||
#endif
|
||||
|
||||
GtkWidgetState state;
|
||||
GtkThemeWidgetType gtkWidgetType;
|
||||
GtkTextDirection direction = GetTextDirection(aFrame);
|
||||
@@ -802,13 +986,14 @@ nsNativeThemeGTK::DrawWidgetBackground(nsRenderingContext* aContext,
|
||||
|
||||
gfxRect rect = presContext->AppUnitsToGfxUnits(aRect);
|
||||
gfxRect dirtyRect = presContext->AppUnitsToGfxUnits(aDirtyRect);
|
||||
gint scaleFactor = GdkScaleFactor();
|
||||
|
||||
// Align to device pixels where sensible
|
||||
// to provide crisper and faster drawing.
|
||||
// Don't snap if it's a non-unit scale factor. We're going to have to take
|
||||
// slow paths then in any case.
|
||||
bool snapXY = ctx->UserToDevicePixelSnapped(rect);
|
||||
if (snapXY) {
|
||||
bool snapped = ctx->UserToDevicePixelSnapped(rect);
|
||||
if (snapped) {
|
||||
// Leave rect in device coords but make dirtyRect consistent.
|
||||
dirtyRect = ctx->UserToDevice(dirtyRect);
|
||||
}
|
||||
@@ -837,20 +1022,6 @@ nsNativeThemeGTK::DrawWidgetBackground(nsRenderingContext* aContext,
|
||||
|| !drawingRect.IntersectRect(overflowRect, drawingRect))
|
||||
return NS_OK;
|
||||
|
||||
// gdk rectangles are wrt the drawing rect.
|
||||
|
||||
GdkRectangle gdk_rect = {-drawingRect.x, -drawingRect.y,
|
||||
widgetRect.width, widgetRect.height};
|
||||
|
||||
// translate everything so (0,0) is the top left of the drawingRect
|
||||
gfxContextAutoSaveRestore autoSR(ctx);
|
||||
gfxMatrix tm;
|
||||
if (!snapXY) { // else rects are in device coords
|
||||
tm = ctx->CurrentMatrix();
|
||||
}
|
||||
tm.Translate(rect.TopLeft() + gfxPoint(drawingRect.x, drawingRect.y));
|
||||
ctx->SetMatrix(tm);
|
||||
|
||||
NS_ASSERTION(!IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType),
|
||||
"Trying to render an unsafe widget!");
|
||||
|
||||
@@ -860,7 +1031,27 @@ nsNativeThemeGTK::DrawWidgetBackground(nsRenderingContext* aContext,
|
||||
gdk_error_trap_push ();
|
||||
}
|
||||
|
||||
Transparency transparency = GetWidgetTransparency(aFrame, aWidgetType);
|
||||
|
||||
// gdk rectangles are wrt the drawing rect.
|
||||
GdkRectangle gdk_rect = {-drawingRect.x/scaleFactor,
|
||||
-drawingRect.y/scaleFactor,
|
||||
widgetRect.width/scaleFactor,
|
||||
widgetRect.height/scaleFactor};
|
||||
|
||||
// translate everything so (0,0) is the top left of the drawingRect
|
||||
gfxPoint origin = rect.TopLeft() + drawingRect.TopLeft();
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
gfxContextAutoSaveRestore autoSR(ctx);
|
||||
gfxMatrix matrix;
|
||||
if (!snapped) { // else rects are in device coords
|
||||
matrix = ctx->CurrentMatrix();
|
||||
}
|
||||
matrix.Translate(origin);
|
||||
matrix.Scale(scaleFactor, scaleFactor); // Draw in GDK coords
|
||||
ctx->SetMatrix(matrix);
|
||||
|
||||
// The gdk_clip is just advisory here, meaning "you don't
|
||||
// need to draw outside this rect if you don't feel like it!"
|
||||
GdkRectangle gdk_clip = {0, 0, drawingRect.width, drawingRect.height};
|
||||
@@ -872,7 +1063,7 @@ nsNativeThemeGTK::DrawWidgetBackground(nsRenderingContext* aContext,
|
||||
// clip rect we provide, so we cannot advertise support for clipping within
|
||||
// the widget bounds.
|
||||
uint32_t rendererFlags = 0;
|
||||
if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque) {
|
||||
if (transparency == eOpaque) {
|
||||
rendererFlags |= gfxGdkNativeRenderer::DRAW_IS_OPAQUE;
|
||||
}
|
||||
|
||||
@@ -882,11 +1073,10 @@ nsNativeThemeGTK::DrawWidgetBackground(nsRenderingContext* aContext,
|
||||
|
||||
renderer.Draw(ctx, drawingRect.Size(), rendererFlags, colormap);
|
||||
#else
|
||||
cairo_t *cairo_ctx =
|
||||
(cairo_t*)aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT);
|
||||
MOZ_ASSERT(cairo_ctx);
|
||||
moz_gtk_widget_paint(gtkWidgetType, cairo_ctx, &gdk_rect,
|
||||
&state, flags, direction);
|
||||
DrawThemeWithCairo(ctx, aContext->GetDrawTarget(),
|
||||
state, gtkWidgetType, flags, direction, scaleFactor,
|
||||
snapped, ToPoint(origin), drawingRect.Size(),
|
||||
gdk_rect, transparency);
|
||||
#endif
|
||||
|
||||
if (!safeState) {
|
||||
@@ -1037,6 +1227,11 @@ nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext,
|
||||
aResult->left += horizontal_padding;
|
||||
aResult->right += horizontal_padding;
|
||||
|
||||
aResult->top *= GdkScaleFactor();
|
||||
aResult->right *= GdkScaleFactor();
|
||||
aResult->bottom *= GdkScaleFactor();
|
||||
aResult->left *= GdkScaleFactor();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1298,6 +1493,9 @@ nsNativeThemeGTK::GetMinimumWidgetSize(nsPresContext* aPresContext,
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
*aResult = *aResult * GdkScaleFactor();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ private:
|
||||
nsIntMargin* aExtra);
|
||||
|
||||
void RefreshWidgetWindow(nsIFrame* aFrame);
|
||||
gint GdkScaleFactor();
|
||||
|
||||
uint8_t mDisabledWidgetTypes[32];
|
||||
uint8_t mSafeWidgetStates[1024]; // 256 widgets * 32 bits per widget
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#endif
|
||||
#include <gtk/gtk.h>
|
||||
#include <dlfcn.h>
|
||||
#include "gfxPlatformGtk.h"
|
||||
|
||||
static uint32_t sScreenId = 0;
|
||||
|
||||
|
||||
+77
-97
@@ -2049,71 +2049,52 @@ gdk_window_flash(GdkWindow * aGdkWindow,
|
||||
#endif // DEBUG
|
||||
#endif
|
||||
|
||||
struct ExposeRegion
|
||||
{
|
||||
nsIntRegion mRegion;
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
GdkRectangle *mRects;
|
||||
GdkRectangle *mRectsEnd;
|
||||
static bool
|
||||
ExtractExposeRegion(nsIntRegion& aRegion, GdkEventExpose* aEvent)
|
||||
{
|
||||
GdkRectangle* rects;
|
||||
gint nrects;
|
||||
gdk_region_get_rectangles(aEvent->region, &rects, &nrects);
|
||||
|
||||
ExposeRegion() : mRects(nullptr)
|
||||
{
|
||||
}
|
||||
~ExposeRegion()
|
||||
{
|
||||
g_free(mRects);
|
||||
}
|
||||
bool Init(GdkEventExpose *aEvent)
|
||||
{
|
||||
gint nrects;
|
||||
gdk_region_get_rectangles(aEvent->region, &mRects, &nrects);
|
||||
if (nrects > MAX_RECTS_IN_REGION) {
|
||||
// Just use the bounding box
|
||||
rects[0] = aEvent->area;
|
||||
nrects = 1;
|
||||
}
|
||||
|
||||
if (nrects > MAX_RECTS_IN_REGION) {
|
||||
// Just use the bounding box
|
||||
mRects[0] = aEvent->area;
|
||||
nrects = 1;
|
||||
}
|
||||
for (GdkRectangle* r = rects; r < rects + nrects; r++) {
|
||||
aRegion.Or(aRegion, nsIntRect(r->x, r->y, r->width, r->height));
|
||||
LOGDRAW(("\t%d %d %d %d\n", r->x, r->y, r->width, r->height));
|
||||
}
|
||||
|
||||
mRectsEnd = mRects + nrects;
|
||||
|
||||
for (GdkRectangle *r = mRects; r < mRectsEnd; r++) {
|
||||
mRegion.Or(mRegion, nsIntRect(r->x, r->y, r->width, r->height));
|
||||
LOGDRAW(("\t%d %d %d %d\n", r->x, r->y, r->width, r->height));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
g_free(rects);
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
# ifdef cairo_copy_clip_rectangle_list
|
||||
# error "Looks like we're including Mozilla's cairo instead of system cairo"
|
||||
# endif
|
||||
cairo_rectangle_list_t *mRects;
|
||||
static bool
|
||||
ExtractExposeRegion(nsIntRegion& aRegion, cairo_t* cr)
|
||||
{
|
||||
cairo_rectangle_list_t* rects = cairo_copy_clip_rectangle_list(cr);
|
||||
if (rects->status != CAIRO_STATUS_SUCCESS) {
|
||||
NS_WARNING("Failed to obtain cairo rectangle list.");
|
||||
return false;
|
||||
}
|
||||
|
||||
ExposeRegion() : mRects(nullptr)
|
||||
{
|
||||
}
|
||||
~ExposeRegion()
|
||||
{
|
||||
cairo_rectangle_list_destroy(mRects);
|
||||
}
|
||||
bool Init(cairo_t* cr)
|
||||
{
|
||||
mRects = cairo_copy_clip_rectangle_list(cr);
|
||||
if (mRects->status != CAIRO_STATUS_SUCCESS) {
|
||||
NS_WARNING("Failed to obtain cairo rectangle list.");
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < rects->num_rectangles; i++) {
|
||||
const cairo_rectangle_t& r = rects->rectangles[i];
|
||||
aRegion.Or(aRegion, nsIntRect(r.x, r.y, r.width, r.height));
|
||||
LOGDRAW(("\t%d %d %d %d\n", r.x, r.y, r.width, r.height));
|
||||
}
|
||||
|
||||
for (int i = 0; i < mRects->num_rectangles; i++) {
|
||||
const cairo_rectangle_t& r = mRects->rectangles[i];
|
||||
mRegion.Or(mRegion, nsIntRect(r.x, r.y, r.width, r.height));
|
||||
LOGDRAW(("\t%d %d %d %d\n", r.x, r.y, r.width, r.height));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
cairo_rectangle_list_destroy(rects);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
gboolean
|
||||
@@ -2136,17 +2117,17 @@ nsWindow::OnExposeEvent(cairo_t *cr)
|
||||
if (!listener)
|
||||
return FALSE;
|
||||
|
||||
ExposeRegion exposeRegion;
|
||||
nsIntRegion exposeRegion;
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
if (!exposeRegion.Init(aEvent)) {
|
||||
if (!ExtractExposeRegion(exposeRegion, aEvent)) {
|
||||
#else
|
||||
if (!exposeRegion.Init(cr)) {
|
||||
if (!ExtractExposeRegion(exposeRegion, cr)) {
|
||||
#endif
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gint scale = GdkScaleFactor();
|
||||
nsIntRegion& region = exposeRegion.mRegion;
|
||||
nsIntRegion region = exposeRegion;
|
||||
region.ScaleRoundOut(scale, scale);
|
||||
|
||||
ClientLayerManager *clientLayers =
|
||||
@@ -2239,33 +2220,11 @@ nsWindow::OnExposeEvent(cairo_t *cr)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gfxASurface* surf;
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
surf = GetThebesSurface();
|
||||
#else
|
||||
surf = GetThebesSurface(cr);
|
||||
#endif
|
||||
|
||||
nsRefPtr<gfxContext> ctx;
|
||||
if (gfxPlatform::GetPlatform()->
|
||||
SupportsAzureContentForType(BackendType::CAIRO)) {
|
||||
IntSize intSize(surf->GetSize().width, surf->GetSize().height);
|
||||
RefPtr<DrawTarget> dt =
|
||||
gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, intSize);
|
||||
ctx = new gfxContext(dt);
|
||||
} else if (gfxPlatform::GetPlatform()->
|
||||
SupportsAzureContentForType(BackendType::SKIA) &&
|
||||
surf->GetType() == gfxSurfaceType::Image) {
|
||||
gfxImageSurface* imgSurf = static_cast<gfxImageSurface*>(surf);
|
||||
SurfaceFormat format = ImageFormatToSurfaceFormat(imgSurf->Format());
|
||||
IntSize intSize(surf->GetSize().width, surf->GetSize().height);
|
||||
RefPtr<DrawTarget> dt =
|
||||
gfxPlatform::GetPlatform()->CreateDrawTargetForData(
|
||||
imgSurf->Data(), intSize, imgSurf->Stride(), format);
|
||||
ctx = new gfxContext(dt);
|
||||
} else {
|
||||
MOZ_CRASH("Unexpected content type");
|
||||
RefPtr<DrawTarget> dt = StartRemoteDrawing();
|
||||
if(!dt) {
|
||||
return FALSE;
|
||||
}
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(dt);
|
||||
|
||||
#ifdef MOZ_X11
|
||||
nsIntRect boundsRect; // for shaped only
|
||||
@@ -2340,11 +2299,7 @@ nsWindow::OnExposeEvent(cairo_t *cr)
|
||||
}
|
||||
# ifdef MOZ_HAVE_SHMIMAGE
|
||||
if (mShmImage && MOZ_LIKELY(!mIsDestroyed)) {
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
mShmImage->Put(mGdkWindow, exposeRegion.mRects, exposeRegion.mRectsEnd);
|
||||
#else
|
||||
mShmImage->Put(mGdkWindow, exposeRegion.mRects);
|
||||
#endif
|
||||
mShmImage->Put(mGdkWindow, exposeRegion);
|
||||
}
|
||||
# endif // MOZ_HAVE_SHMIMAGE
|
||||
#endif // MOZ_X11
|
||||
@@ -6268,24 +6223,49 @@ nsWindow::StartRemoteDrawing()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IntSize size(surf->GetSize().width, surf->GetSize().height);
|
||||
nsIntSize size = surf->GetSize();
|
||||
if (size.width <= 0 || size.height <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
|
||||
gfxPlatform *platform = gfxPlatform::GetPlatform();
|
||||
if (platform->SupportsAzureContentForType(BackendType::CAIRO) ||
|
||||
surf->GetType() == gfxSurfaceType::Xlib) {
|
||||
return platform->CreateDrawTargetForSurface(surf, size);
|
||||
} else if (platform->SupportsAzureContentForType(BackendType::SKIA) &&
|
||||
surf->GetType() == gfxSurfaceType::Image) {
|
||||
gfxImageSurface* imgSurf = static_cast<gfxImageSurface*>(surf);
|
||||
SurfaceFormat format = ImageFormatToSurfaceFormat(imgSurf->Format());
|
||||
return platform->CreateDrawTargetForData(
|
||||
imgSurf->Data(), size, imgSurf->Stride(), format);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::EndRemoteDrawingInRegion(DrawTarget* aDrawTarget, nsIntRegion& aInvalidRegion)
|
||||
{
|
||||
#ifdef MOZ_X11
|
||||
# ifdef MOZ_HAVE_SHMIMAGE
|
||||
if (!mGdkWindow || mIsFullyObscured || !mHasMappedToplevel || mIsDestroyed ||
|
||||
!mShmImage)
|
||||
return;
|
||||
|
||||
gint scale = GdkScaleFactor();
|
||||
if (scale != 1) {
|
||||
aInvalidRegion.ScaleInverseRoundOut(scale, scale);
|
||||
}
|
||||
|
||||
mShmImage->Put(mGdkWindow, aInvalidRegion);
|
||||
|
||||
# endif // MOZ_HAVE_SHMIMAGE
|
||||
#endif // MOZ_X11
|
||||
}
|
||||
|
||||
// return the gfxASurface for rendering to this widget
|
||||
gfxASurface*
|
||||
nsWindow::GetThebesSurface()
|
||||
#if (MOZ_WIDGET_GTK == 3)
|
||||
{
|
||||
return GetThebesSurface(nullptr);
|
||||
}
|
||||
gfxASurface*
|
||||
nsWindow::GetThebesSurface(cairo_t *cr)
|
||||
#endif
|
||||
{
|
||||
if (!mGdkWindow)
|
||||
return nullptr;
|
||||
|
||||
@@ -194,7 +194,10 @@ public:
|
||||
guint aTime,
|
||||
gpointer aData);
|
||||
|
||||
already_AddRefed<mozilla::gfx::DrawTarget> StartRemoteDrawing() override;
|
||||
virtual already_AddRefed<mozilla::gfx::DrawTarget>
|
||||
StartRemoteDrawing() override;
|
||||
virtual void EndRemoteDrawingInRegion(mozilla::gfx::DrawTarget* aDrawTarget,
|
||||
nsIntRegion& aInvalidRegion) override;
|
||||
|
||||
private:
|
||||
void UpdateAlpha(gfxPattern* aPattern, nsIntRect aBoundsRect);
|
||||
@@ -476,9 +479,6 @@ private:
|
||||
LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE,
|
||||
LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
|
||||
bool* aAllowRetaining = nullptr) override;
|
||||
#if (MOZ_WIDGET_GTK == 3)
|
||||
gfxASurface* GetThebesSurface(cairo_t *cr);
|
||||
#endif
|
||||
|
||||
void CleanLayerManagerRecursive();
|
||||
|
||||
|
||||
@@ -1670,6 +1670,9 @@ class nsIWidget : public nsISupports {
|
||||
* after each composition.
|
||||
*/
|
||||
virtual void EndRemoteDrawing() = 0;
|
||||
virtual void EndRemoteDrawingInRegion(mozilla::gfx::DrawTarget* aDrawTarget, nsIntRegion& aInvalidRegion) {
|
||||
EndRemoteDrawing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up any resources used by Start/EndRemoteDrawing.
|
||||
|
||||
@@ -121,7 +121,7 @@ nsShmImage::AsSurface()
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
void
|
||||
nsShmImage::Put(GdkWindow* aWindow, GdkRectangle* aRects, GdkRectangle* aEnd)
|
||||
nsShmImage::Put(GdkWindow* aWindow, const nsIntRegion& aRegion)
|
||||
{
|
||||
GdkDrawable* gd;
|
||||
gint dx, dy;
|
||||
@@ -131,7 +131,8 @@ nsShmImage::Put(GdkWindow* aWindow, GdkRectangle* aRects, GdkRectangle* aEnd)
|
||||
Drawable d = GDK_DRAWABLE_XID(gd);
|
||||
|
||||
GC gc = XCreateGC(dpy, d, 0, nullptr);
|
||||
for (GdkRectangle* r = aRects; r < aEnd; r++) {
|
||||
nsIntRegionRectIterator iter(aRegion);
|
||||
for (const nsIntRect *r = iter.Next(); r; r = iter.Next()) {
|
||||
XShmPutImage(dpy, d, gc, mImage,
|
||||
r->x, r->y,
|
||||
r->x - dx, r->y - dy,
|
||||
@@ -151,20 +152,19 @@ nsShmImage::Put(GdkWindow* aWindow, GdkRectangle* aRects, GdkRectangle* aEnd)
|
||||
|
||||
#elif (MOZ_WIDGET_GTK == 3)
|
||||
void
|
||||
nsShmImage::Put(GdkWindow* aWindow, cairo_rectangle_list_t* aRects)
|
||||
nsShmImage::Put(GdkWindow* aWindow, const nsIntRegion& aRegion)
|
||||
{
|
||||
Display* dpy = gdk_x11_get_default_xdisplay();
|
||||
Drawable d = GDK_WINDOW_XID(aWindow);
|
||||
int dx = 0, dy = 0;
|
||||
|
||||
GC gc = XCreateGC(dpy, d, 0, nullptr);
|
||||
cairo_rectangle_t r;
|
||||
for (int i = 0; i < aRects->num_rectangles; i++) {
|
||||
r = aRects->rectangles[i];
|
||||
nsIntRegionRectIterator iter(aRegion);
|
||||
for (const nsIntRect *r = iter.Next(); r; r = iter.Next()) {
|
||||
XShmPutImage(dpy, d, gc, mImage,
|
||||
r.x, r.y,
|
||||
r.x - dx, r.y - dy,
|
||||
r.width, r.height,
|
||||
r->x, r->y,
|
||||
r->x - dx, r->y - dy,
|
||||
r->width, r->height,
|
||||
False);
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -63,10 +63,8 @@ private:
|
||||
public:
|
||||
already_AddRefed<gfxASurface> AsSurface();
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
void Put(GdkWindow* aWindow, GdkRectangle* aRects, GdkRectangle* aEnd);
|
||||
#elif (MOZ_WIDGET_GTK == 3)
|
||||
void Put(GdkWindow* aWindow, cairo_rectangle_list_t* aRects);
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
void Put(GdkWindow* aWindow, const nsIntRegion& aRegion);
|
||||
#elif defined(MOZ_WIDGET_QT)
|
||||
void Put(QWindow* aWindow, QRect& aRect);
|
||||
#endif
|
||||
|
||||
@@ -16,5 +16,10 @@
|
||||
|
||||
#define MOZ_LOG PR_LOG
|
||||
|
||||
// Tests if a module has enabled the given log level.
|
||||
// NB: _module can be null.
|
||||
#define MOZ_LOG_TEST(_module, _level) \
|
||||
((_module) && (_module)->level >= (_level))
|
||||
|
||||
#endif // mozilla_logging_h
|
||||
|
||||
|
||||
Reference in New Issue
Block a user