summaryrefslogtreecommitdiffstats
path: root/dom/events/ClipboardItem.h
blob: 9fe45bf93643b7840f2df4b7ee9ab56badf0e607 (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
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 mozilla_dom_ClipboardItem_h_
#define mozilla_dom_ClipboardItem_h_

#include "mozilla/dom/Blob.h"
#include "mozilla/dom/ClipboardBinding.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/MozPromise.h"

#include "nsIClipboard.h"
#include "nsWrapperCache.h"

class nsITransferable;

namespace mozilla::dom {

struct ClipboardItemOptions;
template <typename KeyType, typename ValueType>
class Record;
class Promise;

class ClipboardItem final : public nsWrapperCache {
 public:
  class ItemEntry final : public PromiseNativeHandler,
                          public nsIAsyncClipboardRequestCallback {
   public:
    using GetDataPromise =
        MozPromise<OwningStringOrBlob, nsresult, /* IsExclusive = */ true>;

    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    NS_DECL_NSIASYNCCLIPBOARDREQUESTCALLBACK
    NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ItemEntry, PromiseNativeHandler)

    explicit ItemEntry(nsIGlobalObject* aGlobal, const nsAString& aType)
        : mGlobal(aGlobal), mType(aType) {
      MOZ_ASSERT(mGlobal);
    }
    ItemEntry(nsIGlobalObject* aGlobal, const nsAString& aType,
              const nsAString& aData)
        : ItemEntry(aGlobal, aType) {
      mLoadResult.emplace(NS_OK);
      mData.SetAsString() = aData;
    }

    // PromiseNativeHandler
    void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
                          ErrorResult& aRv) override;
    void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
                          ErrorResult& aRv) override;

    const nsString& Type() const { return mType; }
    RefPtr<GetDataPromise> GetData();

    //  Load data from system clipboard.
    void LoadDataFromSystemClipboard(nsIAsyncGetClipboardData* aDataGetter);
    void LoadDataFromDataPromise(Promise& aDataPromise);

    // If clipboard data is in the process of loading from either system
    // clipboard or data promise, add `aPromise` to the pending list which will
    // be resolved/rejected later when process is finished. Otherwise,
    // resolve/reject `aPromise` based on cached result and data.
    void ReactGetTypePromise(Promise& aPromise);

   private:
    ~ItemEntry() {
      if (!mPendingGetDataRequests.IsEmpty()) {
        RejectPendingPromises(NS_ERROR_FAILURE);
      }
    };

    void MaybeResolveGetTypePromise(const OwningStringOrBlob& aData,
                                    Promise& aPromise);
    void MaybeResolvePendingPromises(OwningStringOrBlob&& aData);
    void RejectPendingPromises(nsresult rv);

    nsCOMPtr<nsIGlobalObject> mGlobal;

    // MIME type of this entry.
    nsString mType;

    // Cache the loading result.
    OwningStringOrBlob mData;
    Maybe<nsresult> mLoadResult;

    // Indicates if the data is still being loaded.
    bool mIsLoadingData = false;
    nsCOMPtr<nsITransferable> mTransferable;

    // Pending promises for data retrieval requests.
    nsTArray<MozPromiseHolder<GetDataPromise>> mPendingGetDataRequests;
    nsTArray<RefPtr<Promise>> mPendingGetTypeRequests;
  };

  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ClipboardItem)
  NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(ClipboardItem)

  ClipboardItem(nsISupports* aOwner, dom::PresentationStyle aPresentationStyle,
                nsTArray<RefPtr<ItemEntry>>&& aItems);

  static already_AddRefed<ClipboardItem> Constructor(
      const GlobalObject& aGlobal,
      const Record<nsString, OwningNonNull<Promise>>& aItems,
      const ClipboardItemOptions& aOptions, ErrorResult& aRv);

  dom::PresentationStyle PresentationStyle() const {
    return mPresentationStyle;
  };
  void GetTypes(nsTArray<nsString>& aTypes) const;

  already_AddRefed<Promise> GetType(const nsAString& aType, ErrorResult& aRv);

  nsISupports* GetParentObject() const { return mOwner; }

  JSObject* WrapObject(JSContext* aCx,
                       JS::Handle<JSObject*> aGivenProto) override;

  const nsTArray<RefPtr<ItemEntry>>& Entries() const { return mItems; }

 private:
  ~ClipboardItem() = default;

  nsCOMPtr<nsISupports> mOwner;
  dom::PresentationStyle mPresentationStyle;
  nsTArray<RefPtr<ItemEntry>> mItems;
};

}  // namespace mozilla::dom

#endif  // mozilla_dom_ClipboardItem_h_