summaryrefslogtreecommitdiffstats
path: root/widget/gtk/nsNativeMenuDocListener.cpp
diff options
context:
space:
mode:
authorLootyhoof <lootyhoofer@gmail.com>2020-06-06 00:37:29 +0100
committerLootyhoof <lootyhoofer@gmail.com>2020-06-09 13:41:53 +0100
commitd18faf81938c947f1d02feab2c394b8135f88d8f (patch)
tree17ad9c1ecb14acd726cb8404c3a82e2781e27253 /widget/gtk/nsNativeMenuDocListener.cpp
parentfdb918dea124a6efaf0c86f6d5c2ab1b83013e40 (diff)
downloadUXP-d18faf81938c947f1d02feab2c394b8135f88d8f.tar
UXP-d18faf81938c947f1d02feab2c394b8135f88d8f.tar.gz
UXP-d18faf81938c947f1d02feab2c394b8135f88d8f.tar.lz
UXP-d18faf81938c947f1d02feab2c394b8135f88d8f.tar.xz
UXP-d18faf81938c947f1d02feab2c394b8135f88d8f.zip
Issue MoonchildProductions/UXP#1578 - Add global menubar support for GTK
Diffstat (limited to 'widget/gtk/nsNativeMenuDocListener.cpp')
-rw-r--r--widget/gtk/nsNativeMenuDocListener.cpp329
1 files changed, 329 insertions, 0 deletions
diff --git a/widget/gtk/nsNativeMenuDocListener.cpp b/widget/gtk/nsNativeMenuDocListener.cpp
new file mode 100644
index 000000000..46a9c3aa9
--- /dev/null
+++ b/widget/gtk/nsNativeMenuDocListener.cpp
@@ -0,0 +1,329 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/dom/Element.h"
+#include "nsContentUtils.h"
+#include "nsIAtom.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+
+#include "nsMenuContainer.h"
+
+#include "nsNativeMenuDocListener.h"
+
+using namespace mozilla;
+
+uint32_t nsNativeMenuDocListener::sUpdateBlockersCount = 0;
+
+nsNativeMenuDocListenerTArray* gPendingListeners;
+
+/*
+ * Small helper which caches a single listener, so that consecutive
+ * events which go to the same node avoid multiple hash table lookups
+ */
+class MOZ_STACK_CLASS DispatchHelper {
+public:
+ DispatchHelper(nsNativeMenuDocListener* aListener,
+ nsIContent* aContent
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
+ mObserver(nullptr) {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ if (aContent == aListener->mLastSource) {
+ mObserver = aListener->mLastTarget;
+ } else {
+ mObserver = aListener->mContentToObserverTable.Get(aContent);
+ if (mObserver) {
+ aListener->mLastSource = aContent;
+ aListener->mLastTarget = mObserver;
+ }
+ }
+ }
+
+ ~DispatchHelper() { };
+
+ nsNativeMenuChangeObserver* Observer() const {
+ return mObserver;
+ }
+
+ bool HasObserver() const {
+ return !!mObserver;
+ }
+
+private:
+ nsNativeMenuChangeObserver* mObserver;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+NS_IMPL_ISUPPORTS(nsNativeMenuDocListener, nsIMutationObserver)
+
+nsNativeMenuDocListener::~nsNativeMenuDocListener() {
+ MOZ_ASSERT(mContentToObserverTable.Count() == 0,
+ "Some nodes forgot to unregister listeners. This is bad! (and we're lucky we made it this far)");
+ MOZ_COUNT_DTOR(nsNativeMenuDocListener);
+}
+
+void
+nsNativeMenuDocListener::AttributeChanged(nsIDocument* aDocument,
+ mozilla::dom::Element* aElement,
+ int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType,
+ const nsAttrValue* aOldValue) {
+ if (sUpdateBlockersCount == 0) {
+ DoAttributeChanged(aElement, aAttribute);
+ return;
+ }
+
+ MutationRecord* m =* mPendingMutations.AppendElement(new MutationRecord);
+ m->mType = MutationRecord::eAttributeChanged;
+ m->mTarget = aElement;
+ m->mAttribute = aAttribute;
+
+ ScheduleFlush(this);
+}
+
+void
+nsNativeMenuDocListener::ContentAppended(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aFirstNewContent,
+ int32_t aNewIndexInContainer) {
+ for (nsIContent* c = aFirstNewContent; c; c = c->GetNextSibling()) {
+ ContentInserted(aDocument, aContainer, c, 0);
+ }
+}
+
+void
+nsNativeMenuDocListener::ContentInserted(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ int32_t aIndexInContainer) {
+ nsIContent* prevSibling = nsMenuContainer::GetPreviousSupportedSibling(aChild);
+
+ if (sUpdateBlockersCount == 0) {
+ DoContentInserted(aContainer, aChild, prevSibling);
+ return;
+ }
+
+ MutationRecord* m =* mPendingMutations.AppendElement(new MutationRecord);
+ m->mType = MutationRecord::eContentInserted;
+ m->mTarget = aContainer;
+ m->mChild = aChild;
+ m->mPrevSibling = prevSibling;
+
+ ScheduleFlush(this);
+}
+
+void
+nsNativeMenuDocListener::ContentRemoved(nsIDocument* aDocument,
+ nsIContent* aContainer,
+ nsIContent* aChild,
+ int32_t aIndexInContainer,
+ nsIContent* aPreviousSibling) {
+ if (sUpdateBlockersCount == 0) {
+ DoContentRemoved(aContainer, aChild);
+ return;
+ }
+
+ MutationRecord* m =* mPendingMutations.AppendElement(new MutationRecord);
+ m->mType = MutationRecord::eContentRemoved;
+ m->mTarget = aContainer;
+ m->mChild = aChild;
+
+ ScheduleFlush(this);
+}
+
+void
+nsNativeMenuDocListener::NodeWillBeDestroyed(const nsINode* aNode) {
+ mDocument = nullptr;
+}
+
+void
+nsNativeMenuDocListener::DoAttributeChanged(nsIContent* aContent,
+ nsIAtom* aAttribute) {
+ DispatchHelper h(this, aContent);
+ if (h.HasObserver()) {
+ h.Observer()->OnAttributeChanged(aContent, aAttribute);
+ }
+}
+
+void
+nsNativeMenuDocListener::DoContentInserted(nsIContent* aContainer,
+ nsIContent* aChild,
+ nsIContent* aPrevSibling) {
+ DispatchHelper h(this, aContainer);
+ if (h.HasObserver()) {
+ h.Observer()->OnContentInserted(aContainer, aChild, aPrevSibling);
+ }
+}
+
+void
+nsNativeMenuDocListener::DoContentRemoved(nsIContent* aContainer,
+ nsIContent* aChild) {
+ DispatchHelper h(this, aContainer);
+ if (h.HasObserver()) {
+ h.Observer()->OnContentRemoved(aContainer, aChild);
+ }
+}
+
+void
+nsNativeMenuDocListener::DoBeginUpdates(nsIContent* aTarget) {
+ DispatchHelper h(this, aTarget);
+ if (h.HasObserver()) {
+ h.Observer()->OnBeginUpdates(aTarget);
+ }
+}
+
+void
+nsNativeMenuDocListener::DoEndUpdates(nsIContent* aTarget) {
+ DispatchHelper h(this, aTarget);
+ if (h.HasObserver()) {
+ h.Observer()->OnEndUpdates();
+ }
+}
+
+void
+nsNativeMenuDocListener::FlushPendingMutations() {
+ nsIContent* currentTarget = nullptr;
+ bool inUpdateSequence = false;
+
+ while (mPendingMutations.Length() > 0) {
+ MutationRecord* m = mPendingMutations[0];
+
+ if (m->mTarget != currentTarget) {
+ if (inUpdateSequence) {
+ DoEndUpdates(currentTarget);
+ inUpdateSequence = false;
+ }
+
+ currentTarget = m->mTarget;
+
+ if (mPendingMutations.Length() > 1 &&
+ mPendingMutations[1]->mTarget == currentTarget) {
+ DoBeginUpdates(currentTarget);
+ inUpdateSequence = true;
+ }
+ }
+
+ switch (m->mType) {
+ case MutationRecord::eAttributeChanged:
+ DoAttributeChanged(m->mTarget, m->mAttribute);
+ break;
+ case MutationRecord::eContentInserted:
+ DoContentInserted(m->mTarget, m->mChild, m->mPrevSibling);
+ break;
+ case MutationRecord::eContentRemoved:
+ DoContentRemoved(m->mTarget, m->mChild);
+ break;
+ default:
+ NS_NOTREACHED("Invalid type");
+ }
+
+ mPendingMutations.RemoveElementAt(0);
+ }
+
+ if (inUpdateSequence) {
+ DoEndUpdates(currentTarget);
+ }
+}
+
+/* static */ void
+nsNativeMenuDocListener::ScheduleFlush(nsNativeMenuDocListener* aListener) {
+ MOZ_ASSERT(sUpdateBlockersCount > 0, "Shouldn't be doing this now");
+
+ if (!gPendingListeners) {
+ gPendingListeners = new nsNativeMenuDocListenerTArray;
+ }
+
+ if (gPendingListeners->IndexOf(aListener) ==
+ nsNativeMenuDocListenerTArray::NoIndex) {
+ gPendingListeners->AppendElement(aListener);
+ }
+}
+
+/* static */ void
+nsNativeMenuDocListener::CancelFlush(nsNativeMenuDocListener* aListener) {
+ if (!gPendingListeners) {
+ return;
+ }
+
+ gPendingListeners->RemoveElement(aListener);
+}
+
+/* static */ void
+nsNativeMenuDocListener::RemoveUpdateBlocker() {
+ if (sUpdateBlockersCount == 1 && gPendingListeners) {
+ while (gPendingListeners->Length() > 0) {
+ (*gPendingListeners)[0]->FlushPendingMutations();
+ gPendingListeners->RemoveElementAt(0);
+ }
+ }
+
+ MOZ_ASSERT(sUpdateBlockersCount > 0, "Negative update blockers count!");
+ sUpdateBlockersCount--;
+}
+
+nsNativeMenuDocListener::nsNativeMenuDocListener(nsIContent* aRootNode) :
+ mRootNode(aRootNode),
+ mDocument(nullptr),
+ mLastSource(nullptr),
+ mLastTarget(nullptr) {
+ MOZ_COUNT_CTOR(nsNativeMenuDocListener);
+}
+
+void
+nsNativeMenuDocListener::RegisterForContentChanges(nsIContent* aContent,
+ nsNativeMenuChangeObserver* aObserver) {
+ MOZ_ASSERT(aContent, "Need content parameter");
+ MOZ_ASSERT(aObserver, "Need observer parameter");
+ if (!aContent || !aObserver) {
+ return;
+ }
+
+ DebugOnly<nsNativeMenuChangeObserver* > old;
+ MOZ_ASSERT(!mContentToObserverTable.Get(aContent, &old) || old == aObserver,
+ "Multiple observers for the same content node are not supported");
+
+ mContentToObserverTable.Put(aContent, aObserver);
+}
+
+void
+nsNativeMenuDocListener::UnregisterForContentChanges(nsIContent* aContent) {
+ MOZ_ASSERT(aContent, "Need content parameter");
+ if (!aContent) {
+ return;
+ }
+
+ mContentToObserverTable.Remove(aContent);
+ if (aContent == mLastSource) {
+ mLastSource = nullptr;
+ mLastTarget = nullptr;
+ }
+}
+
+void
+nsNativeMenuDocListener::Start() {
+ if (mDocument) {
+ return;
+ }
+
+ mDocument = mRootNode->OwnerDoc();
+ if (!mDocument) {
+ return;
+ }
+
+ mDocument->AddMutationObserver(this);
+}
+
+void
+nsNativeMenuDocListener::Stop() {
+ if (mDocument) {
+ mDocument->RemoveMutationObserver(this);
+ mDocument = nullptr;
+ }
+
+ CancelFlush(this);
+ mPendingMutations.Clear();
+}