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:
2021-01-25 21:58:53 +08:00
parent 2e5493ebe2
commit d543e1fe54
31 changed files with 1037 additions and 321 deletions
+14 -36
View File
@@ -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
+67 -74
View File
@@ -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");
});
}
+2 -1
View File
@@ -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) {
+68
View File
@@ -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.
+45
View File
@@ -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
+1
View File
@@ -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();
+31 -2
View File
@@ -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;
+3
View File
@@ -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,
+1
View File
@@ -79,6 +79,7 @@ if CONFIG['MOZ_ENABLE_SKIA']:
'image_operations.cpp', # Uses _USE_MATH_DEFINES
]
EXPORTS.mozilla.gfx += [
'HelpersCairo.h',
'HelpersSkia.h',
]
+2 -2
View File
@@ -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;
+6
View File
@@ -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);
+17
View File
@@ -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)
{
+5
View File
@@ -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
View File
@@ -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()
{
+15 -4
View File
@@ -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
+5 -4
View File
@@ -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.
};
+36
View File
@@ -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
+175
View File
@@ -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
+66
View File
@@ -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
+3 -3
View File
@@ -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;
+2
View File
@@ -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
View File
@@ -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;
}
+1
View File
@@ -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
+1
View File
@@ -12,6 +12,7 @@
#endif
#include <gtk/gtk.h>
#include <dlfcn.h>
#include "gfxPlatformGtk.h"
static uint32_t sScreenId = 0;
+77 -97
View File
@@ -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;
+4 -4
View File
@@ -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();
+3
View File
@@ -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.
+9 -9
View File
@@ -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
View File
@@ -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
+5
View File
@@ -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