Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/events/Clipboard.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/AbstractThread.h"
8
#include "mozilla/dom/Clipboard.h"
9
#include "mozilla/dom/ClipboardBinding.h"
10
#include "mozilla/dom/Promise.h"
11
#include "mozilla/dom/DataTransfer.h"
12
#include "mozilla/dom/DataTransferItemList.h"
13
#include "mozilla/dom/DataTransferItem.h"
14
#include "mozilla/dom/ContentChild.h"
15
#include "nsIClipboard.h"
16
#include "nsISupportsPrimitives.h"
17
#include "nsComponentManagerUtils.h"
18
#include "nsITransferable.h"
19
#include "nsArrayUtils.h"
20
21
22
static mozilla::LazyLogModule gClipboardLog("Clipboard");
23
24
namespace mozilla {
25
namespace dom {
26
27
Clipboard::Clipboard(nsPIDOMWindowInner* aWindow)
28
: DOMEventTargetHelper(aWindow)
29
0
{
30
0
}
31
32
Clipboard::~Clipboard()
33
0
{
34
0
}
35
36
already_AddRefed<Promise>
37
Clipboard::ReadHelper(JSContext* aCx, nsIPrincipal& aSubjectPrincipal,
38
                      ClipboardReadType aClipboardReadType, ErrorResult& aRv)
39
0
{
40
0
  // Create a new promise
41
0
  RefPtr<Promise> p = dom::Promise::Create(GetOwnerGlobal(), aRv);
42
0
  if (aRv.Failed()) {
43
0
    return nullptr;
44
0
  }
45
0
46
0
  // We want to disable security check for automated tests that have the pref
47
0
  //  dom.events.testing.asyncClipboard set to true
48
0
  if (!IsTestingPrefEnabled() && !nsContentUtils::PrincipalHasPermission(&aSubjectPrincipal,
49
0
                                                         nsGkAtoms::clipboardRead)) {
50
0
    MOZ_LOG(GetClipboardLog(), LogLevel::Debug, ("Clipboard, ReadHelper, "
51
0
            "Don't have permissions for reading\n"));
52
0
    p->MaybeRejectWithUndefined();
53
0
    return p.forget();
54
0
  }
55
0
56
0
  // Want isExternal = true in order to use the data transfer object to perform a read
57
0
  RefPtr<DataTransfer> dataTransfer = new DataTransfer(this, ePaste, /* is external */ true,
58
0
                                                       nsIClipboard::kGlobalClipboard);
59
0
60
0
  // Create a new runnable
61
0
  RefPtr<nsIRunnable> r = NS_NewRunnableFunction(
62
0
    "Clipboard::Read",
63
0
    [p, dataTransfer, &aSubjectPrincipal, aClipboardReadType]() {
64
0
      IgnoredErrorResult ier;
65
0
      switch (aClipboardReadType) {
66
0
        case eRead:
67
0
          MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
68
0
                  ("Clipboard, ReadHelper, read case\n"));
69
0
          dataTransfer->FillAllExternalData();
70
0
          // If there are items on the clipboard, data transfer will contain those,
71
0
          // else, data transfer will be empty and we will be resolving with an empty data transfer
72
0
          p->MaybeResolve(dataTransfer);
73
0
          break;
74
0
        case eReadText:
75
0
          MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
76
0
                  ("Clipboard, ReadHelper, read text case\n"));
77
0
          nsAutoString str;
78
0
          dataTransfer->GetData(NS_LITERAL_STRING(kTextMime), str, aSubjectPrincipal, ier);
79
0
          // Either resolve with a string extracted from data transfer item
80
0
          // or resolve with an empty string if nothing was found
81
0
          p->MaybeResolve(str);
82
0
          break;
83
0
      }
84
0
    });
85
0
  // Dispatch the runnable
86
0
  GetParentObject()->Dispatch(TaskCategory::Other, r.forget());
87
0
  return p.forget();
88
0
}
89
90
already_AddRefed<Promise>
91
Clipboard::Read(JSContext* aCx, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv)
92
0
{
93
0
  return ReadHelper(aCx, aSubjectPrincipal, eRead, aRv);
94
0
}
95
96
already_AddRefed<Promise>
97
Clipboard::ReadText(JSContext* aCx, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv)
98
0
{
99
0
  return ReadHelper(aCx, aSubjectPrincipal, eReadText, aRv);
100
0
}
101
102
already_AddRefed<Promise>
103
Clipboard::Write(JSContext* aCx, DataTransfer& aData, nsIPrincipal& aSubjectPrincipal,
104
                 ErrorResult& aRv)
105
0
{
106
0
  // Create a promise
107
0
  RefPtr<Promise> p = dom::Promise::Create(GetOwnerGlobal(), aRv);
108
0
  if (aRv.Failed()) {
109
0
    return nullptr;
110
0
  }
111
0
112
0
  // We want to disable security check for automated tests that have the pref
113
0
  //  dom.events.testing.asyncClipboard set to true
114
0
  if (!IsTestingPrefEnabled() && !nsContentUtils::IsCutCopyAllowed(&aSubjectPrincipal)) {
115
0
    MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
116
0
            ("Clipboard, Write, Not allowed to write to clipboard\n"));
117
0
    p->MaybeRejectWithUndefined();
118
0
    return p.forget();
119
0
  }
120
0
121
0
  // Get the clipboard service
122
0
  nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
123
0
  if (!clipboard) {
124
0
    p->MaybeRejectWithUndefined();
125
0
    return p.forget();
126
0
  }
127
0
128
0
  nsPIDOMWindowInner* owner = GetOwner();
129
0
  nsIDocument* doc          = owner ? owner->GetDoc() : nullptr;
130
0
  nsILoadContext* context   = doc ? doc->GetLoadContext() : nullptr;
131
0
  if (!context) {
132
0
    p->MaybeRejectWithUndefined();
133
0
    return p.forget();
134
0
  }
135
0
136
0
  // Get the transferable
137
0
  RefPtr<nsITransferable> transferable = aData.GetTransferable(0, context);
138
0
  if (!transferable) {
139
0
    p->MaybeRejectWithUndefined();
140
0
    return p.forget();
141
0
  }
142
0
143
0
  // Create a runnable
144
0
  RefPtr<nsIRunnable> r = NS_NewRunnableFunction(
145
0
    "Clipboard::Write",
146
0
    [transferable, p, clipboard]() {
147
0
      nsresult rv = clipboard->SetData(transferable,
148
0
                                       /* owner of the transferable */ nullptr,
149
0
                                       nsIClipboard::kGlobalClipboard);
150
0
      if (NS_FAILED(rv)) {
151
0
        p->MaybeRejectWithUndefined();
152
0
        return;
153
0
      }
154
0
      p->MaybeResolveWithUndefined();
155
0
      return;
156
0
    });
157
0
  // Dispatch the runnable
158
0
  GetParentObject()->Dispatch(TaskCategory::Other, r.forget());
159
0
  return p.forget();
160
0
}
161
162
already_AddRefed<Promise>
163
Clipboard::WriteText(JSContext* aCx, const nsAString& aData,
164
                     nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv)
165
0
{
166
0
  // We create a data transfer with text/plain format so that
167
0
  //  we can reuse Clipboard::Write(...) member function
168
0
  RefPtr<DataTransfer> dataTransfer = new DataTransfer(this, eCopy,
169
0
                                                      /* is external */ true,
170
0
                                                      /* clipboard type */ -1);
171
0
  dataTransfer->SetData(NS_LITERAL_STRING(kTextMime), aData, aSubjectPrincipal, aRv);
172
0
  return Write(aCx, *dataTransfer, aSubjectPrincipal, aRv);
173
0
}
174
175
JSObject*
176
Clipboard::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
177
0
{
178
0
  return Clipboard_Binding::Wrap(aCx, this, aGivenProto);
179
0
}
180
181
/* static */ LogModule*
182
Clipboard::GetClipboardLog()
183
0
{
184
0
  return gClipboardLog;
185
0
}
186
187
bool
188
Clipboard::IsTestingPrefEnabled()
189
0
{
190
0
  static bool sPrefCached = false;
191
0
  static bool sPrefCacheValue = false;
192
0
193
0
  if (!sPrefCached) {
194
0
    sPrefCached = true;
195
0
    Preferences::AddBoolVarCache(&sPrefCacheValue, "dom.events.testing.asyncClipboard");
196
0
  }
197
0
  MOZ_LOG(GetClipboardLog(), LogLevel::Debug,
198
0
            ("Clipboard, Is testing enabled? %d\n", sPrefCacheValue));
199
0
  return sPrefCacheValue;
200
0
}
201
202
203
NS_IMPL_CYCLE_COLLECTION_CLASS(Clipboard)
204
205
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Clipboard,
206
0
                                                  DOMEventTargetHelper)
207
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
208
209
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Clipboard,
210
0
                                                DOMEventTargetHelper)
211
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
212
213
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Clipboard)
214
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
215
216
NS_IMPL_ADDREF_INHERITED(Clipboard, DOMEventTargetHelper)
217
NS_IMPL_RELEASE_INHERITED(Clipboard, DOMEventTargetHelper)
218
219
} // namespace dom
220
} // namespace mozilla