/src/mozilla-central/dom/events/DataTransferItemList.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 "DataTransferItemList.h" |
8 | | |
9 | | #include "nsContentUtils.h" |
10 | | #include "nsIGlobalObject.h" |
11 | | #include "nsIClipboard.h" |
12 | | #include "nsIScriptObjectPrincipal.h" |
13 | | #include "nsIScriptGlobalObject.h" |
14 | | #include "nsIScriptContext.h" |
15 | | #include "nsISupportsPrimitives.h" |
16 | | #include "nsQueryObject.h" |
17 | | #include "nsVariant.h" |
18 | | #include "mozilla/ContentEvents.h" |
19 | | #include "mozilla/EventForwards.h" |
20 | | #include "mozilla/storage/Variant.h" |
21 | | #include "mozilla/dom/DataTransferItemListBinding.h" |
22 | | |
23 | | namespace mozilla { |
24 | | namespace dom { |
25 | | |
26 | | NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DataTransferItemList, mDataTransfer, mItems, |
27 | | mIndexedItems, mFiles) |
28 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransferItemList) |
29 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransferItemList) |
30 | | |
31 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransferItemList) |
32 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
33 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
34 | 0 | NS_INTERFACE_MAP_END |
35 | | |
36 | | JSObject* |
37 | | DataTransferItemList::WrapObject(JSContext* aCx, |
38 | | JS::Handle<JSObject*> aGivenProto) |
39 | 0 | { |
40 | 0 | return DataTransferItemList_Binding::Wrap(aCx, this, aGivenProto); |
41 | 0 | } |
42 | | |
43 | | already_AddRefed<DataTransferItemList> |
44 | | DataTransferItemList::Clone(DataTransfer* aDataTransfer) const |
45 | 0 | { |
46 | 0 | RefPtr<DataTransferItemList> list = |
47 | 0 | new DataTransferItemList(aDataTransfer, mIsExternal); |
48 | 0 |
|
49 | 0 | // We need to clone the mItems and mIndexedItems lists while keeping the same |
50 | 0 | // correspondences between the mIndexedItems and mItems lists (namely, if an |
51 | 0 | // item is in mIndexedItems, and mItems it must have the same new identity) |
52 | 0 |
|
53 | 0 | // First, we copy over indexedItems, and clone every entry. Then, we go over |
54 | 0 | // mItems. For every entry, we use its mIndex property to locate it in |
55 | 0 | // mIndexedItems on the original DataTransferItemList, and then copy over the |
56 | 0 | // reference from the same index pair on the new DataTransferItemList |
57 | 0 |
|
58 | 0 | list->mIndexedItems.SetLength(mIndexedItems.Length()); |
59 | 0 | list->mItems.SetLength(mItems.Length()); |
60 | 0 |
|
61 | 0 | // Copy over mIndexedItems, cloning every entry |
62 | 0 | for (uint32_t i = 0; i < mIndexedItems.Length(); i++) { |
63 | 0 | const nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[i]; |
64 | 0 | nsTArray<RefPtr<DataTransferItem>>& newItems = list->mIndexedItems[i]; |
65 | 0 | newItems.SetLength(items.Length()); |
66 | 0 | for (uint32_t j = 0; j < items.Length(); j++) { |
67 | 0 | newItems[j] = items[j]->Clone(aDataTransfer); |
68 | 0 | } |
69 | 0 | } |
70 | 0 |
|
71 | 0 | // Copy over mItems, getting the actual entries from mIndexedItems |
72 | 0 | for (uint32_t i = 0; i < mItems.Length(); i++) { |
73 | 0 | uint32_t index = mItems[i]->Index(); |
74 | 0 | MOZ_ASSERT(index < mIndexedItems.Length()); |
75 | 0 | uint32_t subIndex = mIndexedItems[index].IndexOf(mItems[i]); |
76 | 0 |
|
77 | 0 | // Copy over the reference |
78 | 0 | list->mItems[i] = list->mIndexedItems[index][subIndex]; |
79 | 0 | } |
80 | 0 |
|
81 | 0 | return list.forget(); |
82 | 0 | } |
83 | | |
84 | | void |
85 | | DataTransferItemList::Remove(uint32_t aIndex, |
86 | | nsIPrincipal& aSubjectPrincipal, |
87 | | ErrorResult& aRv) |
88 | 0 | { |
89 | 0 | if (mDataTransfer->IsReadOnly()) { |
90 | 0 | aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
91 | 0 | return; |
92 | 0 | } |
93 | 0 | |
94 | 0 | if (aIndex >= Length()) { |
95 | 0 | return; |
96 | 0 | } |
97 | 0 | |
98 | 0 | ClearDataHelper(mItems[aIndex], aIndex, -1, aSubjectPrincipal, aRv); |
99 | 0 | } |
100 | | |
101 | | DataTransferItem* |
102 | | DataTransferItemList::IndexedGetter(uint32_t aIndex, bool& aFound) const |
103 | 0 | { |
104 | 0 | if (aIndex >= mItems.Length()) { |
105 | 0 | aFound = false; |
106 | 0 | return nullptr; |
107 | 0 | } |
108 | 0 | |
109 | 0 | MOZ_ASSERT(mItems[aIndex]); |
110 | 0 | aFound = true; |
111 | 0 | return mItems[aIndex]; |
112 | 0 | } |
113 | | |
114 | | uint32_t |
115 | | DataTransferItemList::MozItemCount() const |
116 | 0 | { |
117 | 0 | uint32_t length = mIndexedItems.Length(); |
118 | 0 | // XXX: Compat hack - Index 0 always exists due to changes in internals, but |
119 | 0 | // if it is empty, scripts using the moz* APIs should see it as not existing. |
120 | 0 | if (length == 1 && mIndexedItems[0].IsEmpty()) { |
121 | 0 | return 0; |
122 | 0 | } |
123 | 0 | return length; |
124 | 0 | } |
125 | | |
126 | | void |
127 | | DataTransferItemList::Clear(nsIPrincipal& aSubjectPrincipal, |
128 | | ErrorResult& aRv) |
129 | 0 | { |
130 | 0 | if (NS_WARN_IF(mDataTransfer->IsReadOnly())) { |
131 | 0 | return; |
132 | 0 | } |
133 | 0 | |
134 | 0 | uint32_t count = Length(); |
135 | 0 | for (uint32_t i = 0; i < count; i++) { |
136 | 0 | // We always remove the last item first, to avoid moving items around in |
137 | 0 | // memory as much |
138 | 0 | Remove(Length() - 1, aSubjectPrincipal, aRv); |
139 | 0 | ENSURE_SUCCESS_VOID(aRv); |
140 | 0 | } |
141 | 0 |
|
142 | 0 | MOZ_ASSERT(Length() == 0); |
143 | 0 | } |
144 | | |
145 | | DataTransferItem* |
146 | | DataTransferItemList::Add(const nsAString& aData, |
147 | | const nsAString& aType, |
148 | | nsIPrincipal& aSubjectPrincipal, |
149 | | ErrorResult& aRv) |
150 | 0 | { |
151 | 0 | if (NS_WARN_IF(mDataTransfer->IsReadOnly())) { |
152 | 0 | return nullptr; |
153 | 0 | } |
154 | 0 | |
155 | 0 | RefPtr<nsVariantCC> data(new nsVariantCC()); |
156 | 0 | data->SetAsAString(aData); |
157 | 0 |
|
158 | 0 | nsAutoString format; |
159 | 0 | mDataTransfer->GetRealFormat(aType, format); |
160 | 0 |
|
161 | 0 | if (!DataTransfer::PrincipalMaySetData(format, data, &aSubjectPrincipal)) { |
162 | 0 | aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
163 | 0 | return nullptr; |
164 | 0 | } |
165 | 0 | |
166 | 0 | // We add the textual data to index 0. We set aInsertOnly to true, as we don't |
167 | 0 | // want to update an existing entry if it is already present, as per the spec. |
168 | 0 | RefPtr<DataTransferItem> item = |
169 | 0 | SetDataWithPrincipal(format, data, 0, &aSubjectPrincipal, |
170 | 0 | /* aInsertOnly = */ true, |
171 | 0 | /* aHidden = */ false, |
172 | 0 | aRv); |
173 | 0 | if (NS_WARN_IF(aRv.Failed())) { |
174 | 0 | return nullptr; |
175 | 0 | } |
176 | 0 | MOZ_ASSERT(item->Kind() != DataTransferItem::KIND_FILE); |
177 | 0 |
|
178 | 0 | return item; |
179 | 0 | } |
180 | | |
181 | | DataTransferItem* |
182 | | DataTransferItemList::Add(File& aData, |
183 | | nsIPrincipal& aSubjectPrincipal, |
184 | | ErrorResult& aRv) |
185 | 0 | { |
186 | 0 | if (mDataTransfer->IsReadOnly()) { |
187 | 0 | return nullptr; |
188 | 0 | } |
189 | 0 | |
190 | 0 | nsCOMPtr<nsISupports> supports = do_QueryObject(&aData); |
191 | 0 | nsCOMPtr<nsIWritableVariant> data = new nsVariant(); |
192 | 0 | data->SetAsISupports(supports); |
193 | 0 |
|
194 | 0 | nsAutoString type; |
195 | 0 | aData.GetType(type); |
196 | 0 |
|
197 | 0 | if (!DataTransfer::PrincipalMaySetData(type, data, &aSubjectPrincipal)) { |
198 | 0 | aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
199 | 0 | return nullptr; |
200 | 0 | } |
201 | 0 | |
202 | 0 | // We need to add this as a new item, as multiple files can't exist in the |
203 | 0 | // same item in the Moz DataTransfer layout. It will be appended at the end of |
204 | 0 | // the internal specced layout. |
205 | 0 | uint32_t index = mIndexedItems.Length(); |
206 | 0 | RefPtr<DataTransferItem> item = |
207 | 0 | SetDataWithPrincipal(type, data, index, &aSubjectPrincipal, |
208 | 0 | /* aInsertOnly = */ true, |
209 | 0 | /* aHidden = */ false, |
210 | 0 | aRv); |
211 | 0 | if (NS_WARN_IF(aRv.Failed())) { |
212 | 0 | return nullptr; |
213 | 0 | } |
214 | 0 | MOZ_ASSERT(item->Kind() == DataTransferItem::KIND_FILE); |
215 | 0 |
|
216 | 0 | return item; |
217 | 0 | } |
218 | | |
219 | | already_AddRefed<FileList> |
220 | | DataTransferItemList::Files(nsIPrincipal* aPrincipal) |
221 | 0 | { |
222 | 0 | // The DataTransfer can hold data with varying principals, coming from |
223 | 0 | // different windows. This means that permissions checks need to be made when |
224 | 0 | // accessing data from the DataTransfer. With the accessor methods, this is |
225 | 0 | // checked by DataTransferItem::Data(), however with files, we keep a cached |
226 | 0 | // live copy of the files list for spec compliance. |
227 | 0 | // |
228 | 0 | // A DataTransfer is only exposed to one webpage, and chrome code. The chrome |
229 | 0 | // code should be able to see all files on the DataTransfer, while the webpage |
230 | 0 | // should only be able to see the files it can see. As chrome code doesn't |
231 | 0 | // need as strict spec compliance as web visible code, we generate a new |
232 | 0 | // FileList object every time you access the Files list from chrome code, but |
233 | 0 | // re-use the cached one when accessing from non-chrome code. |
234 | 0 | // |
235 | 0 | // It is not legal to expose an identical DataTransfer object is to multiple |
236 | 0 | // different principals without using the `Clone` method or similar to copy it |
237 | 0 | // first. If that happens, this method will assert, and return nullptr in |
238 | 0 | // release builds. If this functionality is required in the future, a more |
239 | 0 | // advanced caching mechanism for the FileList objects will be required. |
240 | 0 | RefPtr<FileList> files; |
241 | 0 | if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
242 | 0 | files = new FileList(mDataTransfer); |
243 | 0 | GenerateFiles(files, aPrincipal); |
244 | 0 | return files.forget(); |
245 | 0 | } |
246 | 0 | |
247 | 0 | if (!mFiles) { |
248 | 0 | mFiles = new FileList(mDataTransfer); |
249 | 0 | mFilesPrincipal = aPrincipal; |
250 | 0 | RegenerateFiles(); |
251 | 0 | } |
252 | 0 |
|
253 | 0 | if (!aPrincipal->Subsumes(mFilesPrincipal)) { |
254 | 0 | MOZ_ASSERT(false, "This DataTransfer should only be accessed by the system " |
255 | 0 | "and a single principal"); |
256 | 0 | return nullptr; |
257 | 0 | } |
258 | 0 |
|
259 | 0 | files = mFiles; |
260 | 0 | return files.forget(); |
261 | 0 | } |
262 | | |
263 | | void |
264 | | DataTransferItemList::MozRemoveByTypeAt(const nsAString& aType, |
265 | | uint32_t aIndex, |
266 | | nsIPrincipal& aSubjectPrincipal, |
267 | | ErrorResult& aRv) |
268 | 0 | { |
269 | 0 | if (NS_WARN_IF(mDataTransfer->IsReadOnly() || |
270 | 0 | aIndex >= mIndexedItems.Length())) { |
271 | 0 | return; |
272 | 0 | } |
273 | 0 | |
274 | 0 | bool removeAll = aType.IsEmpty(); |
275 | 0 |
|
276 | 0 | nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[aIndex]; |
277 | 0 | uint32_t count = items.Length(); |
278 | 0 | // We remove the last item of the list repeatedly - that way we don't |
279 | 0 | // have to worry about modifying the loop iterator |
280 | 0 | if (removeAll) { |
281 | 0 | for (uint32_t i = 0; i < count; ++i) { |
282 | 0 | uint32_t index = items.Length() - 1; |
283 | 0 | MOZ_ASSERT(index == count - i - 1); |
284 | 0 |
|
285 | 0 | ClearDataHelper(items[index], -1, index, aSubjectPrincipal, aRv); |
286 | 0 | if (NS_WARN_IF(aRv.Failed())) { |
287 | 0 | return; |
288 | 0 | } |
289 | 0 | } |
290 | 0 |
|
291 | 0 | // items is no longer a valid reference, as removing the last element from |
292 | 0 | // it via ClearDataHelper invalidated it. so we can't MOZ_ASSERT that the |
293 | 0 | // length is now 0. |
294 | 0 | return; |
295 | 0 | } |
296 | 0 | |
297 | 0 | for (uint32_t i = 0; i < count; ++i) { |
298 | 0 | // NOTE: As this is a moz-prefixed API, it works based on internal types. |
299 | 0 | nsAutoString type; |
300 | 0 | items[i]->GetInternalType(type); |
301 | 0 | if (type == aType) { |
302 | 0 | ClearDataHelper(items[i], -1, i, aSubjectPrincipal, aRv); |
303 | 0 | return; |
304 | 0 | } |
305 | 0 | } |
306 | 0 | } |
307 | | |
308 | | DataTransferItem* |
309 | | DataTransferItemList::MozItemByTypeAt(const nsAString& aType, uint32_t aIndex) |
310 | 0 | { |
311 | 0 | if (NS_WARN_IF(aIndex >= mIndexedItems.Length())) { |
312 | 0 | return nullptr; |
313 | 0 | } |
314 | 0 | |
315 | 0 | uint32_t count = mIndexedItems[aIndex].Length(); |
316 | 0 | for (uint32_t i = 0; i < count; i++) { |
317 | 0 | RefPtr<DataTransferItem> item = mIndexedItems[aIndex][i]; |
318 | 0 | // NOTE: As this is a moz-prefixed API it works on internal types |
319 | 0 | nsString type; |
320 | 0 | item->GetInternalType(type); |
321 | 0 | if (type.Equals(aType)) { |
322 | 0 | return item; |
323 | 0 | } |
324 | 0 | } |
325 | 0 |
|
326 | 0 | return nullptr; |
327 | 0 | } |
328 | | |
329 | | already_AddRefed<DataTransferItem> |
330 | | DataTransferItemList::SetDataWithPrincipal(const nsAString& aType, |
331 | | nsIVariant* aData, |
332 | | uint32_t aIndex, |
333 | | nsIPrincipal* aPrincipal, |
334 | | bool aInsertOnly, |
335 | | bool aHidden, |
336 | | ErrorResult& aRv) |
337 | 0 | { |
338 | 0 | if (aIndex < mIndexedItems.Length()) { |
339 | 0 | nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[aIndex]; |
340 | 0 | uint32_t count = items.Length(); |
341 | 0 | for (uint32_t i = 0; i < count; i++) { |
342 | 0 | RefPtr<DataTransferItem> item = items[i]; |
343 | 0 | nsString type; |
344 | 0 | item->GetInternalType(type); |
345 | 0 | if (type.Equals(aType)) { |
346 | 0 | if (NS_WARN_IF(aInsertOnly)) { |
347 | 0 | aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
348 | 0 | return nullptr; |
349 | 0 | } |
350 | 0 | |
351 | 0 | // don't allow replacing data that has a stronger principal |
352 | 0 | bool subsumes; |
353 | 0 | if (NS_WARN_IF(item->Principal() && aPrincipal && |
354 | 0 | (NS_FAILED(aPrincipal->Subsumes(item->Principal(), |
355 | 0 | &subsumes)) |
356 | 0 | || !subsumes))) { |
357 | 0 | aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
358 | 0 | return nullptr; |
359 | 0 | } |
360 | 0 | item->SetPrincipal(aPrincipal); |
361 | 0 |
|
362 | 0 | DataTransferItem::eKind oldKind = item->Kind(); |
363 | 0 | item->SetData(aData); |
364 | 0 |
|
365 | 0 | mDataTransfer->TypesListMayHaveChanged(); |
366 | 0 |
|
367 | 0 | if (aIndex != 0) { |
368 | 0 | // If the item changes from being a file to not a file or vice-versa, |
369 | 0 | // its presence in the mItems array may need to change. |
370 | 0 | if (item->Kind() == DataTransferItem::KIND_FILE && |
371 | 0 | oldKind != DataTransferItem::KIND_FILE) { |
372 | 0 | // not file => file |
373 | 0 | mItems.AppendElement(item); |
374 | 0 | } else if (item->Kind() != DataTransferItem::KIND_FILE && |
375 | 0 | oldKind == DataTransferItem::KIND_FILE) { |
376 | 0 | // file => not file |
377 | 0 | mItems.RemoveElement(item); |
378 | 0 | } |
379 | 0 | } |
380 | 0 |
|
381 | 0 | // Regenerate the Files array if we have modified a file's status |
382 | 0 | if (item->Kind() == DataTransferItem::KIND_FILE || |
383 | 0 | oldKind == DataTransferItem::KIND_FILE) { |
384 | 0 | RegenerateFiles(); |
385 | 0 | } |
386 | 0 |
|
387 | 0 | return item.forget(); |
388 | 0 | } |
389 | 0 | } |
390 | 0 | } else { |
391 | 0 | // Make sure that we aren't adding past the end of the mIndexedItems array. |
392 | 0 | // XXX Should this be a MOZ_ASSERT instead? |
393 | 0 | aIndex = mIndexedItems.Length(); |
394 | 0 | } |
395 | 0 |
|
396 | 0 | // Add the new item |
397 | 0 | RefPtr<DataTransferItem> item = AppendNewItem(aIndex, aType, aData, aPrincipal, aHidden); |
398 | 0 |
|
399 | 0 | if (item->Kind() == DataTransferItem::KIND_FILE) { |
400 | 0 | RegenerateFiles(); |
401 | 0 | } |
402 | 0 |
|
403 | 0 | return item.forget(); |
404 | 0 | } |
405 | | |
406 | | DataTransferItem* |
407 | | DataTransferItemList::AppendNewItem(uint32_t aIndex, |
408 | | const nsAString& aType, |
409 | | nsIVariant* aData, |
410 | | nsIPrincipal* aPrincipal, |
411 | | bool aHidden) |
412 | 0 | { |
413 | 0 | if (mIndexedItems.Length() <= aIndex) { |
414 | 0 | MOZ_ASSERT(mIndexedItems.Length() == aIndex); |
415 | 0 | mIndexedItems.AppendElement(); |
416 | 0 | } |
417 | 0 | RefPtr<DataTransferItem> item = new DataTransferItem(mDataTransfer, aType); |
418 | 0 | item->SetIndex(aIndex); |
419 | 0 | item->SetPrincipal(aPrincipal); |
420 | 0 | item->SetData(aData); |
421 | 0 | item->SetChromeOnly(aHidden); |
422 | 0 |
|
423 | 0 | mIndexedItems[aIndex].AppendElement(item); |
424 | 0 |
|
425 | 0 | // We only want to add the item to the main mItems list if the index we are |
426 | 0 | // adding to is 0, or the item we are adding is a file. If we add an item |
427 | 0 | // which is not a file to a non-zero index, invariants could be broken. |
428 | 0 | // (namely the invariant that there are not 2 non-file entries in the items |
429 | 0 | // array with the same type). |
430 | 0 | // |
431 | 0 | // We also want to update our DataTransfer's type list any time we're adding a |
432 | 0 | // KIND_FILE item, or an item at index 0. |
433 | 0 | if (item->Kind() == DataTransferItem::KIND_FILE || aIndex == 0) { |
434 | 0 | if (!aHidden) { |
435 | 0 | mItems.AppendElement(item); |
436 | 0 | } |
437 | 0 | mDataTransfer->TypesListMayHaveChanged(); |
438 | 0 | } |
439 | 0 |
|
440 | 0 | return item; |
441 | 0 | } |
442 | | |
443 | | const nsTArray<RefPtr<DataTransferItem>>* |
444 | | DataTransferItemList::MozItemsAt(uint32_t aIndex) // -- INDEXED |
445 | 0 | { |
446 | 0 | if (aIndex >= mIndexedItems.Length()) { |
447 | 0 | return nullptr; |
448 | 0 | } |
449 | 0 | |
450 | 0 | return &mIndexedItems[aIndex]; |
451 | 0 | } |
452 | | |
453 | | void |
454 | | DataTransferItemList::PopIndexZero() |
455 | 0 | { |
456 | 0 | MOZ_ASSERT(mIndexedItems.Length() > 1); |
457 | 0 | MOZ_ASSERT(mIndexedItems[0].IsEmpty()); |
458 | 0 |
|
459 | 0 | mIndexedItems.RemoveElementAt(0); |
460 | 0 |
|
461 | 0 | // Update the index of every element which has now been shifted |
462 | 0 | for (uint32_t i = 0; i < mIndexedItems.Length(); i++) { |
463 | 0 | nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[i]; |
464 | 0 | for (uint32_t j = 0; j < items.Length(); j++) { |
465 | 0 | items[j]->SetIndex(i); |
466 | 0 | } |
467 | 0 | } |
468 | 0 | } |
469 | | |
470 | | void |
471 | | DataTransferItemList::ClearAllItems() |
472 | 0 | { |
473 | 0 | // We always need to have index 0, so don't delete that one |
474 | 0 | mItems.Clear(); |
475 | 0 | mIndexedItems.Clear(); |
476 | 0 | mIndexedItems.SetLength(1); |
477 | 0 | mDataTransfer->TypesListMayHaveChanged(); |
478 | 0 |
|
479 | 0 | // Re-generate files (into an empty list) |
480 | 0 | RegenerateFiles(); |
481 | 0 | } |
482 | | |
483 | | void |
484 | | DataTransferItemList::ClearDataHelper(DataTransferItem* aItem, |
485 | | uint32_t aIndexHint, |
486 | | uint32_t aMozOffsetHint, |
487 | | nsIPrincipal& aSubjectPrincipal, |
488 | | ErrorResult& aRv) |
489 | 0 | { |
490 | 0 | MOZ_ASSERT(aItem); |
491 | 0 | if (NS_WARN_IF(mDataTransfer->IsReadOnly())) { |
492 | 0 | return; |
493 | 0 | } |
494 | 0 | |
495 | 0 | if (aItem->Principal() && !aSubjectPrincipal.Subsumes(aItem->Principal())) { |
496 | 0 | aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
497 | 0 | return; |
498 | 0 | } |
499 | 0 | |
500 | 0 | // Check if the aIndexHint is actually the index, and then remove the item |
501 | 0 | // from aItems |
502 | 0 | bool found; |
503 | 0 | if (IndexedGetter(aIndexHint, found) == aItem) { |
504 | 0 | mItems.RemoveElementAt(aIndexHint); |
505 | 0 | } else { |
506 | 0 | mItems.RemoveElement(aItem); |
507 | 0 | } |
508 | 0 |
|
509 | 0 | // Check if the aMozIndexHint and aMozOffsetHint are actually the index and |
510 | 0 | // offset, and then remove them from mIndexedItems |
511 | 0 | MOZ_ASSERT(aItem->Index() < mIndexedItems.Length()); |
512 | 0 | nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[aItem->Index()]; |
513 | 0 | if (aMozOffsetHint < items.Length() && aItem == items[aMozOffsetHint]) { |
514 | 0 | items.RemoveElementAt(aMozOffsetHint); |
515 | 0 | } else { |
516 | 0 | items.RemoveElement(aItem); |
517 | 0 | } |
518 | 0 |
|
519 | 0 | mDataTransfer->TypesListMayHaveChanged(); |
520 | 0 |
|
521 | 0 | // Check if we should remove the index. We never remove index 0. |
522 | 0 | if (items.Length() == 0 && aItem->Index() != 0) { |
523 | 0 | mIndexedItems.RemoveElementAt(aItem->Index()); |
524 | 0 |
|
525 | 0 | // Update the index of every element which has now been shifted |
526 | 0 | for (uint32_t i = aItem->Index(); i < mIndexedItems.Length(); i++) { |
527 | 0 | nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[i]; |
528 | 0 | for (uint32_t j = 0; j < items.Length(); j++) { |
529 | 0 | items[j]->SetIndex(i); |
530 | 0 | } |
531 | 0 | } |
532 | 0 | } |
533 | 0 |
|
534 | 0 | // Give the removed item the invalid index |
535 | 0 | aItem->SetIndex(-1); |
536 | 0 |
|
537 | 0 | if (aItem->Kind() == DataTransferItem::KIND_FILE) { |
538 | 0 | RegenerateFiles(); |
539 | 0 | } |
540 | 0 | } |
541 | | |
542 | | void |
543 | | DataTransferItemList::RegenerateFiles() |
544 | 0 | { |
545 | 0 | // We don't want to regenerate the files list unless we already have a files |
546 | 0 | // list. That way we can avoid the unnecessary work if the user never touches |
547 | 0 | // the files list. |
548 | 0 | if (mFiles) { |
549 | 0 | // We clear the list rather than performing smaller updates, because it |
550 | 0 | // simplifies the logic greatly on this code path, which should be very |
551 | 0 | // infrequently used. |
552 | 0 | mFiles->Clear(); |
553 | 0 |
|
554 | 0 | DataTransferItemList::GenerateFiles(mFiles, mFilesPrincipal); |
555 | 0 | } |
556 | 0 | } |
557 | | |
558 | | void |
559 | | DataTransferItemList::GenerateFiles(FileList* aFiles, |
560 | | nsIPrincipal* aFilesPrincipal) |
561 | 0 | { |
562 | 0 | MOZ_ASSERT(aFiles); |
563 | 0 | MOZ_ASSERT(aFilesPrincipal); |
564 | 0 |
|
565 | 0 | // For non-system principals, the Files list should be empty if the |
566 | 0 | // DataTransfer is protected. |
567 | 0 | if (!nsContentUtils::IsSystemPrincipal(aFilesPrincipal) && |
568 | 0 | mDataTransfer->IsProtected()) { |
569 | 0 | return; |
570 | 0 | } |
571 | 0 | |
572 | 0 | uint32_t count = Length(); |
573 | 0 | for (uint32_t i = 0; i < count; i++) { |
574 | 0 | bool found; |
575 | 0 | RefPtr<DataTransferItem> item = IndexedGetter(i, found); |
576 | 0 | MOZ_ASSERT(found); |
577 | 0 |
|
578 | 0 | if (item->Kind() == DataTransferItem::KIND_FILE) { |
579 | 0 | RefPtr<File> file = item->GetAsFile(*aFilesPrincipal, IgnoreErrors()); |
580 | 0 | if (NS_WARN_IF(!file)) { |
581 | 0 | continue; |
582 | 0 | } |
583 | 0 | aFiles->Append(file); |
584 | 0 | } |
585 | 0 | } |
586 | 0 | } |
587 | | |
588 | | } // namespace mozilla |
589 | | } // namespace dom |