Line data Source code
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/ArrayUtils.h"
8 : #include "mozilla/BasicEvents.h"
9 :
10 : #include "DataTransfer.h"
11 :
12 : #include "nsIDOMDocument.h"
13 : #include "nsISupportsPrimitives.h"
14 : #include "nsIScriptSecurityManager.h"
15 : #include "mozilla/dom/DOMStringList.h"
16 : #include "nsArray.h"
17 : #include "nsError.h"
18 : #include "nsIDragService.h"
19 : #include "nsIClipboard.h"
20 : #include "nsContentUtils.h"
21 : #include "nsIContent.h"
22 : #include "nsIBinaryInputStream.h"
23 : #include "nsIBinaryOutputStream.h"
24 : #include "nsIStorageStream.h"
25 : #include "nsStringStream.h"
26 : #include "nsCRT.h"
27 : #include "nsIScriptObjectPrincipal.h"
28 : #include "nsIScriptContext.h"
29 : #include "nsIDocument.h"
30 : #include "nsIScriptGlobalObject.h"
31 : #include "nsVariant.h"
32 : #include "mozilla/dom/ContentChild.h"
33 : #include "mozilla/dom/DataTransferBinding.h"
34 : #include "mozilla/dom/DataTransferItemList.h"
35 : #include "mozilla/dom/Directory.h"
36 : #include "mozilla/dom/Element.h"
37 : #include "mozilla/dom/FileList.h"
38 : #include "mozilla/dom/BindingUtils.h"
39 : #include "mozilla/dom/OSFileSystem.h"
40 : #include "mozilla/dom/Promise.h"
41 : #include "nsNetUtil.h"
42 :
43 : namespace mozilla {
44 : namespace dom {
45 :
46 : NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer)
47 :
48 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
49 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
50 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems)
51 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget)
52 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage)
53 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
54 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
55 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer)
56 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
57 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems)
58 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget)
59 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage)
60 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
61 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DataTransfer)
62 :
63 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransfer)
64 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransfer)
65 :
66 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransfer)
67 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
68 0 : NS_INTERFACE_MAP_ENTRY(mozilla::dom::DataTransfer)
69 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMDataTransfer)
70 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDataTransfer)
71 0 : NS_INTERFACE_MAP_END
72 :
73 : // the size of the array
74 : const char DataTransfer::sEffects[8][9] = {
75 : "none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"
76 : };
77 :
78 : // Used for custom clipboard types.
79 : enum CustomClipboardTypeId {
80 : eCustomClipboardTypeId_None,
81 : eCustomClipboardTypeId_String
82 : };
83 :
84 0 : DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
85 0 : bool aIsExternal, int32_t aClipboardType)
86 : : mParent(aParent)
87 : , mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE)
88 : , mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
89 : , mEventMessage(aEventMessage)
90 : , mCursorState(false)
91 : , mReadOnly(true)
92 : , mIsExternal(aIsExternal)
93 : , mUserCancelled(false)
94 : , mIsCrossDomainSubFrameDrop(false)
95 : , mClipboardType(aClipboardType)
96 : , mDragImageX(0)
97 0 : , mDragImageY(0)
98 : {
99 0 : mItems = new DataTransferItemList(this, aIsExternal);
100 : // For these events, we want to be able to add data to the data transfer, so
101 : // clear the readonly state. Otherwise, the data is already present. For
102 : // external usage, cache the data from the native clipboard or drag.
103 0 : if (aEventMessage == eCut ||
104 0 : aEventMessage == eCopy ||
105 : aEventMessage == eDragStart) {
106 0 : mReadOnly = false;
107 0 : } else if (mIsExternal) {
108 0 : if (aEventMessage == ePasteNoFormatting) {
109 0 : mEventMessage = ePaste;
110 0 : CacheExternalClipboardFormats(true);
111 0 : } else if (aEventMessage == ePaste) {
112 0 : CacheExternalClipboardFormats(false);
113 0 : } else if (aEventMessage >= eDragDropEventFirst &&
114 0 : aEventMessage <= eDragDropEventLast) {
115 0 : CacheExternalDragFormats();
116 : }
117 : }
118 0 : }
119 :
120 0 : DataTransfer::DataTransfer(nsISupports* aParent,
121 : EventMessage aEventMessage,
122 : const uint32_t aEffectAllowed,
123 : bool aCursorState,
124 : bool aIsExternal,
125 : bool aUserCancelled,
126 : bool aIsCrossDomainSubFrameDrop,
127 : int32_t aClipboardType,
128 : DataTransferItemList* aItems,
129 : Element* aDragImage,
130 : uint32_t aDragImageX,
131 0 : uint32_t aDragImageY)
132 : : mParent(aParent)
133 : , mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE)
134 : , mEffectAllowed(aEffectAllowed)
135 : , mEventMessage(aEventMessage)
136 : , mCursorState(aCursorState)
137 : , mReadOnly(true)
138 : , mIsExternal(aIsExternal)
139 : , mUserCancelled(aUserCancelled)
140 : , mIsCrossDomainSubFrameDrop(aIsCrossDomainSubFrameDrop)
141 : , mClipboardType(aClipboardType)
142 : , mDragImage(aDragImage)
143 : , mDragImageX(aDragImageX)
144 0 : , mDragImageY(aDragImageY)
145 : {
146 0 : MOZ_ASSERT(mParent);
147 0 : MOZ_ASSERT(aItems);
148 :
149 : // We clone the items array after everything else, so that it has a valid
150 : // mParent value
151 0 : mItems = aItems->Clone(this);
152 : // The items are copied from aItems into mItems. There is no need to copy
153 : // the actual data in the items as the data transfer will be read only. The
154 : // dragstart event is the only time when items are
155 : // modifiable, but those events should have been using the first constructor
156 : // above.
157 0 : NS_ASSERTION(aEventMessage != eDragStart,
158 : "invalid event type for DataTransfer constructor");
159 0 : }
160 :
161 0 : DataTransfer::~DataTransfer()
162 0 : {}
163 :
164 : // static
165 : already_AddRefed<DataTransfer>
166 0 : DataTransfer::Constructor(const GlobalObject& aGlobal,
167 : const nsAString& aEventType, bool aIsExternal,
168 : ErrorResult& aRv)
169 : {
170 0 : nsAutoCString onEventType("on");
171 0 : AppendUTF16toUTF8(aEventType, onEventType);
172 0 : nsCOMPtr<nsIAtom> eventTypeAtom = NS_Atomize(onEventType);
173 0 : if (!eventTypeAtom) {
174 0 : aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
175 0 : return nullptr;
176 : }
177 :
178 0 : EventMessage eventMessage = nsContentUtils::GetEventMessage(eventTypeAtom);
179 0 : RefPtr<DataTransfer> transfer = new DataTransfer(aGlobal.GetAsSupports(),
180 : eventMessage, aIsExternal,
181 0 : -1);
182 0 : return transfer.forget();
183 : }
184 :
185 : JSObject*
186 0 : DataTransfer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
187 : {
188 0 : return DataTransferBinding::Wrap(aCx, this, aGivenProto);
189 : }
190 :
191 : NS_IMETHODIMP
192 0 : DataTransfer::GetDropEffect(nsAString& aDropEffect)
193 : {
194 0 : nsString dropEffect;
195 0 : GetDropEffect(dropEffect);
196 0 : aDropEffect = dropEffect;
197 0 : return NS_OK;
198 : }
199 :
200 : NS_IMETHODIMP
201 0 : DataTransfer::SetDropEffect(const nsAString& aDropEffect)
202 : {
203 : // the drop effect can only be 'none', 'copy', 'move' or 'link'.
204 0 : for (uint32_t e = 0; e <= nsIDragService::DRAGDROP_ACTION_LINK; e++) {
205 0 : if (aDropEffect.EqualsASCII(sEffects[e])) {
206 : // don't allow copyMove
207 0 : if (e != (nsIDragService::DRAGDROP_ACTION_COPY |
208 : nsIDragService::DRAGDROP_ACTION_MOVE)) {
209 0 : mDropEffect = e;
210 : }
211 0 : break;
212 : }
213 : }
214 :
215 0 : return NS_OK;
216 : }
217 :
218 : NS_IMETHODIMP
219 0 : DataTransfer::GetEffectAllowed(nsAString& aEffectAllowed)
220 : {
221 0 : nsString effectAllowed;
222 0 : GetEffectAllowed(effectAllowed);
223 0 : aEffectAllowed = effectAllowed;
224 0 : return NS_OK;
225 : }
226 :
227 : NS_IMETHODIMP
228 0 : DataTransfer::SetEffectAllowed(const nsAString& aEffectAllowed)
229 : {
230 0 : if (aEffectAllowed.EqualsLiteral("uninitialized")) {
231 0 : mEffectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
232 0 : return NS_OK;
233 : }
234 :
235 : static_assert(nsIDragService::DRAGDROP_ACTION_NONE == 0,
236 : "DRAGDROP_ACTION_NONE constant is wrong");
237 : static_assert(nsIDragService::DRAGDROP_ACTION_COPY == 1,
238 : "DRAGDROP_ACTION_COPY constant is wrong");
239 : static_assert(nsIDragService::DRAGDROP_ACTION_MOVE == 2,
240 : "DRAGDROP_ACTION_MOVE constant is wrong");
241 : static_assert(nsIDragService::DRAGDROP_ACTION_LINK == 4,
242 : "DRAGDROP_ACTION_LINK constant is wrong");
243 :
244 0 : for (uint32_t e = 0; e < ArrayLength(sEffects); e++) {
245 0 : if (aEffectAllowed.EqualsASCII(sEffects[e])) {
246 0 : mEffectAllowed = e;
247 0 : break;
248 : }
249 : }
250 :
251 0 : return NS_OK;
252 : }
253 :
254 : NS_IMETHODIMP
255 0 : DataTransfer::GetDropEffectInt(uint32_t* aDropEffect)
256 : {
257 0 : *aDropEffect = mDropEffect;
258 0 : return NS_OK;
259 : }
260 :
261 : NS_IMETHODIMP
262 0 : DataTransfer::SetDropEffectInt(uint32_t aDropEffect)
263 : {
264 0 : mDropEffect = aDropEffect;
265 0 : return NS_OK;
266 : }
267 :
268 : NS_IMETHODIMP
269 0 : DataTransfer::GetEffectAllowedInt(uint32_t* aEffectAllowed)
270 : {
271 0 : *aEffectAllowed = mEffectAllowed;
272 0 : return NS_OK;
273 : }
274 :
275 : NS_IMETHODIMP
276 0 : DataTransfer::SetEffectAllowedInt(uint32_t aEffectAllowed)
277 : {
278 0 : mEffectAllowed = aEffectAllowed;
279 0 : return NS_OK;
280 : }
281 :
282 : NS_IMETHODIMP
283 0 : DataTransfer::GetMozUserCancelled(bool* aUserCancelled)
284 : {
285 0 : *aUserCancelled = MozUserCancelled();
286 0 : return NS_OK;
287 : }
288 :
289 : already_AddRefed<FileList>
290 0 : DataTransfer::GetFiles(nsIPrincipal& aSubjectPrincipal,
291 : ErrorResult& aRv)
292 : {
293 0 : return mItems->Files(&aSubjectPrincipal);
294 : }
295 :
296 : NS_IMETHODIMP
297 0 : DataTransfer::GetFiles(nsIDOMFileList** aFileList)
298 : {
299 0 : if (!aFileList) {
300 0 : return NS_ERROR_FAILURE;
301 : }
302 :
303 : // The XPCOM interface is only avaliable to system code, and thus we can
304 : // assume the system principal. This is consistent with the previous behavour
305 : // of this function, which also assumed the system principal.
306 : //
307 : // This code is also called from C++ code, which expects it to have a System
308 : // Principal, and thus the SubjectPrincipal cannot be used.
309 0 : RefPtr<FileList> files = mItems->Files(nsContentUtils::GetSystemPrincipal());
310 :
311 0 : files.forget(aFileList);
312 0 : return NS_OK;
313 : }
314 :
315 : void
316 0 : DataTransfer::GetTypes(nsTArray<nsString>& aTypes, CallerType aCallerType) const
317 : {
318 : // When called from bindings, aTypes will be empty, but since we might have
319 : // Gecko-internal callers too, clear it to be safe.
320 0 : aTypes.Clear();
321 :
322 0 : const nsTArray<RefPtr<DataTransferItem>>* items = mItems->MozItemsAt(0);
323 0 : if (NS_WARN_IF(!items)) {
324 0 : return;
325 : }
326 :
327 0 : for (uint32_t i = 0; i < items->Length(); i++) {
328 0 : DataTransferItem* item = items->ElementAt(i);
329 0 : MOZ_ASSERT(item);
330 :
331 0 : if (item->ChromeOnly() && aCallerType != CallerType::System) {
332 0 : continue;
333 : }
334 :
335 : // NOTE: The reason why we get the internal type here is because we want
336 : // kFileMime to appear in the types list for backwards compatibility
337 : // reasons.
338 0 : nsAutoString type;
339 0 : item->GetInternalType(type);
340 0 : if (item->Kind() != DataTransferItem::KIND_FILE || type.EqualsASCII(kFileMime)) {
341 : // If the entry has kind KIND_STRING or KIND_OTHER we want to add it to the list.
342 0 : aTypes.AppendElement(type);
343 : }
344 : }
345 :
346 0 : for (uint32_t i = 0; i < mItems->Length(); ++i) {
347 0 : bool found = false;
348 0 : DataTransferItem* item = mItems->IndexedGetter(i, found);
349 0 : MOZ_ASSERT(found);
350 0 : if (item->Kind() != DataTransferItem::KIND_FILE) {
351 0 : continue;
352 : }
353 0 : aTypes.AppendElement(NS_LITERAL_STRING("Files"));
354 0 : break;
355 : }
356 : }
357 :
358 : void
359 0 : DataTransfer::GetData(const nsAString& aFormat, nsAString& aData,
360 : nsIPrincipal& aSubjectPrincipal,
361 : ErrorResult& aRv)
362 : {
363 : // return an empty string if data for the format was not found
364 0 : aData.Truncate();
365 :
366 0 : nsCOMPtr<nsIVariant> data;
367 : nsresult rv =
368 0 : GetDataAtInternal(aFormat, 0, &aSubjectPrincipal,
369 0 : getter_AddRefs(data));
370 0 : if (NS_FAILED(rv)) {
371 0 : if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) {
372 0 : aRv.Throw(rv);
373 : }
374 0 : return;
375 : }
376 :
377 0 : if (data) {
378 0 : nsAutoString stringdata;
379 0 : data->GetAsAString(stringdata);
380 :
381 : // for the URL type, parse out the first URI from the list. The URIs are
382 : // separated by newlines
383 0 : nsAutoString lowercaseFormat;
384 0 : nsContentUtils::ASCIIToLower(aFormat, lowercaseFormat);
385 :
386 0 : if (lowercaseFormat.EqualsLiteral("url")) {
387 0 : int32_t lastidx = 0, idx;
388 0 : int32_t length = stringdata.Length();
389 0 : while (lastidx < length) {
390 0 : idx = stringdata.FindChar('\n', lastidx);
391 : // lines beginning with # are comments
392 0 : if (stringdata[lastidx] == '#') {
393 0 : if (idx == -1) {
394 0 : break;
395 : }
396 : }
397 : else {
398 0 : if (idx == -1) {
399 0 : aData.Assign(Substring(stringdata, lastidx));
400 : } else {
401 0 : aData.Assign(Substring(stringdata, lastidx, idx - lastidx));
402 : }
403 0 : aData = nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(aData,
404 0 : true);
405 0 : return;
406 : }
407 0 : lastidx = idx + 1;
408 : }
409 : }
410 : else {
411 0 : aData = stringdata;
412 : }
413 : }
414 : }
415 :
416 : void
417 0 : DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData,
418 : nsIPrincipal& aSubjectPrincipal,
419 : ErrorResult& aRv)
420 : {
421 0 : RefPtr<nsVariantCC> variant = new nsVariantCC();
422 0 : variant->SetAsAString(aData);
423 :
424 0 : aRv = SetDataAtInternal(aFormat, variant, 0, &aSubjectPrincipal);
425 0 : }
426 :
427 : void
428 0 : DataTransfer::ClearData(const Optional<nsAString>& aFormat,
429 : nsIPrincipal& aSubjectPrincipal,
430 : ErrorResult& aRv)
431 : {
432 0 : if (mReadOnly) {
433 0 : aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
434 0 : return;
435 : }
436 :
437 0 : if (MozItemCount() == 0) {
438 0 : return;
439 : }
440 :
441 0 : if (aFormat.WasPassed()) {
442 0 : MozClearDataAtHelper(aFormat.Value(), 0, aSubjectPrincipal, aRv);
443 : } else {
444 0 : MozClearDataAtHelper(EmptyString(), 0, aSubjectPrincipal, aRv);
445 : }
446 : }
447 :
448 : NS_IMETHODIMP
449 0 : DataTransfer::GetMozItemCount(uint32_t* aCount)
450 : {
451 0 : *aCount = MozItemCount();
452 0 : return NS_OK;
453 : }
454 :
455 : NS_IMETHODIMP
456 0 : DataTransfer::GetMozCursor(nsAString& aCursorState)
457 : {
458 0 : nsString cursor;
459 0 : GetMozCursor(cursor);
460 0 : aCursorState = cursor;
461 0 : return NS_OK;
462 : }
463 :
464 : NS_IMETHODIMP
465 0 : DataTransfer::SetMozCursor(const nsAString& aCursorState)
466 : {
467 : // Lock the cursor to an arrow during the drag.
468 0 : mCursorState = aCursorState.EqualsLiteral("default");
469 :
470 0 : return NS_OK;
471 : }
472 :
473 : already_AddRefed<nsINode>
474 0 : DataTransfer::GetMozSourceNode()
475 : {
476 0 : nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
477 0 : if (!dragSession) {
478 0 : return nullptr;
479 : }
480 :
481 0 : nsCOMPtr<nsIDOMNode> sourceNode;
482 0 : dragSession->GetSourceNode(getter_AddRefs(sourceNode));
483 0 : nsCOMPtr<nsINode> node = do_QueryInterface(sourceNode);
484 0 : if (node && !nsContentUtils::LegacyIsCallerNativeCode()
485 0 : && !nsContentUtils::CanCallerAccess(node)) {
486 0 : return nullptr;
487 : }
488 :
489 0 : return node.forget();
490 : }
491 :
492 : NS_IMETHODIMP
493 0 : DataTransfer::GetMozSourceNode(nsIDOMNode** aSourceNode)
494 : {
495 0 : nsCOMPtr<nsINode> sourceNode = GetMozSourceNode();
496 0 : if (!sourceNode) {
497 0 : *aSourceNode = nullptr;
498 0 : return NS_OK;
499 : }
500 :
501 0 : return CallQueryInterface(sourceNode, aSourceNode);
502 : }
503 :
504 : already_AddRefed<DOMStringList>
505 0 : DataTransfer::MozTypesAt(uint32_t aIndex, CallerType aCallerType,
506 : ErrorResult& aRv) const
507 : {
508 : // Only the first item is valid for clipboard events
509 0 : if (aIndex > 0 &&
510 0 : (mEventMessage == eCut || mEventMessage == eCopy ||
511 0 : mEventMessage == ePaste)) {
512 0 : aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
513 0 : return nullptr;
514 : }
515 :
516 0 : RefPtr<DOMStringList> types = new DOMStringList();
517 0 : if (aIndex < MozItemCount()) {
518 : // note that you can retrieve the types regardless of their principal
519 0 : const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(aIndex);
520 :
521 0 : bool addFile = false;
522 0 : for (uint32_t i = 0; i < items.Length(); i++) {
523 0 : if (items[i]->ChromeOnly() && aCallerType != CallerType::System) {
524 0 : continue;
525 : }
526 :
527 : // NOTE: The reason why we get the internal type here is because we want
528 : // kFileMime to appear in the types list for backwards compatibility
529 : // reasons.
530 0 : nsAutoString type;
531 0 : items[i]->GetInternalType(type);
532 0 : if (NS_WARN_IF(!types->Add(type))) {
533 0 : aRv.Throw(NS_ERROR_FAILURE);
534 0 : return nullptr;
535 : }
536 :
537 0 : if (items[i]->Kind() == DataTransferItem::KIND_FILE) {
538 0 : addFile = true;
539 : }
540 : }
541 :
542 0 : if (addFile) {
543 0 : types->Add(NS_LITERAL_STRING("Files"));
544 : }
545 : }
546 :
547 0 : return types.forget();
548 : }
549 :
550 : nsresult
551 0 : DataTransfer::GetDataAtNoSecurityCheck(const nsAString& aFormat,
552 : uint32_t aIndex,
553 : nsIVariant** aData)
554 : {
555 0 : return GetDataAtInternal(aFormat, aIndex,
556 0 : nsContentUtils::GetSystemPrincipal(), aData);
557 : }
558 :
559 : nsresult
560 0 : DataTransfer::GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
561 : nsIPrincipal* aSubjectPrincipal,
562 : nsIVariant** aData)
563 : {
564 0 : *aData = nullptr;
565 :
566 0 : if (aFormat.IsEmpty()) {
567 0 : return NS_OK;
568 : }
569 :
570 0 : if (aIndex >= MozItemCount()) {
571 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
572 : }
573 :
574 : // Only the first item is valid for clipboard events
575 0 : if (aIndex > 0 &&
576 0 : (mEventMessage == eCut || mEventMessage == eCopy ||
577 0 : mEventMessage == ePaste)) {
578 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
579 : }
580 :
581 0 : nsAutoString format;
582 0 : GetRealFormat(aFormat, format);
583 :
584 0 : MOZ_ASSERT(aSubjectPrincipal);
585 :
586 0 : RefPtr<DataTransferItem> item = mItems->MozItemByTypeAt(format, aIndex);
587 0 : if (!item) {
588 : // The index exists but there's no data for the specified format, in this
589 : // case we just return undefined
590 0 : return NS_OK;
591 : }
592 :
593 : // If we have chrome only content, and we aren't chrome, don't allow access
594 0 : if (!nsContentUtils::IsSystemPrincipal(aSubjectPrincipal) && item->ChromeOnly()) {
595 0 : return NS_OK;
596 : }
597 :
598 : // DataTransferItem::Data() handles the principal checks
599 0 : ErrorResult result;
600 0 : nsCOMPtr<nsIVariant> data = item->Data(aSubjectPrincipal, result);
601 0 : if (NS_WARN_IF(!data || result.Failed())) {
602 0 : return result.StealNSResult();
603 : }
604 :
605 0 : data.forget(aData);
606 0 : return NS_OK;
607 : }
608 :
609 : void
610 0 : DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
611 : uint32_t aIndex,
612 : JS::MutableHandle<JS::Value> aRetval,
613 : nsIPrincipal& aSubjectPrincipal,
614 : mozilla::ErrorResult& aRv)
615 : {
616 0 : nsCOMPtr<nsIVariant> data;
617 0 : aRv = GetDataAtInternal(aFormat, aIndex, &aSubjectPrincipal,
618 0 : getter_AddRefs(data));
619 0 : if (aRv.Failed()) {
620 0 : return;
621 : }
622 :
623 0 : if (!data) {
624 0 : aRetval.setNull();
625 0 : return;
626 : }
627 :
628 0 : JS::Rooted<JS::Value> result(aCx);
629 0 : if (!VariantToJsval(aCx, data, aRetval)) {
630 0 : aRv = NS_ERROR_FAILURE;
631 0 : return;
632 : }
633 : }
634 :
635 : /* static */ bool
636 0 : DataTransfer::PrincipalMaySetData(const nsAString& aType,
637 : nsIVariant* aData,
638 : nsIPrincipal* aPrincipal)
639 : {
640 0 : if (!nsContentUtils::IsSystemPrincipal(aPrincipal)) {
641 0 : DataTransferItem::eKind kind = DataTransferItem::KindFromData(aData);
642 0 : if (kind == DataTransferItem::KIND_OTHER) {
643 0 : NS_WARNING("Disallowing adding non string/file types to DataTransfer");
644 0 : return false;
645 : }
646 :
647 0 : if (aType.EqualsASCII(kFileMime) ||
648 0 : aType.EqualsASCII(kFilePromiseMime)) {
649 0 : NS_WARNING("Disallowing adding x-moz-file or x-moz-file-promize types to DataTransfer");
650 0 : return false;
651 : }
652 : }
653 0 : return true;
654 : }
655 :
656 : void
657 0 : DataTransfer::TypesListMayHaveChanged()
658 : {
659 0 : DataTransferBinding::ClearCachedTypesValue(this);
660 0 : }
661 :
662 : nsresult
663 0 : DataTransfer::SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData,
664 : uint32_t aIndex,
665 : nsIPrincipal* aSubjectPrincipal)
666 : {
667 0 : if (aFormat.IsEmpty()) {
668 0 : return NS_OK;
669 : }
670 :
671 0 : if (mReadOnly) {
672 0 : return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
673 : }
674 :
675 : // Specifying an index less than the current length will replace an existing
676 : // item. Specifying an index equal to the current length will add a new item.
677 0 : if (aIndex > MozItemCount()) {
678 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
679 : }
680 :
681 : // Only the first item is valid for clipboard events
682 0 : if (aIndex > 0 &&
683 0 : (mEventMessage == eCut || mEventMessage == eCopy ||
684 0 : mEventMessage == ePaste)) {
685 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
686 : }
687 :
688 : // Don't allow the custom type to be assigned.
689 0 : if (aFormat.EqualsLiteral(kCustomTypesMime)) {
690 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
691 : }
692 :
693 0 : if (!PrincipalMaySetData(aFormat, aData, aSubjectPrincipal)) {
694 0 : return NS_ERROR_DOM_SECURITY_ERR;
695 : }
696 :
697 0 : return SetDataWithPrincipal(aFormat, aData, aIndex, aSubjectPrincipal);
698 : }
699 :
700 : void
701 0 : DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
702 : JS::Handle<JS::Value> aData, uint32_t aIndex,
703 : nsIPrincipal& aSubjectPrincipal,
704 : ErrorResult& aRv)
705 : {
706 0 : nsCOMPtr<nsIVariant> data;
707 0 : aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData,
708 0 : getter_AddRefs(data));
709 0 : if (!aRv.Failed()) {
710 0 : aRv = SetDataAtInternal(aFormat, data, aIndex, &aSubjectPrincipal);
711 : }
712 0 : }
713 :
714 : void
715 0 : DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
716 : nsIPrincipal& aSubjectPrincipal,
717 : ErrorResult& aRv)
718 : {
719 0 : if (mReadOnly) {
720 0 : aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
721 0 : return;
722 : }
723 :
724 0 : if (aIndex >= MozItemCount()) {
725 0 : aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
726 0 : return;
727 : }
728 :
729 : // Only the first item is valid for clipboard events
730 0 : if (aIndex > 0 &&
731 0 : (mEventMessage == eCut || mEventMessage == eCopy ||
732 0 : mEventMessage == ePaste)) {
733 0 : aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
734 0 : return;
735 : }
736 :
737 0 : MozClearDataAtHelper(aFormat, aIndex, aSubjectPrincipal, aRv);
738 :
739 : // If we just cleared the 0-th index, and there are still more than 1 indexes
740 : // remaining, MozClearDataAt should cause the 1st index to become the 0th
741 : // index. This should _only_ happen when the MozClearDataAt function is
742 : // explicitly called by script, as this behavior is inconsistent with spec.
743 : // (however, so is the MozClearDataAt API)
744 :
745 0 : if (aIndex == 0 && mItems->MozItemCount() > 1 &&
746 0 : mItems->MozItemsAt(0)->Length() == 0) {
747 0 : mItems->PopIndexZero();
748 : }
749 : }
750 :
751 : void
752 0 : DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
753 : nsIPrincipal& aSubjectPrincipal,
754 : ErrorResult& aRv)
755 : {
756 0 : MOZ_ASSERT(!mReadOnly);
757 0 : MOZ_ASSERT(aIndex < MozItemCount());
758 0 : MOZ_ASSERT(aIndex == 0 ||
759 : (mEventMessage != eCut && mEventMessage != eCopy &&
760 : mEventMessage != ePaste));
761 :
762 0 : nsAutoString format;
763 0 : GetRealFormat(aFormat, format);
764 :
765 0 : mItems->MozRemoveByTypeAt(format, aIndex, aSubjectPrincipal, aRv);
766 0 : }
767 :
768 : void
769 0 : DataTransfer::SetDragImage(Element& aImage, int32_t aX, int32_t aY)
770 : {
771 0 : if (!mReadOnly) {
772 0 : mDragImage = &aImage;
773 0 : mDragImageX = aX;
774 0 : mDragImageY = aY;
775 : }
776 0 : }
777 :
778 : NS_IMETHODIMP
779 0 : DataTransfer::SetDragImage(nsIDOMElement* aImage, int32_t aX, int32_t aY)
780 : {
781 0 : nsCOMPtr<Element> image = do_QueryInterface(aImage);
782 0 : if (image) {
783 0 : SetDragImage(*image, aX, aY);
784 : }
785 0 : return NS_OK;
786 : }
787 :
788 : void
789 0 : DataTransfer::UpdateDragImage(Element& aImage, int32_t aX, int32_t aY)
790 : {
791 0 : if (mEventMessage < eDragDropEventFirst || mEventMessage > eDragDropEventLast) {
792 0 : return;
793 : }
794 :
795 0 : nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
796 0 : if (dragSession) {
797 0 : dragSession->UpdateDragImage(aImage.AsDOMNode(), aX, aY);
798 : }
799 : }
800 :
801 : already_AddRefed<Promise>
802 0 : DataTransfer::GetFilesAndDirectories(nsIPrincipal& aSubjectPrincipal,
803 : ErrorResult& aRv)
804 : {
805 0 : nsCOMPtr<nsINode> parentNode = do_QueryInterface(mParent);
806 0 : if (!parentNode) {
807 0 : aRv.Throw(NS_ERROR_FAILURE);
808 0 : return nullptr;
809 : }
810 :
811 0 : nsCOMPtr<nsIGlobalObject> global = parentNode->OwnerDoc()->GetScopeObject();
812 0 : MOZ_ASSERT(global);
813 0 : if (!global) {
814 0 : aRv.Throw(NS_ERROR_FAILURE);
815 0 : return nullptr;
816 : }
817 :
818 0 : RefPtr<Promise> p = Promise::Create(global, aRv);
819 0 : if (NS_WARN_IF(aRv.Failed())) {
820 0 : return nullptr;
821 : }
822 :
823 0 : RefPtr<FileList> files = mItems->Files(&aSubjectPrincipal);
824 0 : if (NS_WARN_IF(!files)) {
825 0 : return nullptr;
826 : }
827 :
828 0 : Sequence<RefPtr<File>> filesSeq;
829 0 : files->ToSequence(filesSeq, aRv);
830 0 : if (NS_WARN_IF(aRv.Failed())) {
831 0 : return nullptr;
832 : }
833 :
834 0 : p->MaybeResolve(filesSeq);
835 :
836 0 : return p.forget();
837 : }
838 :
839 : already_AddRefed<Promise>
840 0 : DataTransfer::GetFiles(bool aRecursiveFlag,
841 : nsIPrincipal& aSubjectPrincipal,
842 : ErrorResult& aRv)
843 : {
844 : // Currently we don't support directories.
845 0 : return GetFilesAndDirectories(aSubjectPrincipal, aRv);
846 : }
847 :
848 : void
849 0 : DataTransfer::AddElement(Element& aElement, ErrorResult& aRv)
850 : {
851 0 : if (mReadOnly) {
852 0 : aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
853 0 : return;
854 : }
855 :
856 0 : mDragTarget = &aElement;
857 : }
858 :
859 : NS_IMETHODIMP
860 0 : DataTransfer::AddElement(nsIDOMElement* aElement)
861 : {
862 0 : NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
863 :
864 0 : nsCOMPtr<Element> element = do_QueryInterface(aElement);
865 0 : NS_ENSURE_TRUE(element, NS_ERROR_INVALID_ARG);
866 :
867 0 : ErrorResult rv;
868 0 : AddElement(*element, rv);
869 0 : return rv.StealNSResult();
870 : }
871 :
872 : nsresult
873 0 : DataTransfer::Clone(nsISupports* aParent, EventMessage aEventMessage,
874 : bool aUserCancelled, bool aIsCrossDomainSubFrameDrop,
875 : DataTransfer** aNewDataTransfer)
876 : {
877 : RefPtr<DataTransfer> newDataTransfer =
878 0 : new DataTransfer(aParent, aEventMessage, mEffectAllowed, mCursorState,
879 0 : mIsExternal, aUserCancelled, aIsCrossDomainSubFrameDrop,
880 : mClipboardType, mItems, mDragImage, mDragImageX,
881 0 : mDragImageY);
882 :
883 0 : newDataTransfer.forget(aNewDataTransfer);
884 0 : return NS_OK;
885 : }
886 :
887 : already_AddRefed<nsIArray>
888 0 : DataTransfer::GetTransferables(nsIDOMNode* aDragTarget)
889 : {
890 0 : MOZ_ASSERT(aDragTarget);
891 :
892 0 : nsCOMPtr<nsINode> dragNode = do_QueryInterface(aDragTarget);
893 0 : if (!dragNode) {
894 0 : return nullptr;
895 : }
896 :
897 0 : nsIDocument* doc = dragNode->GetComposedDoc();
898 0 : if (!doc) {
899 0 : return nullptr;
900 : }
901 :
902 0 : return GetTransferables(doc->GetLoadContext());
903 : }
904 :
905 : already_AddRefed<nsIArray>
906 0 : DataTransfer::GetTransferables(nsILoadContext* aLoadContext)
907 : {
908 0 : nsCOMPtr<nsIMutableArray> transArray = nsArray::Create();
909 0 : if (!transArray) {
910 0 : return nullptr;
911 : }
912 :
913 0 : uint32_t count = MozItemCount();
914 0 : for (uint32_t i = 0; i < count; i++) {
915 0 : nsCOMPtr<nsITransferable> transferable = GetTransferable(i, aLoadContext);
916 0 : if (transferable) {
917 0 : transArray->AppendElement(transferable, /*weak =*/ false);
918 : }
919 : }
920 :
921 0 : return transArray.forget();
922 : }
923 :
924 : already_AddRefed<nsITransferable>
925 0 : DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext)
926 : {
927 0 : if (aIndex >= MozItemCount()) {
928 0 : return nullptr;
929 : }
930 :
931 0 : const nsTArray<RefPtr<DataTransferItem>>& item = *mItems->MozItemsAt(aIndex);
932 0 : uint32_t count = item.Length();
933 0 : if (!count) {
934 0 : return nullptr;
935 : }
936 :
937 : nsCOMPtr<nsITransferable> transferable =
938 0 : do_CreateInstance("@mozilla.org/widget/transferable;1");
939 0 : if (!transferable) {
940 0 : return nullptr;
941 : }
942 0 : transferable->Init(aLoadContext);
943 :
944 0 : nsCOMPtr<nsIStorageStream> storageStream;
945 0 : nsCOMPtr<nsIBinaryOutputStream> stream;
946 :
947 0 : bool added = false;
948 0 : bool handlingCustomFormats = true;
949 :
950 : // When writing the custom data, we need to ensure that there is sufficient
951 : // space for a (uint32_t) data ending type, and the null byte character at
952 : // the end of the nsCString. We claim that space upfront and store it in
953 : // baseLength. This value will be set to zero if a write error occurs
954 : // indicating that the data and length are no longer valid.
955 0 : const uint32_t baseLength = sizeof(uint32_t) + 1;
956 0 : uint32_t totalCustomLength = baseLength;
957 :
958 : const char* knownFormats[] = {
959 : kTextMime, kHTMLMime, kNativeHTMLMime, kRTFMime,
960 : kURLMime, kURLDataMime, kURLDescriptionMime, kURLPrivateMime,
961 : kPNGImageMime, kJPEGImageMime, kGIFImageMime, kNativeImageMime,
962 : kFileMime, kFilePromiseMime, kFilePromiseURLMime,
963 : kFilePromiseDestFilename, kFilePromiseDirectoryMime,
964 0 : kMozTextInternal, kHTMLContext, kHTMLInfo };
965 :
966 : /*
967 : * Two passes are made here to iterate over all of the types. First, look for
968 : * any types that are not in the list of known types. For this pass,
969 : * handlingCustomFormats will be true. Data that corresponds to unknown types
970 : * will be pulled out and inserted into a single type (kCustomTypesMime) by
971 : * writing the data into a stream.
972 : *
973 : * The second pass will iterate over the formats looking for known types.
974 : * These are added as is. The unknown types are all then inserted as a single
975 : * type (kCustomTypesMime) in the same position of the first custom type. This
976 : * model is used to maintain the format order as best as possible.
977 : *
978 : * The format of the kCustomTypesMime type is one or more of the following
979 : * stored sequentially:
980 : * <32-bit> type (only none or string is supported)
981 : * <32-bit> length of format
982 : * <wide string> format
983 : * <32-bit> length of data
984 : * <wide string> data
985 : * A type of eCustomClipboardTypeId_None ends the list, without any following
986 : * data.
987 : */
988 0 : do {
989 0 : for (uint32_t f = 0; f < count; f++) {
990 0 : RefPtr<DataTransferItem> formatitem = item[f];
991 0 : nsCOMPtr<nsIVariant> variant = formatitem->DataNoSecurityCheck();
992 0 : if (!variant) { // skip empty items
993 0 : continue;
994 : }
995 :
996 0 : nsAutoString type;
997 0 : formatitem->GetInternalType(type);
998 :
999 : // If the data is of one of the well-known formats, use it directly.
1000 0 : bool isCustomFormat = true;
1001 0 : for (uint32_t f = 0; f < ArrayLength(knownFormats); f++) {
1002 0 : if (type.EqualsASCII(knownFormats[f])) {
1003 0 : isCustomFormat = false;
1004 0 : break;
1005 : }
1006 : }
1007 :
1008 : uint32_t lengthInBytes;
1009 0 : nsCOMPtr<nsISupports> convertedData;
1010 :
1011 0 : if (handlingCustomFormats) {
1012 0 : if (!ConvertFromVariant(variant, getter_AddRefs(convertedData),
1013 : &lengthInBytes)) {
1014 0 : continue;
1015 : }
1016 :
1017 : // When handling custom types, add the data to the stream if this is a
1018 : // custom type. If totalCustomLength is 0, then a write error occurred
1019 : // on a previous item, so ignore any others.
1020 0 : if (isCustomFormat && totalCustomLength > 0) {
1021 : // If it isn't a string, just ignore it. The dataTransfer is cached in
1022 : // the drag sesion during drag-and-drop, so non-strings will be
1023 : // available when dragging locally.
1024 0 : nsCOMPtr<nsISupportsString> str(do_QueryInterface(convertedData));
1025 0 : if (str) {
1026 0 : nsAutoString data;
1027 0 : str->GetData(data);
1028 :
1029 0 : if (!stream) {
1030 : // Create a storage stream to write to.
1031 0 : NS_NewStorageStream(1024, UINT32_MAX, getter_AddRefs(storageStream));
1032 :
1033 0 : nsCOMPtr<nsIOutputStream> outputStream;
1034 0 : storageStream->GetOutputStream(0, getter_AddRefs(outputStream));
1035 :
1036 0 : stream = do_CreateInstance("@mozilla.org/binaryoutputstream;1");
1037 0 : stream->SetOutputStream(outputStream);
1038 : }
1039 :
1040 : CheckedInt<uint32_t> formatLength =
1041 0 : CheckedInt<uint32_t>(type.Length()) * sizeof(nsString::char_type);
1042 :
1043 : // The total size of the stream is the format length, the data
1044 : // length, two integers to hold the lengths and one integer for
1045 : // the string flag. Guard against large data by ignoring any that
1046 : // don't fit.
1047 0 : CheckedInt<uint32_t> newSize = formatLength + totalCustomLength +
1048 0 : lengthInBytes + (sizeof(uint32_t) * 3);
1049 0 : if (newSize.isValid()) {
1050 : // If a write error occurs, set totalCustomLength to 0 so that
1051 : // further processing gets ignored.
1052 0 : nsresult rv = stream->Write32(eCustomClipboardTypeId_String);
1053 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1054 0 : totalCustomLength = 0;
1055 0 : continue;
1056 : }
1057 0 : rv = stream->Write32(formatLength.value());
1058 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1059 0 : totalCustomLength = 0;
1060 0 : continue;
1061 : }
1062 0 : rv = stream->WriteBytes((const char *)type.get(), formatLength.value());
1063 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1064 0 : totalCustomLength = 0;
1065 0 : continue;
1066 : }
1067 0 : rv = stream->Write32(lengthInBytes);
1068 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1069 0 : totalCustomLength = 0;
1070 0 : continue;
1071 : }
1072 0 : rv = stream->WriteBytes((const char *)data.get(), lengthInBytes);
1073 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1074 0 : totalCustomLength = 0;
1075 0 : continue;
1076 : }
1077 :
1078 0 : totalCustomLength = newSize.value();
1079 : }
1080 : }
1081 : }
1082 0 : } else if (isCustomFormat && stream) {
1083 : // This is the second pass of the loop (handlingCustomFormats is false).
1084 : // When encountering the first custom format, append all of the stream
1085 : // at this position. If totalCustomLength is 0 indicating a write error
1086 : // occurred, or no data has been added to it, don't output anything,
1087 0 : if (totalCustomLength > baseLength) {
1088 : // Write out an end of data terminator.
1089 0 : nsresult rv = stream->Write32(eCustomClipboardTypeId_None);
1090 0 : if (NS_SUCCEEDED(rv)) {
1091 0 : nsCOMPtr<nsIInputStream> inputStream;
1092 0 : storageStream->NewInputStream(0, getter_AddRefs(inputStream));
1093 :
1094 : RefPtr<nsStringBuffer> stringBuffer =
1095 0 : nsStringBuffer::Alloc(totalCustomLength);
1096 :
1097 : // Subtract off the null terminator when reading.
1098 0 : totalCustomLength--;
1099 :
1100 : // Read the data from the stream and add a null-terminator as
1101 : // ToString needs it.
1102 : uint32_t amountRead;
1103 0 : rv = inputStream->Read(static_cast<char*>(stringBuffer->Data()),
1104 0 : totalCustomLength, &amountRead);
1105 0 : if (NS_SUCCEEDED(rv)) {
1106 0 : static_cast<char*>(stringBuffer->Data())[amountRead] = 0;
1107 :
1108 0 : nsCString str;
1109 0 : stringBuffer->ToString(totalCustomLength, str);
1110 : nsCOMPtr<nsISupportsCString>
1111 0 : strSupports(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
1112 0 : strSupports->SetData(str);
1113 :
1114 0 : nsresult rv = transferable->SetTransferData(kCustomTypesMime,
1115 : strSupports,
1116 0 : totalCustomLength);
1117 0 : if (NS_FAILED(rv)) {
1118 0 : return nullptr;
1119 : }
1120 :
1121 0 : added = true;
1122 : }
1123 : }
1124 : }
1125 :
1126 : // Clear the stream so it doesn't get used again.
1127 0 : stream = nullptr;
1128 : } else {
1129 : // This is the second pass of the loop and a known type is encountered.
1130 : // Add it as is.
1131 0 : if (!ConvertFromVariant(variant, getter_AddRefs(convertedData),
1132 : &lengthInBytes)) {
1133 0 : continue;
1134 : }
1135 :
1136 : // The underlying drag code uses text/unicode, so use that instead of
1137 : // text/plain
1138 : const char* format;
1139 0 : NS_ConvertUTF16toUTF8 utf8format(type);
1140 0 : if (utf8format.EqualsLiteral(kTextMime)) {
1141 0 : format = kUnicodeMime;
1142 : } else {
1143 0 : format = utf8format.get();
1144 : }
1145 :
1146 : // If a converter is set for a format, set the converter for the
1147 : // transferable and don't add the item
1148 : nsCOMPtr<nsIFormatConverter> converter =
1149 0 : do_QueryInterface(convertedData);
1150 0 : if (converter) {
1151 0 : transferable->AddDataFlavor(format);
1152 0 : transferable->SetConverter(converter);
1153 0 : continue;
1154 : }
1155 :
1156 0 : nsresult rv = transferable->SetTransferData(format, convertedData,
1157 0 : lengthInBytes);
1158 0 : if (NS_FAILED(rv)) {
1159 0 : return nullptr;
1160 : }
1161 :
1162 0 : added = true;
1163 : }
1164 : }
1165 :
1166 0 : handlingCustomFormats = !handlingCustomFormats;
1167 0 : } while (!handlingCustomFormats);
1168 :
1169 : // only return the transferable if data was successfully added to it
1170 0 : if (added) {
1171 0 : return transferable.forget();
1172 : }
1173 :
1174 0 : return nullptr;
1175 : }
1176 :
1177 : bool
1178 0 : DataTransfer::ConvertFromVariant(nsIVariant* aVariant,
1179 : nsISupports** aSupports,
1180 : uint32_t* aLength) const
1181 : {
1182 0 : *aSupports = nullptr;
1183 0 : *aLength = 0;
1184 :
1185 : uint16_t type;
1186 0 : aVariant->GetDataType(&type);
1187 0 : if (type == nsIDataType::VTYPE_INTERFACE ||
1188 0 : type == nsIDataType::VTYPE_INTERFACE_IS) {
1189 0 : nsCOMPtr<nsISupports> data;
1190 0 : if (NS_FAILED(aVariant->GetAsISupports(getter_AddRefs(data)))) {
1191 0 : return false;
1192 : }
1193 :
1194 0 : nsCOMPtr<nsIFlavorDataProvider> fdp = do_QueryInterface(data);
1195 0 : if (fdp) {
1196 : // for flavour data providers, use kFlavorHasDataProvider (which has the
1197 : // value 0) as the length.
1198 0 : fdp.forget(aSupports);
1199 0 : *aLength = nsITransferable::kFlavorHasDataProvider;
1200 : }
1201 : else {
1202 : // wrap the item in an nsISupportsInterfacePointer
1203 : nsCOMPtr<nsISupportsInterfacePointer> ptrSupports =
1204 0 : do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
1205 0 : if (!ptrSupports) {
1206 0 : return false;
1207 : }
1208 :
1209 0 : ptrSupports->SetData(data);
1210 0 : ptrSupports.forget(aSupports);
1211 :
1212 0 : *aLength = sizeof(nsISupportsInterfacePointer *);
1213 : }
1214 :
1215 0 : return true;
1216 : }
1217 :
1218 : char16_t* chrs;
1219 0 : uint32_t len = 0;
1220 0 : nsresult rv = aVariant->GetAsWStringWithSize(&len, &chrs);
1221 0 : if (NS_FAILED(rv)) {
1222 0 : return false;
1223 : }
1224 :
1225 0 : nsAutoString str;
1226 0 : str.Adopt(chrs, len);
1227 :
1228 : nsCOMPtr<nsISupportsString>
1229 0 : strSupports(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
1230 0 : if (!strSupports) {
1231 0 : return false;
1232 : }
1233 :
1234 0 : strSupports->SetData(str);
1235 :
1236 0 : strSupports.forget(aSupports);
1237 :
1238 : // each character is two bytes
1239 0 : *aLength = str.Length() << 1;
1240 :
1241 0 : return true;
1242 : }
1243 :
1244 : void
1245 0 : DataTransfer::ClearAll()
1246 : {
1247 0 : mItems->ClearAllItems();
1248 0 : }
1249 :
1250 : uint32_t
1251 0 : DataTransfer::MozItemCount() const
1252 : {
1253 0 : return mItems->MozItemCount();
1254 : }
1255 :
1256 : nsresult
1257 0 : DataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
1258 : nsIVariant* aData,
1259 : uint32_t aIndex,
1260 : nsIPrincipal* aPrincipal)
1261 : {
1262 0 : nsAutoString format;
1263 0 : GetRealFormat(aFormat, format);
1264 :
1265 0 : ErrorResult rv;
1266 : RefPtr<DataTransferItem> item =
1267 0 : mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
1268 : /* aInsertOnly = */ false,
1269 : /* aHidden= */ false,
1270 0 : rv);
1271 0 : return rv.StealNSResult();
1272 : }
1273 :
1274 : void
1275 0 : DataTransfer::SetDataWithPrincipalFromOtherProcess(const nsAString& aFormat,
1276 : nsIVariant* aData,
1277 : uint32_t aIndex,
1278 : nsIPrincipal* aPrincipal,
1279 : bool aHidden)
1280 : {
1281 0 : if (aFormat.EqualsLiteral(kCustomTypesMime)) {
1282 0 : FillInExternalCustomTypes(aData, aIndex, aPrincipal);
1283 : } else {
1284 0 : nsAutoString format;
1285 0 : GetRealFormat(aFormat, format);
1286 :
1287 0 : ErrorResult rv;
1288 : RefPtr<DataTransferItem> item =
1289 0 : mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
1290 0 : /* aInsertOnly = */ false, aHidden, rv);
1291 0 : if (NS_WARN_IF(rv.Failed())) {
1292 0 : rv.SuppressException();
1293 : }
1294 : }
1295 0 : }
1296 :
1297 : void
1298 0 : DataTransfer::GetRealFormat(const nsAString& aInFormat,
1299 : nsAString& aOutFormat) const
1300 : {
1301 : // treat text/unicode as equivalent to text/plain
1302 0 : nsAutoString lowercaseFormat;
1303 0 : nsContentUtils::ASCIIToLower(aInFormat, lowercaseFormat);
1304 0 : if (lowercaseFormat.EqualsLiteral("text") ||
1305 0 : lowercaseFormat.EqualsLiteral("text/unicode")) {
1306 0 : aOutFormat.AssignLiteral("text/plain");
1307 0 : return;
1308 : }
1309 :
1310 0 : if (lowercaseFormat.EqualsLiteral("url")) {
1311 0 : aOutFormat.AssignLiteral("text/uri-list");
1312 0 : return;
1313 : }
1314 :
1315 0 : aOutFormat.Assign(lowercaseFormat);
1316 : }
1317 :
1318 : nsresult
1319 0 : DataTransfer::CacheExternalData(const char* aFormat, uint32_t aIndex,
1320 : nsIPrincipal* aPrincipal, bool aHidden)
1321 : {
1322 0 : ErrorResult rv;
1323 0 : RefPtr<DataTransferItem> item;
1324 :
1325 0 : if (strcmp(aFormat, kUnicodeMime) == 0) {
1326 0 : item = mItems->SetDataWithPrincipal(NS_LITERAL_STRING("text/plain"), nullptr,
1327 0 : aIndex, aPrincipal, false, aHidden, rv);
1328 0 : if (NS_WARN_IF(rv.Failed())) {
1329 0 : return rv.StealNSResult();
1330 : }
1331 0 : return NS_OK;
1332 : }
1333 :
1334 0 : if (strcmp(aFormat, kURLDataMime) == 0) {
1335 0 : item = mItems->SetDataWithPrincipal(NS_LITERAL_STRING("text/uri-list"), nullptr,
1336 0 : aIndex, aPrincipal, false, aHidden, rv);
1337 0 : if (NS_WARN_IF(rv.Failed())) {
1338 0 : return rv.StealNSResult();
1339 : }
1340 0 : return NS_OK;
1341 : }
1342 :
1343 0 : nsAutoString format;
1344 0 : GetRealFormat(NS_ConvertUTF8toUTF16(aFormat), format);
1345 0 : item = mItems->SetDataWithPrincipal(format, nullptr, aIndex,
1346 0 : aPrincipal, false, aHidden, rv);
1347 0 : if (NS_WARN_IF(rv.Failed())) {
1348 0 : return rv.StealNSResult();
1349 : }
1350 0 : return NS_OK;
1351 : }
1352 :
1353 : void
1354 0 : DataTransfer::CacheExternalDragFormats()
1355 : {
1356 : // Called during the constructor to cache the formats available from an
1357 : // external drag. The data associated with each format will be set to null.
1358 : // This data will instead only be retrieved in FillInExternalDragData when
1359 : // asked for, as it may be time consuming for the source application to
1360 : // generate it.
1361 :
1362 0 : nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
1363 0 : if (!dragSession) {
1364 0 : return;
1365 : }
1366 :
1367 : // make sure that the system principal is used for external drags
1368 0 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
1369 0 : nsCOMPtr<nsIPrincipal> sysPrincipal;
1370 0 : ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
1371 :
1372 : // there isn't a way to get a list of the formats that might be available on
1373 : // all platforms, so just check for the types that can actually be imported
1374 : // XXXndeakin there are some other formats but those are platform specific.
1375 : // NOTE: kFileMime must have index 0
1376 : const char* formats[] = { kFileMime, kHTMLMime, kURLMime, kURLDataMime,
1377 0 : kUnicodeMime, kPNGImageMime };
1378 :
1379 : uint32_t count;
1380 0 : dragSession->GetNumDropItems(&count);
1381 0 : for (uint32_t c = 0; c < count; c++) {
1382 0 : bool hasFileData = false;
1383 0 : dragSession->IsDataFlavorSupported(kFileMime, &hasFileData);
1384 :
1385 : // First, check for the special format that holds custom types.
1386 : bool supported;
1387 0 : dragSession->IsDataFlavorSupported(kCustomTypesMime, &supported);
1388 0 : if (supported) {
1389 0 : FillInExternalCustomTypes(c, sysPrincipal);
1390 : }
1391 :
1392 0 : for (uint32_t f = 0; f < ArrayLength(formats); f++) {
1393 : // IsDataFlavorSupported doesn't take an index as an argument and just
1394 : // checks if any of the items support a particular flavor, even though
1395 : // the GetData method does take an index. Here, we just assume that
1396 : // every item being dragged has the same set of flavors.
1397 : bool supported;
1398 0 : dragSession->IsDataFlavorSupported(formats[f], &supported);
1399 : // if the format is supported, add an item to the array with null as
1400 : // the data. When retrieved, GetRealData will read the data.
1401 0 : if (supported) {
1402 0 : CacheExternalData(formats[f], c, sysPrincipal, /* hidden = */ f && hasFileData);
1403 : }
1404 : }
1405 : }
1406 : }
1407 :
1408 : void
1409 0 : DataTransfer::CacheExternalClipboardFormats(bool aPlainTextOnly)
1410 : {
1411 0 : NS_ASSERTION(mEventMessage == ePaste,
1412 : "caching clipboard data for invalid event");
1413 :
1414 : // Called during the constructor for paste events to cache the formats
1415 : // available on the clipboard. As with CacheExternalDragFormats, the
1416 : // data will only be retrieved when needed.
1417 :
1418 : nsCOMPtr<nsIClipboard> clipboard =
1419 0 : do_GetService("@mozilla.org/widget/clipboard;1");
1420 0 : if (!clipboard || mClipboardType < 0) {
1421 0 : return;
1422 : }
1423 :
1424 0 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
1425 0 : nsCOMPtr<nsIPrincipal> sysPrincipal;
1426 0 : ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
1427 :
1428 0 : if (aPlainTextOnly) {
1429 : bool supported;
1430 0 : const char* unicodeMime[] = { kUnicodeMime };
1431 0 : clipboard->HasDataMatchingFlavors(unicodeMime, 1, mClipboardType,
1432 0 : &supported);
1433 0 : if (supported) {
1434 0 : CacheExternalData(kUnicodeMime, 0, sysPrincipal, false);
1435 : }
1436 0 : return;
1437 : }
1438 :
1439 : // Check if the clipboard has any files
1440 0 : bool hasFileData = false;
1441 0 : const char *fileMime[] = { kFileMime };
1442 0 : clipboard->HasDataMatchingFlavors(fileMime, 1, mClipboardType, &hasFileData);
1443 :
1444 : // We will be ignoring any application/x-moz-file files found in the paste
1445 : // datatransfer within e10s, as they will fail to be sent over IPC. Because of
1446 : // that, we will unset hasFileData, whether or not it would have been set.
1447 : // (bug 1308007)
1448 0 : if (XRE_IsContentProcess()) {
1449 0 : hasFileData = false;
1450 : }
1451 :
1452 : // there isn't a way to get a list of the formats that might be available on
1453 : // all platforms, so just check for the types that can actually be imported.
1454 : // NOTE: kCustomTypesMime must have index 0, kFileMime index 1
1455 : const char* formats[] = { kCustomTypesMime, kFileMime, kHTMLMime, kRTFMime,
1456 0 : kURLMime, kURLDataMime, kUnicodeMime, kPNGImageMime };
1457 :
1458 0 : for (uint32_t f = 0; f < mozilla::ArrayLength(formats); ++f) {
1459 : // check each format one at a time
1460 : bool supported;
1461 0 : clipboard->HasDataMatchingFlavors(&(formats[f]), 1, mClipboardType,
1462 0 : &supported);
1463 : // if the format is supported, add an item to the array with null as
1464 : // the data. When retrieved, GetRealData will read the data.
1465 0 : if (supported) {
1466 0 : if (f == 0) {
1467 0 : FillInExternalCustomTypes(0, sysPrincipal);
1468 : } else {
1469 : // In non-e10s we support pasting files from explorer.exe.
1470 : // Unfortunately, we fail to send that data over IPC in e10s, so we
1471 : // don't want to add the item to the DataTransfer and end up producing a
1472 : // null `application/x-moz-file`. (bug 1308007)
1473 0 : if (XRE_IsContentProcess() && f == 1) {
1474 0 : continue;
1475 : }
1476 :
1477 : // If we aren't the file data, and we have file data, we want to be hidden
1478 0 : CacheExternalData(formats[f], 0, sysPrincipal, /* hidden = */ f != 1 && hasFileData);
1479 : }
1480 : }
1481 : }
1482 : }
1483 :
1484 : void
1485 0 : DataTransfer::FillAllExternalData()
1486 : {
1487 0 : if (mIsExternal) {
1488 0 : for (uint32_t i = 0; i < MozItemCount(); ++i) {
1489 0 : const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(i);
1490 0 : for (uint32_t j = 0; j < items.Length(); ++j) {
1491 0 : MOZ_ASSERT(items[j]->Index() == i);
1492 :
1493 0 : items[j]->FillInExternalData();
1494 : }
1495 : }
1496 : }
1497 0 : }
1498 :
1499 : void
1500 0 : DataTransfer::FillInExternalCustomTypes(uint32_t aIndex,
1501 : nsIPrincipal* aPrincipal)
1502 : {
1503 : RefPtr<DataTransferItem> item = new DataTransferItem(this,
1504 0 : NS_LITERAL_STRING(kCustomTypesMime),
1505 0 : DataTransferItem::KIND_STRING);
1506 0 : item->SetIndex(aIndex);
1507 :
1508 0 : nsCOMPtr<nsIVariant> variant = item->DataNoSecurityCheck();
1509 0 : if (!variant) {
1510 0 : return;
1511 : }
1512 :
1513 0 : FillInExternalCustomTypes(variant, aIndex, aPrincipal);
1514 : }
1515 :
1516 : void
1517 0 : DataTransfer::FillInExternalCustomTypes(nsIVariant* aData, uint32_t aIndex,
1518 : nsIPrincipal* aPrincipal)
1519 : {
1520 : char* chrs;
1521 0 : uint32_t len = 0;
1522 0 : nsresult rv = aData->GetAsStringWithSize(&len, &chrs);
1523 0 : if (NS_FAILED(rv)) {
1524 0 : return;
1525 : }
1526 :
1527 0 : nsAutoCString str;
1528 0 : str.Adopt(chrs, len);
1529 :
1530 0 : nsCOMPtr<nsIInputStream> stringStream;
1531 0 : NS_NewCStringInputStream(getter_AddRefs(stringStream), str);
1532 :
1533 : nsCOMPtr<nsIBinaryInputStream> stream =
1534 0 : do_CreateInstance("@mozilla.org/binaryinputstream;1");
1535 0 : if (!stream) {
1536 0 : return;
1537 : }
1538 :
1539 0 : rv = stream->SetInputStream(stringStream);
1540 0 : NS_ENSURE_SUCCESS_VOID(rv);
1541 :
1542 : uint32_t type;
1543 0 : do {
1544 0 : rv = stream->Read32(&type);
1545 0 : NS_ENSURE_SUCCESS_VOID(rv);
1546 0 : if (type == eCustomClipboardTypeId_String) {
1547 : uint32_t formatLength;
1548 0 : rv = stream->Read32(&formatLength);
1549 0 : NS_ENSURE_SUCCESS_VOID(rv);
1550 : char* formatBytes;
1551 0 : rv = stream->ReadBytes(formatLength, &formatBytes);
1552 0 : NS_ENSURE_SUCCESS_VOID(rv);
1553 0 : nsAutoString format;
1554 0 : format.Adopt(reinterpret_cast<char16_t*>(formatBytes),
1555 0 : formatLength / sizeof(char16_t));
1556 :
1557 : uint32_t dataLength;
1558 0 : rv = stream->Read32(&dataLength);
1559 0 : NS_ENSURE_SUCCESS_VOID(rv);
1560 : char* dataBytes;
1561 0 : rv = stream->ReadBytes(dataLength, &dataBytes);
1562 0 : NS_ENSURE_SUCCESS_VOID(rv);
1563 0 : nsAutoString data;
1564 0 : data.Adopt(reinterpret_cast<char16_t*>(dataBytes),
1565 0 : dataLength / sizeof(char16_t));
1566 :
1567 0 : RefPtr<nsVariantCC> variant = new nsVariantCC();
1568 0 : rv = variant->SetAsAString(data);
1569 0 : NS_ENSURE_SUCCESS_VOID(rv);
1570 :
1571 0 : SetDataWithPrincipal(format, variant, aIndex, aPrincipal);
1572 : }
1573 0 : } while (type != eCustomClipboardTypeId_None);
1574 : }
1575 :
1576 : } // namespace dom
1577 : } // namespace mozilla
|