summaryrefslogtreecommitdiffstats
path: root/widget/gtk/nsNativeMenuDocListener.h
blob: c0a503da1b0919abba8b27ebc018845f78d78876 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* 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/. */

#ifndef __nsNativeMenuDocListener_h__
#define __nsNativeMenuDocListener_h__

#include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/RefPtr.h"
#include "nsAutoPtr.h"
#include "nsDataHashtable.h"
#include "nsStubMutationObserver.h"
#include "nsTArray.h"

class nsIAtom;
class nsIContent;
class nsIDocument;
class nsNativeMenuChangeObserver;

/*
 * This class keeps a mapping of content nodes to observers and forwards DOM
 * mutations to these. There is exactly one of these for every menubar.
 */
class nsNativeMenuDocListener final : nsStubMutationObserver {
public:
    NS_DECL_ISUPPORTS

    nsNativeMenuDocListener(nsIContent* aRootNode);

    // Register an observer to receive mutation events for the specified
    // content node. The caller must keep the observer alive until
    // UnregisterForContentChanges is called.
    void RegisterForContentChanges(nsIContent* aContent,
                                   nsNativeMenuChangeObserver* aObserver);

    // Unregister the registered observer for the specified content node
    void UnregisterForContentChanges(nsIContent* aContent);

    // Start listening to the document and forwarding DOM mutations to
    // registered observers.
    void Start();

    // Stop listening to the document. No DOM mutations will be forwarded
    // to registered observers.
    void Stop();

    /*
     * This class is intended to be used inside GObject signal handlers.
     * It allows us to queue updates until we have finished delivering
     * events to Goanna, and then we can batch updates to our view of the
     * menu. This allows us to do menu updates without altering the structure
     * seen by the OS.
     */
    class MOZ_STACK_CLASS BlockUpdatesScope {
    public:
        BlockUpdatesScope(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) {
            MOZ_GUARD_OBJECT_NOTIFIER_INIT;
            nsNativeMenuDocListener::AddUpdateBlocker();
        }

        ~BlockUpdatesScope() {
            nsNativeMenuDocListener::RemoveUpdateBlocker();
        }

    private:
        MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
    };

private:
    friend class DispatchHelper;

    struct MutationRecord {
        enum RecordType {
            eAttributeChanged,
            eContentInserted,
            eContentRemoved
        } mType;

        nsCOMPtr<nsIContent> mTarget;
        nsCOMPtr<nsIContent> mChild;
        nsCOMPtr<nsIContent> mPrevSibling;
        nsCOMPtr<nsIAtom> mAttribute;
    };

    ~nsNativeMenuDocListener();

    NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
    NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
    NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
    NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
    NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED

    void DoAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute);
    void DoContentInserted(nsIContent* aContainer,
                           nsIContent* aChild,
                           nsIContent* aPrevSibling);
    void DoContentRemoved(nsIContent* aContainer, nsIContent* aChild);
    void DoBeginUpdates(nsIContent* aTarget);
    void DoEndUpdates(nsIContent* aTarget);

    void FlushPendingMutations();
    static void ScheduleFlush(nsNativeMenuDocListener* aListener);
    static void CancelFlush(nsNativeMenuDocListener* aListener);

    static void AddUpdateBlocker() {
      ++sUpdateBlockersCount;
    }
    static void RemoveUpdateBlocker();

    nsCOMPtr<nsIContent> mRootNode;
    nsIDocument* mDocument;
    nsIContent* mLastSource;
    nsNativeMenuChangeObserver* mLastTarget;
    nsTArray<nsAutoPtr<MutationRecord> > mPendingMutations;
    nsDataHashtable<nsPtrHashKey<nsIContent>, nsNativeMenuChangeObserver* > mContentToObserverTable;

    static uint32_t sUpdateBlockersCount;
};

typedef nsTArray<RefPtr<nsNativeMenuDocListener> > nsNativeMenuDocListenerTArray;

/*
 *  Implemented by classes that want to listen to mutation events from content
 *  nodes.
 */
class nsNativeMenuChangeObserver {
public:
    virtual void OnAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute) {}

    virtual void OnContentInserted(nsIContent* aContainer,
                                   nsIContent* aChild,
                                   nsIContent* aPrevSibling) {}

    virtual void OnContentRemoved(nsIContent* aContainer, nsIContent* aChild) {}

    // Signals the start of a sequence of more than 1 event for the specified
    // node. This only happens when events are flushed as all BlockUpdatesScope
    // instances go out of scope
    virtual void OnBeginUpdates(nsIContent* aContent) {};

    // Signals the end of a sequence of events
    virtual void OnEndUpdates() {};
};

#endif /* __nsNativeMenuDocListener_h__ */