From d543e1fe54ca37fbceaa94e6273c10f54e1644da Mon Sep 17 00:00:00 2001 From: roytam1 Date: Mon, 25 Jan 2021 21:58:53 +0800 Subject: [PATCH] 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) --- dom/base/nsPropertyTable.cpp | 50 +--- dom/base/test/chrome/cpows_child.js | 141 +++++----- dom/base/test/chrome/cpows_parent.xul | 3 +- gfx/2d/BorrowedContext.h | 68 +++++ gfx/2d/DrawTargetCairo.cpp | 45 +++ gfx/2d/DrawTargetCairo.h | 1 + gfx/2d/DrawTargetSkia.cpp | 33 ++- gfx/2d/DrawTargetSkia.h | 3 + gfx/2d/moz.build | 1 + gfx/layers/basic/BasicCompositor.cpp | 4 +- gfx/src/nsRegion.h | 6 + gfx/thebes/gfxContext.cpp | 17 ++ gfx/thebes/gfxContext.h | 5 + ipc/glue/MessageChannel.cpp | 186 +++++++++---- ipc/glue/MessageChannel.h | 19 +- ipc/glue/ProtocolUtils.h | 9 +- ipc/ipdl/test/cxx/PTestCancel.ipdl | 36 +++ ipc/ipdl/test/cxx/TestCancel.cpp | 175 ++++++++++++ ipc/ipdl/test/cxx/TestCancel.h | 66 +++++ ipc/ipdl/test/cxx/TestUrgentHangs.cpp | 6 +- ipc/ipdl/test/cxx/moz.build | 2 + .../mozapps/extensions/test/xpinstall/head.js | 6 +- widget/gtk/nsNativeThemeGTK.cpp | 260 +++++++++++++++--- widget/gtk/nsNativeThemeGTK.h | 1 + widget/gtk/nsScreenGtk.cpp | 1 + widget/gtk/nsWindow.cpp | 174 ++++++------ widget/gtk/nsWindow.h | 8 +- widget/nsIWidget.h | 3 + widget/nsShmImage.cpp | 18 +- widget/nsShmImage.h | 6 +- xpcom/glue/Logging.h | 5 + 31 files changed, 1037 insertions(+), 321 deletions(-) create mode 100644 ipc/ipdl/test/cxx/PTestCancel.ipdl create mode 100644 ipc/ipdl/test/cxx/TestCancel.cpp create mode 100644 ipc/ipdl/test/cxx/TestCancel.h diff --git a/dom/base/nsPropertyTable.cpp b/dom/base/nsPropertyTable.cpp index 521105f00d..b27241a811 100644 --- a/dom/base/nsPropertyTable.cpp +++ b/dom/base/nsPropertyTable.cpp @@ -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(aHdr); - PropertyEnumeratorData* data = static_cast(aArg); - data->mCallBack(const_cast(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(iter.NextEntry()); + aCallBack(const_cast(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(arg); - PropertyListMapEntry* entry = static_cast(hdr); - - propList->mDtorFunc(const_cast(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(iter.NextEntry()); + mDtorFunc(const_cast(entry->key), mName, entry->value, mDtorData); + } + } } bool diff --git a/dom/base/test/chrome/cpows_child.js b/dom/base/test/chrome/cpows_child.js index 440225727e..dd58273aa9 100644 --- a/dom/base/test/chrome/cpows_child.js +++ b/dom/base/test/chrome/cpows_child.js @@ -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"); }); } diff --git a/dom/base/test/chrome/cpows_parent.xul b/dom/base/test/chrome/cpows_parent.xul index 3348e599f0..ac2c5529fd 100644 --- a/dom/base/test/chrome/cpows_parent.xul +++ b/dom/base/test/chrome/cpows_parent.xul @@ -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) { diff --git a/gfx/2d/BorrowedContext.h b/gfx/2d/BorrowedContext.h index 915ad99e5b..477ec9d12d 100644 --- a/gfx/2d/BorrowedContext.h +++ b/gfx/2d/BorrowedContext.h @@ -8,6 +8,11 @@ #include "2D.h" +#ifdef MOZ_X11 +#include +#include +#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. diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp index 09fe925700..8e84498c11 100644 --- a/gfx/2d/DrawTargetCairo.cpp +++ b/gfx/2d/DrawTargetCairo.cpp @@ -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(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 diff --git a/gfx/2d/DrawTargetCairo.h b/gfx/2d/DrawTargetCairo.h index e02af4a5d2..f6a5dc6005 100644 --- a/gfx/2d/DrawTargetCairo.h +++ b/gfx/2d/DrawTargetCairo.h @@ -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(); diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp index 6c91a8512e..fcc0d0de94 100644 --- a/gfx/2d/DrawTargetSkia.cpp +++ b/gfx/2d/DrawTargetSkia.cpp @@ -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(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(aSurface.mSurface); return MakeAndAddRef(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 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; diff --git a/gfx/2d/DrawTargetSkia.h b/gfx/2d/DrawTargetSkia.h index a1bde6dcb9..9143c531b6 100644 --- a/gfx/2d/DrawTargetSkia.h +++ b/gfx/2d/DrawTargetSkia.h @@ -36,6 +36,9 @@ public: virtual BackendType GetBackendType() const override { return BackendType::SKIA; } virtual already_AddRefed 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, diff --git a/gfx/2d/moz.build b/gfx/2d/moz.build index c8c74f3e35..e3fc115b32 100644 --- a/gfx/2d/moz.build +++ b/gfx/2d/moz.build @@ -79,6 +79,7 @@ if CONFIG['MOZ_ENABLE_SKIA']: 'image_operations.cpp', # Uses _USE_MATH_DEFINES ] EXPORTS.mozilla.gfx += [ + 'HelpersCairo.h', 'HelpersSkia.h', ] diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp index af73850e48..97f30fbe9b 100644 --- a/gfx/layers/basic/BasicCompositor.cpp +++ b/gfx/layers/basic/BasicCompositor.cpp @@ -522,7 +522,7 @@ BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion, RefPtr 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; diff --git a/gfx/src/nsRegion.h b/gfx/src/nsRegion.h index 05dca175ca..fe7c765be8 100644 --- a/gfx/src/nsRegion.h +++ b/gfx/src/nsRegion.h @@ -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); diff --git a/gfx/thebes/gfxContext.cpp b/gfx/thebes/gfxContext.cpp index d0dd30d56b..b443f481ea 100644 --- a/gfx/thebes/gfxContext.cpp +++ b/gfx/thebes/gfxContext.cpp @@ -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) { diff --git a/gfx/thebes/gfxContext.h b/gfx/thebes/gfxContext.h index 9340c1c2a3..f57de3648f 100644 --- a/gfx/thebes/gfxContext.h +++ b/gfx/thebes/gfxContext.h @@ -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 diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 4065ac3384..6123901fb9 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -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 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 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() { diff --git a/ipc/glue/MessageChannel.h b/ipc/glue/MessageChannel.h index 5a4953c40e..4f3fa2665a 100644 --- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -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 diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h index f874e31561..8570a459b5 100644 --- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -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. }; diff --git a/ipc/ipdl/test/cxx/PTestCancel.ipdl b/ipc/ipdl/test/cxx/PTestCancel.ipdl new file mode 100644 index 0000000000..f7622e5803 --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestCancel.ipdl @@ -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 diff --git a/ipc/ipdl/test/cxx/TestCancel.cpp b/ipc/ipdl/test/cxx/TestCancel.cpp new file mode 100644 index 0000000000..160bdc71c8 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestCancel.cpp @@ -0,0 +1,175 @@ +#include "TestCancel.h" + +#include "IPDLUnitTests.h" // fail etc. + +template<> +struct RunnableMethodTraits +{ + 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 diff --git a/ipc/ipdl/test/cxx/TestCancel.h b/ipc/ipdl/test/cxx/TestCancel.h new file mode 100644 index 0000000000..8cd51acd16 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestCancel.h @@ -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 diff --git a/ipc/ipdl/test/cxx/TestUrgentHangs.cpp b/ipc/ipdl/test/cxx/TestUrgentHangs.cpp index 09ff7b5b1c..f2ca7839a2 100644 --- a/ipc/ipdl/test/cxx/TestUrgentHangs.cpp +++ b/ipc/ipdl/test/cxx/TestUrgentHangs.cpp @@ -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; diff --git a/ipc/ipdl/test/cxx/moz.build b/ipc/ipdl/test/cxx/moz.build index 8e1e537236..33fd41a038 100644 --- a/ipc/ipdl/test/cxx/moz.build +++ b/ipc/ipdl/test/cxx/moz.build @@ -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', diff --git a/toolkit/mozapps/extensions/test/xpinstall/head.js b/toolkit/mozapps/extensions/test/xpinstall/head.js index 90db299242..428a8abec1 100644 --- a/toolkit/mozapps/extensions/test/xpinstall/head.js +++ b/toolkit/mozapps/extensions/test/xpinstall/head.js @@ -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); } }, diff --git a/widget/gtk/nsNativeThemeGTK.cpp b/widget/gtk/nsNativeThemeGTK.cpp index 6e194d0404..193270b1c4 100644 --- a/widget/gtk/nsNativeThemeGTK.cpp +++ b/widget/gtk/nsNativeThemeGTK.cpp @@ -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 +#include 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 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; } diff --git a/widget/gtk/nsNativeThemeGTK.h b/widget/gtk/nsNativeThemeGTK.h index 112503709b..13d4fb0437 100644 --- a/widget/gtk/nsNativeThemeGTK.h +++ b/widget/gtk/nsNativeThemeGTK.h @@ -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 diff --git a/widget/gtk/nsScreenGtk.cpp b/widget/gtk/nsScreenGtk.cpp index fd17fa0903..be657a8651 100644 --- a/widget/gtk/nsScreenGtk.cpp +++ b/widget/gtk/nsScreenGtk.cpp @@ -12,6 +12,7 @@ #endif #include #include +#include "gfxPlatformGtk.h" static uint32_t sScreenId = 0; diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 31f563812f..2b250342b2 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -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 ctx; - if (gfxPlatform::GetPlatform()-> - SupportsAzureContentForType(BackendType::CAIRO)) { - IntSize intSize(surf->GetSize().width, surf->GetSize().height); - RefPtr 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(surf); - SurfaceFormat format = ImageFormatToSurfaceFormat(imgSurf->Format()); - IntSize intSize(surf->GetSize().width, surf->GetSize().height); - RefPtr dt = - gfxPlatform::GetPlatform()->CreateDrawTargetForData( - imgSurf->Data(), intSize, imgSurf->Stride(), format); - ctx = new gfxContext(dt); - } else { - MOZ_CRASH("Unexpected content type"); + RefPtr dt = StartRemoteDrawing(); + if(!dt) { + return FALSE; } + nsRefPtr 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(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; diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index ea54caad08..84e8e1e05c 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -194,7 +194,10 @@ public: guint aTime, gpointer aData); - already_AddRefed StartRemoteDrawing() override; + virtual already_AddRefed + 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(); diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index b0c25223ab..f31be8ad9f 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -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. diff --git a/widget/nsShmImage.cpp b/widget/nsShmImage.cpp index 2d328aece9..4c614aec12 100644 --- a/widget/nsShmImage.cpp +++ b/widget/nsShmImage.cpp @@ -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); } diff --git a/widget/nsShmImage.h b/widget/nsShmImage.h index df71a9ed1a..af948b3d2f 100644 --- a/widget/nsShmImage.h +++ b/widget/nsShmImage.h @@ -63,10 +63,8 @@ private: public: already_AddRefed 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 diff --git a/xpcom/glue/Logging.h b/xpcom/glue/Logging.h index 0a5e7597f2..91119088e1 100644 --- a/xpcom/glue/Logging.h +++ b/xpcom/glue/Logging.h @@ -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