summaryrefslogtreecommitdiffstats
path: root/widget/nsBaseClipboard.h
blob: 6d9c064b76fc1c349a27dbb7f688df17a1dc09e1 (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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 nsBaseClipboard_h__
#define nsBaseClipboard_h__

#include "mozilla/dom/PContent.h"
#include "mozilla/Logging.h"
#include "mozilla/UniquePtr.h"
#include "nsIClipboard.h"
#include "nsITransferable.h"
#include "nsCOMPtr.h"

static mozilla::LazyLogModule sWidgetClipboardLog("WidgetClipboard");
#define CLIPBOARD_LOG(...) \
  MOZ_LOG(sWidgetClipboardLog, LogLevel::Debug, (__VA_ARGS__))
#define CLIPBOARD_LOG_ENABLED() \
  MOZ_LOG_TEST(sWidgetClipboardLog, LogLevel::Debug)

class nsITransferable;
class nsIClipboardOwner;
class nsIWidget;

/**
 * A helper base class to implement nsIClipboard::SetData/AsyncSetData, so that
 * all platform can share the same implementation.
 *
 * XXX this could be merged into nsBaseClipboard once all platform use
 * nsBaseClipboard as base clipboard class to share the common code, see bug
 * 1773707.
 */
class ClipboardSetDataHelper : public nsIClipboard {
 public:
  NS_DECL_ISUPPORTS

  ClipboardSetDataHelper() = default;

  // nsIClipboard
  NS_IMETHOD SetData(nsITransferable* aTransferable, nsIClipboardOwner* aOwner,
                     int32_t aWhichClipboard) override;
  NS_IMETHOD AsyncSetData(int32_t aWhichClipboard,
                          nsIAsyncSetClipboardDataCallback* aCallback,
                          nsIAsyncSetClipboardData** _retval) override final;

 protected:
  virtual ~ClipboardSetDataHelper();

  // Implement the native clipboard behavior.
  NS_IMETHOD SetNativeClipboardData(nsITransferable* aTransferable,
                                    nsIClipboardOwner* aOwner,
                                    int32_t aWhichClipboard) = 0;

  class AsyncSetClipboardData final : public nsIAsyncSetClipboardData {
   public:
    NS_DECL_ISUPPORTS
    NS_DECL_NSIASYNCSETCLIPBOARDDATA

    AsyncSetClipboardData(int32_t aClipboardType,
                          ClipboardSetDataHelper* aClipboard,
                          nsIAsyncSetClipboardDataCallback* aCallback);

   private:
    virtual ~AsyncSetClipboardData() = default;
    bool IsValid() const {
      // If this request is no longer valid, the callback should be notified.
      MOZ_ASSERT_IF(!mClipboard, !mCallback);
      return !!mClipboard;
    }
    void MaybeNotifyCallback(nsresult aResult);

    // The clipboard type defined in nsIClipboard.
    int32_t mClipboardType;
    // It is safe to use a raw pointer as it will be nullified (by calling
    // NotifyCallback()) once ClipboardSetDataHelper stops tracking us. This is
    // also used to indicate whether this request is valid.
    ClipboardSetDataHelper* mClipboard;
    // mCallback will be nullified once the callback is notified to ensure the
    // callback is only notified once.
    nsCOMPtr<nsIAsyncSetClipboardDataCallback> mCallback;
  };

 private:
  void RejectPendingAsyncSetDataRequestIfAny(int32_t aClipboardType);

  // Track the pending request for each clipboard type separately. And only need
  // to track the latest request for each clipboard type as the prior pending
  // request will be canceled when a new request is made.
  RefPtr<AsyncSetClipboardData>
      mPendingWriteRequests[nsIClipboard::kClipboardTypeCount];
};

/**
 * A base clipboard class for Windows and Cocoa widget.
 */
class nsBaseClipboard : public ClipboardSetDataHelper {
 public:
  explicit nsBaseClipboard(
      const mozilla::dom::ClipboardCapabilities& aClipboardCaps);

  // nsISupports
  NS_DECL_ISUPPORTS_INHERITED

  // nsIClipboard
  NS_IMETHOD SetData(nsITransferable* aTransferable, nsIClipboardOwner* anOwner,
                     int32_t aWhichClipboard) override final;
  NS_IMETHOD GetData(nsITransferable* aTransferable,
                     int32_t aWhichClipboard) override;
  NS_IMETHOD EmptyClipboard(int32_t aWhichClipboard) override;
  NS_IMETHOD HasDataMatchingFlavors(const nsTArray<nsCString>& aFlavorList,
                                    int32_t aWhichClipboard,
                                    bool* _retval) override;
  NS_IMETHOD IsClipboardTypeSupported(int32_t aWhichClipboard,
                                      bool* aRetval) override final;
  RefPtr<mozilla::GenericPromise> AsyncGetData(
      nsITransferable* aTransferable, int32_t aWhichClipboard) override;
  RefPtr<DataFlavorsPromise> AsyncHasDataMatchingFlavors(
      const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard) override;

 protected:
  virtual ~nsBaseClipboard() = default;

  // Implement the native clipboard behavior.
  NS_IMETHOD GetNativeClipboardData(nsITransferable* aTransferable,
                                    int32_t aWhichClipboard) = 0;

  class ClipboardCache final {
   public:
    ~ClipboardCache() {
      // In order to notify the old clipboard owner.
      Clear();
    }

    /**
     * Clear the cached transferable and notify the original clipboard owner
     * that it has lost ownership.
     */
    void Clear();
    void Update(nsITransferable* aTransferable,
                nsIClipboardOwner* aClipboardOwner) {
      // Clear first to notify the old clipboard owner.
      Clear();
      mTransferable = aTransferable;
      mClipboardOwner = aClipboardOwner;
    }
    nsITransferable* GetTransferable() const { return mTransferable; }
    nsIClipboardOwner* GetClipboardOwner() const { return mClipboardOwner; }

   private:
    nsCOMPtr<nsITransferable> mTransferable;
    nsCOMPtr<nsIClipboardOwner> mClipboardOwner;
  };

  mozilla::UniquePtr<ClipboardCache> mCaches[nsIClipboard::kClipboardTypeCount];
  bool mEmptyingForSetData = false;

 private:
  const mozilla::dom::ClipboardCapabilities mClipboardCaps;
  bool mIgnoreEmptyNotification = false;
};

#endif  // nsBaseClipboard_h__