1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is the Mozilla Corporation.
18 : * Portions created by the Initial Developer are Copyright (C) 2008
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Neil Deakin <enndeakin@gmail.com>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "mozilla/Util.h"
39 :
40 : #include "nsDOMDataTransfer.h"
41 :
42 : #include "prlog.h"
43 : #include "nsString.h"
44 : #include "nsIServiceManager.h"
45 : #include "nsIVariant.h"
46 : #include "nsISupportsPrimitives.h"
47 : #include "nsDOMClassInfoID.h"
48 : #include "nsIScriptSecurityManager.h"
49 : #include "nsDOMLists.h"
50 : #include "nsGUIEvent.h"
51 : #include "nsDOMError.h"
52 : #include "nsIDragService.h"
53 : #include "nsIScriptableRegion.h"
54 : #include "nsContentUtils.h"
55 : #include "nsIContent.h"
56 : #include "nsCRT.h"
57 : #include "nsIScriptObjectPrincipal.h"
58 :
59 : using namespace mozilla;
60 :
61 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMDataTransfer)
62 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMDataTransfer)
63 0 : if (tmp->mFiles) {
64 0 : tmp->mFiles->Disconnect();
65 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFiles)
66 : }
67 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDragTarget)
68 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDragImage)
69 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
70 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMDataTransfer)
71 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFiles)
72 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDragTarget)
73 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDragImage)
74 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
75 :
76 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMDataTransfer)
77 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMDataTransfer)
78 :
79 : DOMCI_DATA(DataTransfer, nsDOMDataTransfer)
80 :
81 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMDataTransfer)
82 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMDataTransfer)
83 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDataTransfer)
84 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DataTransfer)
85 0 : NS_INTERFACE_MAP_END
86 :
87 : // the size of the array
88 : const char nsDOMDataTransfer::sEffects[8][9] = {
89 : "none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"
90 : };
91 :
92 0 : nsDOMDataTransfer::nsDOMDataTransfer()
93 : : mEventType(NS_DRAGDROP_START),
94 : mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
95 : mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
96 : mCursorState(false),
97 : mReadOnly(false),
98 : mIsExternal(false),
99 : mUserCancelled(false),
100 : mDragImageX(0),
101 0 : mDragImageY(0)
102 : {
103 0 : }
104 :
105 0 : nsDOMDataTransfer::nsDOMDataTransfer(PRUint32 aEventType)
106 : : mEventType(aEventType),
107 : mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
108 : mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
109 : mCursorState(false),
110 : mReadOnly(true),
111 : mIsExternal(true),
112 : mUserCancelled(false),
113 : mDragImageX(0),
114 0 : mDragImageY(0)
115 : {
116 0 : CacheExternalFormats();
117 0 : }
118 :
119 0 : nsDOMDataTransfer::nsDOMDataTransfer(PRUint32 aEventType,
120 : const PRUint32 aEffectAllowed,
121 : bool aCursorState,
122 : bool aIsExternal,
123 : bool aUserCancelled,
124 : nsTArray<nsTArray<TransferItem> >& aItems,
125 : nsIDOMElement* aDragImage,
126 : PRUint32 aDragImageX,
127 : PRUint32 aDragImageY)
128 : : mEventType(aEventType),
129 : mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
130 : mEffectAllowed(aEffectAllowed),
131 : mCursorState(aCursorState),
132 : mReadOnly(true),
133 : mIsExternal(aIsExternal),
134 : mUserCancelled(aUserCancelled),
135 : mItems(aItems),
136 : mDragImage(aDragImage),
137 : mDragImageX(aDragImageX),
138 0 : mDragImageY(aDragImageY)
139 : {
140 : // The items are copied from aItems into mItems. There is no need to copy
141 : // the actual data in the items as the data transfer will be read only. The
142 : // draggesture and dragstart events are the only times when items are
143 : // modifiable, but those events should have been using the first constructor
144 : // above.
145 0 : NS_ASSERTION(aEventType != NS_DRAGDROP_GESTURE &&
146 : aEventType != NS_DRAGDROP_START,
147 : "invalid event type for nsDOMDataTransfer constructor");
148 0 : }
149 :
150 : NS_IMETHODIMP
151 0 : nsDOMDataTransfer::GetDropEffect(nsAString& aDropEffect)
152 : {
153 0 : aDropEffect.AssignASCII(sEffects[mDropEffect]);
154 0 : return NS_OK;
155 : }
156 :
157 : NS_IMETHODIMP
158 0 : nsDOMDataTransfer::SetDropEffect(const nsAString& aDropEffect)
159 : {
160 : // the drop effect can only be 'none', 'copy', 'move' or 'link'.
161 0 : for (PRUint32 e = 0; e <= nsIDragService::DRAGDROP_ACTION_LINK; e++) {
162 0 : if (aDropEffect.EqualsASCII(sEffects[e])) {
163 : // don't allow copyMove
164 0 : if (e != (nsIDragService::DRAGDROP_ACTION_COPY |
165 : nsIDragService::DRAGDROP_ACTION_MOVE))
166 0 : mDropEffect = e;
167 0 : break;
168 : }
169 : }
170 :
171 0 : return NS_OK;
172 : }
173 :
174 : NS_IMETHODIMP
175 0 : nsDOMDataTransfer::GetEffectAllowed(nsAString& aEffectAllowed)
176 : {
177 0 : if (mEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
178 0 : aEffectAllowed.AssignLiteral("uninitialized");
179 : else
180 0 : aEffectAllowed.AssignASCII(sEffects[mEffectAllowed]);
181 0 : return NS_OK;
182 : }
183 :
184 : NS_IMETHODIMP
185 0 : nsDOMDataTransfer::SetEffectAllowed(const nsAString& aEffectAllowed)
186 : {
187 0 : if (aEffectAllowed.EqualsLiteral("uninitialized")) {
188 0 : mEffectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
189 0 : return NS_OK;
190 : }
191 :
192 : PR_STATIC_ASSERT(nsIDragService::DRAGDROP_ACTION_NONE == 0);
193 : PR_STATIC_ASSERT(nsIDragService::DRAGDROP_ACTION_COPY == 1);
194 : PR_STATIC_ASSERT(nsIDragService::DRAGDROP_ACTION_MOVE == 2);
195 : PR_STATIC_ASSERT(nsIDragService::DRAGDROP_ACTION_LINK == 4);
196 :
197 0 : for (PRUint32 e = 0; e < ArrayLength(sEffects); e++) {
198 0 : if (aEffectAllowed.EqualsASCII(sEffects[e])) {
199 0 : mEffectAllowed = e;
200 0 : break;
201 : }
202 : }
203 :
204 0 : return NS_OK;
205 : }
206 :
207 : NS_IMETHODIMP
208 0 : nsDOMDataTransfer::GetDropEffectInt(PRUint32* aDropEffect)
209 : {
210 0 : *aDropEffect = mDropEffect;
211 0 : return NS_OK;
212 : }
213 :
214 : NS_IMETHODIMP
215 0 : nsDOMDataTransfer::SetDropEffectInt(PRUint32 aDropEffect)
216 : {
217 0 : mDropEffect = aDropEffect;
218 0 : return NS_OK;
219 : }
220 :
221 : NS_IMETHODIMP
222 0 : nsDOMDataTransfer::GetEffectAllowedInt(PRUint32* aEffectAllowed)
223 : {
224 0 : *aEffectAllowed = mEffectAllowed;
225 0 : return NS_OK;
226 : }
227 :
228 : NS_IMETHODIMP
229 0 : nsDOMDataTransfer::SetEffectAllowedInt(PRUint32 aEffectAllowed)
230 : {
231 0 : mEffectAllowed = aEffectAllowed;
232 0 : return NS_OK;
233 : }
234 :
235 : NS_IMETHODIMP
236 0 : nsDOMDataTransfer::GetMozUserCancelled(bool* aUserCancelled)
237 : {
238 0 : *aUserCancelled = mUserCancelled;
239 0 : return NS_OK;
240 : }
241 :
242 : NS_IMETHODIMP
243 0 : nsDOMDataTransfer::GetFiles(nsIDOMFileList** aFileList)
244 : {
245 0 : *aFileList = nsnull;
246 :
247 0 : if (mEventType != NS_DRAGDROP_DROP && mEventType != NS_DRAGDROP_DRAGDROP)
248 0 : return NS_OK;
249 :
250 0 : if (!mFiles) {
251 0 : mFiles = new nsDOMFileList(static_cast<nsIDOMDataTransfer*>(this));
252 0 : NS_ENSURE_TRUE(mFiles, NS_ERROR_OUT_OF_MEMORY);
253 :
254 0 : PRUint32 count = mItems.Length();
255 :
256 0 : for (PRUint32 i = 0; i < count; i++) {
257 0 : nsCOMPtr<nsIVariant> variant;
258 0 : nsresult rv = MozGetDataAt(NS_ConvertUTF8toUTF16(kFileMime), i, getter_AddRefs(variant));
259 0 : NS_ENSURE_SUCCESS(rv, rv);
260 :
261 0 : if (!variant)
262 0 : continue;
263 :
264 0 : nsCOMPtr<nsISupports> supports;
265 0 : rv = variant->GetAsISupports(getter_AddRefs(supports));
266 :
267 0 : if (NS_FAILED(rv))
268 0 : continue;
269 :
270 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
271 :
272 0 : if (!file)
273 0 : continue;
274 :
275 0 : nsRefPtr<nsDOMFileFile> domFile = new nsDOMFileFile(file);
276 :
277 0 : if (!mFiles->Append(domFile))
278 0 : return NS_ERROR_FAILURE;
279 : }
280 : }
281 :
282 0 : *aFileList = mFiles;
283 0 : NS_ADDREF(*aFileList);
284 0 : return NS_OK;
285 : }
286 :
287 : NS_IMETHODIMP
288 0 : nsDOMDataTransfer::GetTypes(nsIDOMDOMStringList** aTypes)
289 : {
290 0 : *aTypes = nsnull;
291 :
292 0 : nsRefPtr<nsDOMStringList> types = new nsDOMStringList();
293 0 : NS_ENSURE_TRUE(types, NS_ERROR_OUT_OF_MEMORY);
294 :
295 0 : if (mItems.Length()) {
296 0 : nsTArray<TransferItem>& item = mItems[0];
297 0 : for (PRUint32 i = 0; i < item.Length(); i++)
298 0 : types->Add(item[i].mFormat);
299 :
300 : bool filePresent, filePromisePresent;
301 0 : types->Contains(NS_LITERAL_STRING(kFileMime), &filePresent);
302 0 : types->Contains(NS_LITERAL_STRING("application/x-moz-file-promise"), &filePromisePresent);
303 0 : if (filePresent || filePromisePresent)
304 0 : types->Add(NS_LITERAL_STRING("Files"));
305 : }
306 :
307 0 : *aTypes = types;
308 0 : NS_ADDREF(*aTypes);
309 :
310 0 : return NS_OK;
311 : }
312 :
313 : NS_IMETHODIMP
314 0 : nsDOMDataTransfer::GetData(const nsAString& aFormat, nsAString& aData)
315 : {
316 : // return an empty string if data for the format was not found
317 0 : aData.Truncate();
318 :
319 0 : nsCOMPtr<nsIVariant> data;
320 0 : nsresult rv = MozGetDataAt(aFormat, 0, getter_AddRefs(data));
321 0 : if (rv == NS_ERROR_DOM_INDEX_SIZE_ERR)
322 0 : return NS_OK;
323 :
324 0 : NS_ENSURE_SUCCESS(rv, rv);
325 :
326 0 : if (data) {
327 0 : nsAutoString stringdata;
328 0 : data->GetAsAString(stringdata);
329 :
330 : // for the URL type, parse out the first URI from the list. The URIs are
331 : // separated by newlines
332 0 : nsAutoString lowercaseFormat;
333 0 : rv = nsContentUtils::ASCIIToLower(aFormat, lowercaseFormat);
334 0 : if (NS_FAILED(rv)) {
335 0 : return rv;
336 : }
337 :
338 0 : if (lowercaseFormat.EqualsLiteral("url")) {
339 0 : PRInt32 lastidx = 0, idx;
340 0 : PRInt32 length = stringdata.Length();
341 0 : while (lastidx < length) {
342 0 : idx = stringdata.FindChar('\n', lastidx);
343 : // lines beginning with # are comments
344 0 : if (stringdata[lastidx] == '#') {
345 0 : if (idx == -1)
346 0 : break;
347 : }
348 : else {
349 0 : if (idx == -1)
350 0 : aData.Assign(Substring(stringdata, lastidx));
351 : else
352 0 : aData.Assign(Substring(stringdata, lastidx, idx - lastidx));
353 0 : aData = nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(aData, true);
354 0 : return NS_OK;
355 : }
356 0 : lastidx = idx + 1;
357 : }
358 : }
359 : else {
360 0 : aData = stringdata;
361 : }
362 : }
363 :
364 0 : return NS_OK;
365 : }
366 :
367 : NS_IMETHODIMP
368 0 : nsDOMDataTransfer::SetData(const nsAString& aFormat, const nsAString& aData)
369 : {
370 0 : nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
371 0 : NS_ENSURE_TRUE(variant, NS_ERROR_OUT_OF_MEMORY);
372 :
373 0 : variant->SetAsAString(aData);
374 :
375 0 : return MozSetDataAt(aFormat, variant, 0);
376 : }
377 :
378 : NS_IMETHODIMP
379 0 : nsDOMDataTransfer::ClearData(const nsAString& aFormat)
380 : {
381 0 : nsresult rv = MozClearDataAt(aFormat, 0);
382 0 : return (rv == NS_ERROR_DOM_INDEX_SIZE_ERR) ? NS_OK : rv;
383 : }
384 :
385 : NS_IMETHODIMP
386 0 : nsDOMDataTransfer::GetMozItemCount(PRUint32* aCount)
387 : {
388 0 : *aCount = mItems.Length();
389 0 : return NS_OK;
390 : }
391 :
392 : NS_IMETHODIMP
393 0 : nsDOMDataTransfer::GetMozCursor(nsAString& aCursorState)
394 : {
395 0 : if (mCursorState) {
396 0 : aCursorState.AssignLiteral("default");
397 : } else {
398 0 : aCursorState.AssignLiteral("auto");
399 : }
400 0 : return NS_OK;
401 : }
402 :
403 : NS_IMETHODIMP
404 0 : nsDOMDataTransfer::SetMozCursor(const nsAString& aCursorState)
405 : {
406 : // Lock the cursor to an arrow during the drag.
407 0 : mCursorState = aCursorState.EqualsLiteral("default");
408 :
409 0 : return NS_OK;
410 : }
411 :
412 : NS_IMETHODIMP
413 0 : nsDOMDataTransfer::GetMozSourceNode(nsIDOMNode** aSourceNode)
414 : {
415 0 : *aSourceNode = nsnull;
416 :
417 0 : nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
418 0 : if (!dragSession)
419 0 : return NS_OK;
420 :
421 0 : nsCOMPtr<nsIDOMNode> sourceNode;
422 0 : dragSession->GetSourceNode(getter_AddRefs(sourceNode));
423 0 : if (sourceNode && !nsContentUtils::CanCallerAccess(sourceNode))
424 0 : return NS_OK;
425 :
426 0 : sourceNode.swap(*aSourceNode);
427 0 : return NS_OK;
428 : }
429 :
430 : NS_IMETHODIMP
431 0 : nsDOMDataTransfer::MozTypesAt(PRUint32 aIndex, nsIDOMDOMStringList** aTypes)
432 : {
433 0 : *aTypes = nsnull;
434 :
435 0 : nsRefPtr<nsDOMStringList> types = new nsDOMStringList();
436 0 : NS_ENSURE_TRUE(types, NS_ERROR_OUT_OF_MEMORY);
437 :
438 0 : if (aIndex < mItems.Length()) {
439 : // note that you can retrieve the types regardless of their principal
440 0 : nsTArray<TransferItem>& item = mItems[aIndex];
441 0 : for (PRUint32 i = 0; i < item.Length(); i++)
442 0 : types->Add(item[i].mFormat);
443 : }
444 :
445 0 : *aTypes = types;
446 0 : NS_ADDREF(*aTypes);
447 :
448 0 : return NS_OK;
449 : }
450 :
451 : NS_IMETHODIMP
452 0 : nsDOMDataTransfer::MozGetDataAt(const nsAString& aFormat,
453 : PRUint32 aIndex,
454 : nsIVariant** aData)
455 : {
456 0 : *aData = nsnull;
457 :
458 0 : if (aFormat.IsEmpty())
459 0 : return NS_OK;
460 :
461 0 : if (aIndex >= mItems.Length())
462 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
463 :
464 0 : nsAutoString format;
465 0 : GetRealFormat(aFormat, format);
466 :
467 0 : nsTArray<TransferItem>& item = mItems[aIndex];
468 :
469 : // allow access to any data in the drop and dragdrop events, or if the
470 : // UniversalXPConnect privilege is set, otherwise only allow access to
471 : // data from the same principal.
472 0 : nsIPrincipal* principal = nsnull;
473 0 : if (mEventType != NS_DRAGDROP_DROP && mEventType != NS_DRAGDROP_DRAGDROP &&
474 0 : !nsContentUtils::CallerHasUniversalXPConnect()) {
475 0 : nsresult rv = NS_OK;
476 0 : principal = GetCurrentPrincipal(&rv);
477 0 : NS_ENSURE_SUCCESS(rv, rv);
478 : }
479 :
480 0 : PRUint32 count = item.Length();
481 0 : for (PRUint32 i = 0; i < count; i++) {
482 0 : TransferItem& formatitem = item[i];
483 0 : if (formatitem.mFormat.Equals(format)) {
484 : bool subsumes;
485 0 : if (formatitem.mPrincipal && principal &&
486 0 : (NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes))
487 0 : return NS_ERROR_DOM_SECURITY_ERR;
488 :
489 0 : if (!formatitem.mData) {
490 0 : FillInExternalDragData(formatitem, aIndex);
491 : } else {
492 0 : nsCOMPtr<nsISupports> data;
493 0 : formatitem.mData->GetAsISupports(getter_AddRefs(data));
494 : // Make sure the code that is calling us is same-origin with the data.
495 0 : nsCOMPtr<nsIDOMEventTarget> pt = do_QueryInterface(data);
496 0 : if (pt) {
497 0 : nsresult rv = NS_OK;
498 0 : nsIScriptContext* c = pt->GetContextForEventHandlers(&rv);
499 0 : NS_ENSURE_TRUE(c && NS_SUCCEEDED(rv), NS_ERROR_DOM_SECURITY_ERR);
500 0 : nsIScriptObjectPrincipal* sp = c->GetObjectPrincipal();
501 0 : NS_ENSURE_TRUE(sp, NS_ERROR_DOM_SECURITY_ERR);
502 0 : nsIPrincipal* dataPrincipal = sp->GetPrincipal();
503 0 : NS_ENSURE_TRUE(dataPrincipal, NS_ERROR_DOM_SECURITY_ERR);
504 0 : NS_ENSURE_TRUE(principal || (principal = GetCurrentPrincipal(&rv)),
505 : NS_ERROR_DOM_SECURITY_ERR);
506 0 : NS_ENSURE_SUCCESS(rv, rv);
507 0 : bool equals = false;
508 0 : NS_ENSURE_TRUE(NS_SUCCEEDED(principal->Equals(dataPrincipal, &equals)) && equals,
509 : NS_ERROR_DOM_SECURITY_ERR);
510 : }
511 : }
512 0 : *aData = formatitem.mData;
513 0 : NS_IF_ADDREF(*aData);
514 0 : return NS_OK;
515 : }
516 : }
517 :
518 0 : return NS_OK;
519 : }
520 :
521 : NS_IMETHODIMP
522 0 : nsDOMDataTransfer::MozSetDataAt(const nsAString& aFormat,
523 : nsIVariant* aData,
524 : PRUint32 aIndex)
525 : {
526 0 : NS_ENSURE_TRUE(aData, NS_ERROR_NULL_POINTER);
527 :
528 0 : if (aFormat.IsEmpty())
529 0 : return NS_OK;
530 :
531 0 : if (mReadOnly)
532 0 : return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
533 :
534 : // Specifying an index less than the current length will replace an existing
535 : // item. Specifying an index equal to the current length will add a new item.
536 0 : if (aIndex > mItems.Length())
537 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
538 :
539 : // don't allow non-chrome to add file data
540 : // XXX perhaps this should also limit any non-string type as well
541 0 : if ((aFormat.EqualsLiteral("application/x-moz-file-promise") ||
542 0 : aFormat.EqualsLiteral("application/x-moz-file")) &&
543 0 : !nsContentUtils::CallerHasUniversalXPConnect()) {
544 0 : return NS_ERROR_DOM_SECURITY_ERR;
545 : }
546 :
547 0 : nsresult rv = NS_OK;
548 0 : nsIPrincipal* principal = GetCurrentPrincipal(&rv);
549 0 : NS_ENSURE_SUCCESS(rv, rv);
550 0 : return SetDataWithPrincipal(aFormat, aData, aIndex, principal);
551 : }
552 :
553 : NS_IMETHODIMP
554 0 : nsDOMDataTransfer::MozClearDataAt(const nsAString& aFormat, PRUint32 aIndex)
555 : {
556 0 : if (mReadOnly)
557 0 : return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
558 :
559 0 : if (aIndex >= mItems.Length())
560 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
561 :
562 0 : nsAutoString format;
563 0 : GetRealFormat(aFormat, format);
564 :
565 0 : nsresult rv = NS_OK;
566 0 : nsIPrincipal* principal = GetCurrentPrincipal(&rv);
567 0 : NS_ENSURE_SUCCESS(rv, rv);
568 :
569 : // if the format is empty, clear all formats
570 0 : bool clearall = format.IsEmpty();
571 :
572 0 : nsTArray<TransferItem>& item = mItems[aIndex];
573 : // count backwards so that the count and index don't have to be adjusted
574 : // after removing an element
575 0 : for (PRInt32 i = item.Length() - 1; i >= 0; i--) {
576 0 : TransferItem& formatitem = item[i];
577 0 : if (clearall || formatitem.mFormat.Equals(format)) {
578 : // don't allow removing data that has a stronger principal
579 : bool subsumes;
580 0 : if (formatitem.mPrincipal && principal &&
581 0 : (NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes))
582 0 : return NS_ERROR_DOM_SECURITY_ERR;
583 :
584 0 : item.RemoveElementAt(i);
585 :
586 : // if a format was specified, break out. Otherwise, loop around until
587 : // all formats have been removed
588 0 : if (!clearall)
589 0 : break;
590 : }
591 : }
592 :
593 : // if the last format for an item is removed, remove the entire item
594 0 : if (!item.Length())
595 0 : mItems.RemoveElementAt(aIndex);
596 :
597 0 : return NS_OK;
598 : }
599 :
600 : NS_IMETHODIMP
601 0 : nsDOMDataTransfer::SetDragImage(nsIDOMElement* aImage, PRInt32 aX, PRInt32 aY)
602 : {
603 0 : if (mReadOnly)
604 0 : return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
605 :
606 0 : if (aImage) {
607 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aImage);
608 0 : NS_ENSURE_TRUE(content, NS_ERROR_INVALID_ARG);
609 : }
610 0 : mDragImage = aImage;
611 0 : mDragImageX = aX;
612 0 : mDragImageY = aY;
613 0 : return NS_OK;
614 : }
615 :
616 : NS_IMETHODIMP
617 0 : nsDOMDataTransfer::AddElement(nsIDOMElement* aElement)
618 : {
619 0 : NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
620 :
621 0 : if (mReadOnly)
622 0 : return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
623 :
624 0 : mDragTarget = do_QueryInterface(aElement);
625 :
626 0 : return NS_OK;
627 : }
628 :
629 : nsresult
630 0 : nsDOMDataTransfer::Clone(PRUint32 aEventType, bool aUserCancelled,
631 : nsIDOMDataTransfer** aNewDataTransfer)
632 : {
633 : nsDOMDataTransfer* newDataTransfer =
634 : new nsDOMDataTransfer(aEventType, mEffectAllowed, mCursorState,
635 : mIsExternal, aUserCancelled, mItems,
636 0 : mDragImage, mDragImageX, mDragImageY);
637 0 : NS_ENSURE_TRUE(newDataTransfer, NS_ERROR_OUT_OF_MEMORY);
638 :
639 0 : *aNewDataTransfer = newDataTransfer;
640 0 : NS_ADDREF(*aNewDataTransfer);
641 0 : return NS_OK;
642 : }
643 :
644 : void
645 0 : nsDOMDataTransfer::GetTransferables(nsISupportsArray** aArray)
646 : {
647 0 : *aArray = nsnull;
648 :
649 : nsCOMPtr<nsISupportsArray> transArray =
650 0 : do_CreateInstance("@mozilla.org/supports-array;1");
651 0 : if (!transArray)
652 : return;
653 :
654 0 : bool added = false;
655 0 : PRUint32 count = mItems.Length();
656 0 : for (PRUint32 i = 0; i < count; i++) {
657 :
658 0 : nsTArray<TransferItem>& item = mItems[i];
659 0 : PRUint32 count = item.Length();
660 0 : if (!count)
661 0 : continue;
662 :
663 : nsCOMPtr<nsITransferable> transferable =
664 0 : do_CreateInstance("@mozilla.org/widget/transferable;1");
665 0 : if (!transferable)
666 : return;
667 :
668 0 : for (PRUint32 f = 0; f < count; f++) {
669 0 : TransferItem& formatitem = item[f];
670 0 : if (!formatitem.mData) // skip empty items
671 0 : continue;
672 :
673 : PRUint32 length;
674 0 : nsCOMPtr<nsISupports> convertedData;
675 0 : if (!ConvertFromVariant(formatitem.mData, getter_AddRefs(convertedData), &length))
676 0 : continue;
677 :
678 : // the underlying drag code uses text/unicode, so use that instead of text/plain
679 : const char* format;
680 0 : NS_ConvertUTF16toUTF8 utf8format(formatitem.mFormat);
681 0 : if (utf8format.EqualsLiteral("text/plain"))
682 0 : format = kUnicodeMime;
683 : else
684 0 : format = utf8format.get();
685 :
686 : // if a converter is set for a format, set the converter for the
687 : // transferable and don't add the item
688 0 : nsCOMPtr<nsIFormatConverter> converter = do_QueryInterface(convertedData);
689 0 : if (converter) {
690 0 : transferable->AddDataFlavor(format);
691 0 : transferable->SetConverter(converter);
692 0 : continue;
693 : }
694 :
695 0 : nsresult rv = transferable->SetTransferData(format, convertedData, length);
696 0 : if (NS_FAILED(rv))
697 : return;
698 :
699 0 : added = true;
700 : }
701 :
702 : // only append the transferable if data was successfully added to it
703 0 : if (added)
704 0 : transArray->AppendElement(transferable);
705 : }
706 :
707 0 : NS_ADDREF(*aArray = transArray);
708 : }
709 :
710 : bool
711 0 : nsDOMDataTransfer::ConvertFromVariant(nsIVariant* aVariant,
712 : nsISupports** aSupports,
713 : PRUint32* aLength)
714 : {
715 0 : *aSupports = nsnull;
716 0 : *aLength = 0;
717 :
718 : PRUint16 type;
719 0 : aVariant->GetDataType(&type);
720 0 : if (type == nsIDataType::VTYPE_INTERFACE ||
721 : type == nsIDataType::VTYPE_INTERFACE_IS) {
722 0 : nsCOMPtr<nsISupports> data;
723 0 : if (NS_FAILED(aVariant->GetAsISupports(getter_AddRefs(data))))
724 0 : return false;
725 :
726 0 : nsCOMPtr<nsIFlavorDataProvider> fdp = do_QueryInterface(data);
727 0 : if (fdp) {
728 : // for flavour data providers, use kFlavorHasDataProvider (which has the
729 : // value 0) as the length.
730 0 : NS_ADDREF(*aSupports = fdp);
731 0 : *aLength = nsITransferable::kFlavorHasDataProvider;
732 : }
733 : else {
734 : // wrap the item in an nsISupportsInterfacePointer
735 : nsCOMPtr<nsISupportsInterfacePointer> ptrSupports =
736 0 : do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
737 0 : if (!ptrSupports)
738 0 : return false;
739 :
740 0 : ptrSupports->SetData(data);
741 0 : NS_ADDREF(*aSupports = ptrSupports);
742 :
743 0 : *aLength = sizeof(nsISupportsInterfacePointer *);
744 : }
745 :
746 0 : return true;
747 : }
748 :
749 : PRUnichar* chrs;
750 0 : PRUint32 len = 0;
751 0 : nsresult rv = aVariant->GetAsWStringWithSize(&len, &chrs);
752 0 : if (NS_FAILED(rv))
753 0 : return false;
754 :
755 0 : nsAutoString str;
756 0 : str.Adopt(chrs, len);
757 :
758 : nsCOMPtr<nsISupportsString>
759 0 : strSupports(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
760 0 : if (!strSupports)
761 0 : return false;
762 :
763 0 : strSupports->SetData(str);
764 :
765 0 : *aSupports = strSupports;
766 0 : NS_ADDREF(*aSupports);
767 :
768 : // each character is two bytes
769 0 : *aLength = str.Length() << 1;
770 :
771 0 : return true;
772 : }
773 :
774 : void
775 0 : nsDOMDataTransfer::ClearAll()
776 : {
777 0 : mItems.Clear();
778 0 : }
779 :
780 : nsresult
781 0 : nsDOMDataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
782 : nsIVariant* aData,
783 : PRUint32 aIndex,
784 : nsIPrincipal* aPrincipal)
785 : {
786 0 : nsAutoString format;
787 0 : GetRealFormat(aFormat, format);
788 :
789 : // check if the item for the format already exists. In that case,
790 : // just replace it.
791 : TransferItem* formatitem;
792 0 : if (aIndex < mItems.Length()) {
793 0 : nsTArray<TransferItem>& item = mItems[aIndex];
794 0 : PRUint32 count = item.Length();
795 0 : for (PRUint32 i = 0; i < count; i++) {
796 0 : TransferItem& itemformat = item[i];
797 0 : if (itemformat.mFormat.Equals(format)) {
798 : // don't allow replacing data that has a stronger principal
799 : bool subsumes;
800 0 : if (itemformat.mPrincipal && aPrincipal &&
801 0 : (NS_FAILED(aPrincipal->Subsumes(itemformat.mPrincipal, &subsumes)) || !subsumes))
802 0 : return NS_ERROR_DOM_SECURITY_ERR;
803 :
804 0 : itemformat.mPrincipal = aPrincipal;
805 0 : itemformat.mData = aData;
806 0 : return NS_OK;
807 : }
808 : }
809 :
810 : // add a new format
811 0 : formatitem = item.AppendElement();
812 : }
813 : else {
814 0 : NS_ASSERTION(aIndex == mItems.Length(), "Index out of range");
815 :
816 : // add a new index
817 0 : nsTArray<TransferItem>* item = mItems.AppendElement();
818 0 : NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
819 :
820 0 : formatitem = item->AppendElement();
821 : }
822 :
823 0 : NS_ENSURE_TRUE(formatitem, NS_ERROR_OUT_OF_MEMORY);
824 :
825 0 : formatitem->mFormat = format;
826 0 : formatitem->mPrincipal = aPrincipal;
827 0 : formatitem->mData = aData;
828 :
829 0 : return NS_OK;
830 : }
831 :
832 : nsIPrincipal*
833 0 : nsDOMDataTransfer::GetCurrentPrincipal(nsresult* rv)
834 : {
835 0 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
836 :
837 0 : nsCOMPtr<nsIPrincipal> currentPrincipal;
838 0 : *rv = ssm->GetSubjectPrincipal(getter_AddRefs(currentPrincipal));
839 0 : NS_ENSURE_SUCCESS(*rv, nsnull);
840 :
841 0 : if (!currentPrincipal)
842 0 : ssm->GetSystemPrincipal(getter_AddRefs(currentPrincipal));
843 :
844 0 : return currentPrincipal.get();
845 : }
846 :
847 : void
848 0 : nsDOMDataTransfer::GetRealFormat(const nsAString& aInFormat, nsAString& aOutFormat)
849 : {
850 : // treat text/unicode as equivalent to text/plain
851 0 : nsAutoString lowercaseFormat;
852 0 : nsContentUtils::ASCIIToLower(aInFormat, lowercaseFormat);
853 0 : if (lowercaseFormat.EqualsLiteral("text") || lowercaseFormat.EqualsLiteral("text/unicode"))
854 0 : aOutFormat.AssignLiteral("text/plain");
855 0 : else if (lowercaseFormat.EqualsLiteral("url"))
856 0 : aOutFormat.AssignLiteral("text/uri-list");
857 : else
858 0 : aOutFormat.Assign(lowercaseFormat);
859 0 : }
860 :
861 : void
862 0 : nsDOMDataTransfer::CacheExternalFormats()
863 : {
864 : // Called during the constructor to cache the formats available from an
865 : // external drag. The data associated with each format will be set to null.
866 : // This data will instead only be retrieved in FillInExternalDragData when
867 : // asked for, as it may be time consuming for the source application to
868 : // generate it.
869 :
870 0 : nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
871 0 : if (!dragSession)
872 : return;
873 :
874 : // make sure that the system principal is used for external drags
875 0 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
876 0 : nsCOMPtr<nsIPrincipal> sysPrincipal;
877 0 : ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
878 :
879 : // there isn't a way to get a list of the formats that might be available on
880 : // all platforms, so just check for the types that can actually be imported
881 : // XXXndeakin there are some other formats but those are platform specific.
882 0 : const char* formats[] = { kFileMime, kHTMLMime, kURLMime, kURLDataMime, kUnicodeMime };
883 :
884 : PRUint32 count;
885 0 : dragSession->GetNumDropItems(&count);
886 0 : for (PRUint32 c = 0; c < count; c++) {
887 0 : for (PRUint32 f = 0; f < ArrayLength(formats); f++) {
888 : // IsDataFlavorSupported doesn't take an index as an argument and just
889 : // checks if any of the items support a particular flavor, even though
890 : // the GetData method does take an index. Here, we just assume that
891 : // every item being dragged has the same set of flavors.
892 : bool supported;
893 0 : dragSession->IsDataFlavorSupported(formats[f], &supported);
894 : // if the format is supported, add an item to the array with null as
895 : // the data. When retrieved, GetRealData will read the data.
896 0 : if (supported) {
897 0 : if (strcmp(formats[f], kUnicodeMime) == 0) {
898 0 : SetDataWithPrincipal(NS_LITERAL_STRING("text/plain"), nsnull, c, sysPrincipal);
899 : }
900 : else {
901 0 : if (strcmp(formats[f], kURLDataMime) == 0)
902 0 : SetDataWithPrincipal(NS_LITERAL_STRING("text/uri-list"), nsnull, c, sysPrincipal);
903 0 : SetDataWithPrincipal(NS_ConvertUTF8toUTF16(formats[f]), nsnull, c, sysPrincipal);
904 : }
905 : }
906 : }
907 : }
908 : }
909 :
910 : void
911 0 : nsDOMDataTransfer::FillInExternalDragData(TransferItem& aItem, PRUint32 aIndex)
912 : {
913 0 : NS_PRECONDITION(mIsExternal, "Not an external drag");
914 :
915 0 : if (!aItem.mData) {
916 : nsCOMPtr<nsITransferable> trans =
917 0 : do_CreateInstance("@mozilla.org/widget/transferable;1");
918 0 : if (!trans)
919 : return;
920 :
921 0 : NS_ConvertUTF16toUTF8 utf8format(aItem.mFormat);
922 0 : const char* format = utf8format.get();
923 0 : if (strcmp(format, "text/plain") == 0)
924 0 : format = kUnicodeMime;
925 0 : else if (strcmp(format, "text/uri-list") == 0)
926 0 : format = kURLDataMime;
927 :
928 0 : nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
929 0 : if (!dragSession)
930 : return;
931 :
932 0 : trans->AddDataFlavor(format);
933 0 : dragSession->GetData(trans, aIndex);
934 :
935 0 : PRUint32 length = 0;
936 0 : nsCOMPtr<nsISupports> data;
937 0 : trans->GetTransferData(format, getter_AddRefs(data), &length);
938 0 : if (!data)
939 : return;
940 :
941 0 : nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
942 0 : if (!variant)
943 : return;
944 :
945 0 : nsCOMPtr<nsISupportsString> supportsstr = do_QueryInterface(data);
946 0 : if (supportsstr) {
947 0 : nsAutoString str;
948 0 : supportsstr->GetData(str);
949 0 : variant->SetAsAString(str);
950 : }
951 : else {
952 0 : variant->SetAsISupports(data);
953 : }
954 0 : aItem.mData = variant;
955 : }
956 4392 : }
|