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