1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2/* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6#import <Cocoa/Cocoa.h> 7 8#include "nsFilePicker.h" 9#include "nsCOMPtr.h" 10#include "nsReadableUtils.h" 11#include "nsNetUtil.h" 12#include "nsIFile.h" 13#include "nsILocalFileMac.h" 14#include "nsArrayEnumerator.h" 15#include "nsIStringBundle.h" 16#include "nsCocoaUtils.h" 17#include "mozilla/Preferences.h" 18 19// This must be included last: 20#include "nsObjCExceptions.h" 21 22using namespace mozilla; 23 24const float kAccessoryViewPadding = 5; 25const int kSaveTypeControlTag = 1; 26 27static bool gCallSecretHiddenFileAPI = false; 28const char kShowHiddenFilesPref[] = "filepicker.showHiddenFiles"; 29 30/** 31 * This class is an observer of NSPopUpButton selection change. 32 */ 33@interface NSPopUpButtonObserver : NSObject { 34 NSPopUpButton* mPopUpButton; 35 NSOpenPanel* mOpenPanel; 36 nsFilePicker* mFilePicker; 37} 38- (void)setPopUpButton:(NSPopUpButton*)aPopUpButton; 39- (void)setOpenPanel:(NSOpenPanel*)aOpenPanel; 40- (void)setFilePicker:(nsFilePicker*)aFilePicker; 41- (void)menuChangedItem:(NSNotification*)aSender; 42@end 43 44NS_IMPL_ISUPPORTS(nsFilePicker, nsIFilePicker) 45 46// We never want to call the secret show hidden files API unless the pref 47// has been set. Once the pref has been set we always need to call it even 48// if it disappears so that we stop showing hidden files if a user deletes 49// the pref. If the secret API was used once and things worked out it should 50// continue working for subsequent calls so the user is at no more risk. 51static void SetShowHiddenFileState(NSSavePanel* panel) { 52 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; 53 54 bool show = false; 55 if (NS_SUCCEEDED(Preferences::GetBool(kShowHiddenFilesPref, &show))) { 56 gCallSecretHiddenFileAPI = true; 57 } 58 59 if (gCallSecretHiddenFileAPI) { 60 // invoke a method to get a Cocoa-internal nav view 61 SEL navViewSelector = @selector(_navView); 62 NSMethodSignature* navViewSignature = [panel methodSignatureForSelector:navViewSelector]; 63 if (!navViewSignature) return; 64 NSInvocation* navViewInvocation = [NSInvocation invocationWithMethodSignature:navViewSignature]; 65 [navViewInvocation setSelector:navViewSelector]; 66 [navViewInvocation setTarget:panel]; 67 [navViewInvocation invoke]; 68 69 // get the returned nav view 70 id navView = nil; 71 [navViewInvocation getReturnValue:&navView]; 72 73 // invoke the secret show hidden file state method on the nav view 74 SEL showHiddenFilesSelector = @selector(setShowsHiddenFiles:); 75 NSMethodSignature* showHiddenFilesSignature = 76 [navView methodSignatureForSelector:showHiddenFilesSelector]; 77 if (!showHiddenFilesSignature) return; 78 NSInvocation* showHiddenFilesInvocation = 79 [NSInvocation invocationWithMethodSignature:showHiddenFilesSignature]; 80 [showHiddenFilesInvocation setSelector:showHiddenFilesSelector]; 81 [showHiddenFilesInvocation setTarget:navView]; 82 [showHiddenFilesInvocation setArgument:&show atIndex:2]; 83 [showHiddenFilesInvocation invoke]; 84 } 85 86 NS_OBJC_END_TRY_IGNORE_BLOCK; 87} 88 89nsFilePicker::nsFilePicker() : mSelectedTypeIndex(0) {} 90 91nsFilePicker::~nsFilePicker() {} 92 93void nsFilePicker::InitNative(nsIWidget* aParent, const nsAString& aTitle) { mTitle = aTitle; } 94 95NSView* nsFilePicker::GetAccessoryView() { 96 NS_OBJC_BEGIN_TRY_BLOCK_RETURN; 97 98 NSView* accessoryView = [[[NSView alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)] autorelease]; 99 100 // Set a label's default value. 101 NSString* label = @"Format:"; 102 103 // Try to get the localized string. 104 nsCOMPtr<nsIStringBundleService> sbs = do_GetService(NS_STRINGBUNDLE_CONTRACTID); 105 nsCOMPtr<nsIStringBundle> bundle; 106 nsresult rv = 107 sbs->CreateBundle("chrome://global/locale/filepicker.properties", getter_AddRefs(bundle)); 108 if (NS_SUCCEEDED(rv)) { 109 nsAutoString locaLabel; 110 rv = bundle->GetStringFromName("formatLabel", locaLabel); 111 if (NS_SUCCEEDED(rv)) { 112 label = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(locaLabel.get()) 113 length:locaLabel.Length()]; 114 } 115 } 116 117 // set up label text field 118 NSTextField* textField = [[[NSTextField alloc] init] autorelease]; 119 [textField setEditable:NO]; 120 [textField setSelectable:NO]; 121 [textField setDrawsBackground:NO]; 122 [textField setBezeled:NO]; 123 [textField setBordered:NO]; 124 [textField setFont:[NSFont labelFontOfSize:13.0]]; 125 [textField setStringValue:label]; 126 [textField setTag:0]; 127 [textField sizeToFit]; 128 129 // set up popup button 130 NSPopUpButton* popupButton = [[[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 0, 0) 131 pullsDown:NO] autorelease]; 132 uint32_t numMenuItems = mTitles.Length(); 133 for (uint32_t i = 0; i < numMenuItems; i++) { 134 const nsString& currentTitle = mTitles[i]; 135 NSString* titleString; 136 if (currentTitle.IsEmpty()) { 137 const nsString& currentFilter = mFilters[i]; 138 titleString = 139 [[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(currentFilter.get()) 140 length:currentFilter.Length()]; 141 } else { 142 titleString = 143 [[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(currentTitle.get()) 144 length:currentTitle.Length()]; 145 } 146 [popupButton addItemWithTitle:titleString]; 147 [titleString release]; 148 } 149 if (mSelectedTypeIndex >= 0 && (uint32_t)mSelectedTypeIndex < numMenuItems) 150 [popupButton selectItemAtIndex:mSelectedTypeIndex]; 151 [popupButton setTag:kSaveTypeControlTag]; 152 [popupButton sizeToFit]; // we have to do sizeToFit to get the height calculated for us 153 // This is just a default width that works well, doesn't truncate the vast majority of 154 // things that might end up in the menu. 155 [popupButton setFrameSize:NSMakeSize(180, [popupButton frame].size.height)]; 156 157 // position everything based on control sizes with kAccessoryViewPadding pix padding 158 // on each side kAccessoryViewPadding pix horizontal padding between controls 159 float greatestHeight = [textField frame].size.height; 160 if ([popupButton frame].size.height > greatestHeight) 161 greatestHeight = [popupButton frame].size.height; 162 float totalViewHeight = greatestHeight + kAccessoryViewPadding * 2; 163 float totalViewWidth = 164 [textField frame].size.width + [popupButton frame].size.width + kAccessoryViewPadding * 3; 165 [accessoryView setFrameSize:NSMakeSize(totalViewWidth, totalViewHeight)]; 166 167 float textFieldOriginY = 168 ((greatestHeight - [textField frame].size.height) / 2 + 1) + kAccessoryViewPadding; 169 [textField setFrameOrigin:NSMakePoint(kAccessoryViewPadding, textFieldOriginY)]; 170 171 float popupOriginX = [textField frame].size.width + kAccessoryViewPadding * 2; 172 float popupOriginY = 173 ((greatestHeight - [popupButton frame].size.height) / 2) + kAccessoryViewPadding; 174 [popupButton setFrameOrigin:NSMakePoint(popupOriginX, popupOriginY)]; 175 176 [accessoryView addSubview:textField]; 177 [accessoryView addSubview:popupButton]; 178 return accessoryView; 179 180 NS_OBJC_END_TRY_BLOCK_RETURN(nil); 181} 182 183// Display the file dialog 184nsresult nsFilePicker::Show(int16_t* retval) { 185 NS_ENSURE_ARG_POINTER(retval); 186 187 *retval = returnCancel; 188 189 int16_t userClicksOK = returnCancel; 190 191 mFiles.Clear(); 192 nsCOMPtr<nsIFile> theFile; 193 194 // Note that GetLocalFolder shares a lot of code with GetLocalFiles. 195 // Could combine the functions and just pass the mode in. 196 switch (mMode) { 197 case modeOpen: 198 userClicksOK = GetLocalFiles(false, mFiles); 199 break; 200 201 case modeOpenMultiple: 202 userClicksOK = GetLocalFiles(true, mFiles); 203 break; 204 205 case modeSave: 206 userClicksOK = PutLocalFile(getter_AddRefs(theFile)); 207 break; 208 209 case modeGetFolder: 210 userClicksOK = GetLocalFolder(getter_AddRefs(theFile)); 211 break; 212 213 default: 214 NS_ERROR("Unknown file picker mode"); 215 break; 216 } 217 218 if (theFile) mFiles.AppendObject(theFile); 219 220 *retval = userClicksOK; 221 return NS_OK; 222} 223 224static void UpdatePanelFileTypes(NSOpenPanel* aPanel, NSArray* aFilters) { 225 // If we show all file types, also "expose" bundles' contents. 226 [aPanel setTreatsFilePackagesAsDirectories:!aFilters]; 227 228 [aPanel setAllowedFileTypes:aFilters]; 229} 230 231@implementation NSPopUpButtonObserver 232- (void)setPopUpButton:(NSPopUpButton*)aPopUpButton { 233 mPopUpButton = aPopUpButton; 234} 235 236- (void)setOpenPanel:(NSOpenPanel*)aOpenPanel { 237 mOpenPanel = aOpenPanel; 238} 239 240- (void)setFilePicker:(nsFilePicker*)aFilePicker { 241 mFilePicker = aFilePicker; 242} 243 244- (void)menuChangedItem:(NSNotification*)aSender { 245 NS_OBJC_BEGIN_TRY_BLOCK_RETURN; 246 int32_t selectedItem = [mPopUpButton indexOfSelectedItem]; 247 if (selectedItem < 0) { 248 return; 249 } 250 251 mFilePicker->SetFilterIndex(selectedItem); 252 UpdatePanelFileTypes(mOpenPanel, mFilePicker->GetFilterList()); 253 254 NS_OBJC_END_TRY_BLOCK_RETURN(); 255} 256@end 257 258// Use OpenPanel to do a GetFile. Returns |returnOK| if the user presses OK in the dialog. 259int16_t nsFilePicker::GetLocalFiles(bool inAllowMultiple, nsCOMArray<nsIFile>& outFiles) { 260 NS_OBJC_BEGIN_TRY_BLOCK_RETURN; 261 262 int16_t retVal = (int16_t)returnCancel; 263 NSOpenPanel* thePanel = [NSOpenPanel openPanel]; 264 265 SetShowHiddenFileState(thePanel); 266 267 // Set the options for how the get file dialog will appear 268 SetDialogTitle(mTitle, thePanel); 269 [thePanel setAllowsMultipleSelection:inAllowMultiple]; 270 [thePanel setCanSelectHiddenExtension:YES]; 271 [thePanel setCanChooseDirectories:NO]; 272 [thePanel setCanChooseFiles:YES]; 273 [thePanel setResolvesAliases:YES]; 274 275 // Get filters 276 // filters may be null, if we should allow all file types. 277 NSArray* filters = GetFilterList(); 278 279 // set up default directory 280 NSString* theDir = PanelDefaultDirectory(); 281 282 // if this is the "Choose application..." dialog, and no other start 283 // dir has been set, then use the Applications folder. 284 if (!theDir) { 285 if (filters && [filters count] == 1 && 286 [(NSString*)[filters objectAtIndex:0] isEqualToString:@"app"]) 287 theDir = @"/Applications/"; 288 else 289 theDir = @""; 290 } 291 292 if (theDir) { 293 [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; 294 } 295 296 int result; 297 nsCocoaUtils::PrepareForNativeAppModalDialog(); 298 if (mFilters.Length() > 1) { 299 // [NSURL initWithString:] (below) throws an exception if URLString is nil. 300 301 NSPopUpButtonObserver* observer = [[NSPopUpButtonObserver alloc] init]; 302 303 NSView* accessoryView = GetAccessoryView(); 304 [thePanel setAccessoryView:accessoryView]; 305 306 [observer setPopUpButton:[accessoryView viewWithTag:kSaveTypeControlTag]]; 307 [observer setOpenPanel:thePanel]; 308 [observer setFilePicker:this]; 309 310 [[NSNotificationCenter defaultCenter] addObserver:observer 311 selector:@selector(menuChangedItem:) 312 name:NSMenuWillSendActionNotification 313 object:nil]; 314 315 UpdatePanelFileTypes(thePanel, filters); 316 result = [thePanel runModal]; 317 318 [[NSNotificationCenter defaultCenter] removeObserver:observer]; 319 [observer release]; 320 } else { 321 // If we show all file types, also "expose" bundles' contents. 322 if (!filters) { 323 [thePanel setTreatsFilePackagesAsDirectories:YES]; 324 } 325 [thePanel setAllowedFileTypes:filters]; 326 result = [thePanel runModal]; 327 } 328 nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); 329 330 if (result == NSFileHandlingPanelCancelButton) return retVal; 331 332 // Converts data from a NSArray of NSURL to the returned format. 333 // We should be careful to not call [thePanel URLs] more than once given that 334 // it creates a new array each time. 335 // We are using Fast Enumeration, thus the NSURL array is created once then 336 // iterated. 337 for (NSURL* url in [thePanel URLs]) { 338 if (!url) { 339 continue; 340 } 341 342 nsCOMPtr<nsIFile> localFile; 343 NS_NewLocalFile(u""_ns, true, getter_AddRefs(localFile)); 344 nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile); 345 if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)url))) { 346 outFiles.AppendObject(localFile); 347 } 348 } 349 350 if (outFiles.Count() > 0) retVal = returnOK; 351 352 return retVal; 353 354 NS_OBJC_END_TRY_BLOCK_RETURN(0); 355} 356 357// Use OpenPanel to do a GetFolder. Returns |returnOK| if the user presses OK in the dialog. 358int16_t nsFilePicker::GetLocalFolder(nsIFile** outFile) { 359 NS_OBJC_BEGIN_TRY_BLOCK_RETURN; 360 NS_ASSERTION(outFile, "this protected member function expects a null initialized out pointer"); 361 362 int16_t retVal = (int16_t)returnCancel; 363 NSOpenPanel* thePanel = [NSOpenPanel openPanel]; 364 365 SetShowHiddenFileState(thePanel); 366 367 // Set the options for how the get file dialog will appear 368 SetDialogTitle(mTitle, thePanel); 369 [thePanel setAllowsMultipleSelection:NO]; 370 [thePanel setCanSelectHiddenExtension:YES]; 371 [thePanel setCanChooseDirectories:YES]; 372 [thePanel setCanChooseFiles:NO]; 373 [thePanel setResolvesAliases:YES]; 374 [thePanel setCanCreateDirectories:YES]; 375 376 // packages != folders 377 [thePanel setTreatsFilePackagesAsDirectories:NO]; 378 379 // set up default directory 380 NSString* theDir = PanelDefaultDirectory(); 381 if (theDir) { 382 [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; 383 } 384 nsCocoaUtils::PrepareForNativeAppModalDialog(); 385 int result = [thePanel runModal]; 386 nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); 387 388 if (result == NSFileHandlingPanelCancelButton) return retVal; 389 390 // get the path for the folder (we allow just 1, so that's all we get) 391 NSURL* theURL = [[thePanel URLs] objectAtIndex:0]; 392 if (theURL) { 393 nsCOMPtr<nsIFile> localFile; 394 NS_NewLocalFile(u""_ns, true, getter_AddRefs(localFile)); 395 nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile); 396 if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)theURL))) { 397 *outFile = localFile; 398 NS_ADDREF(*outFile); 399 retVal = returnOK; 400 } 401 } 402 403 return retVal; 404 405 NS_OBJC_END_TRY_BLOCK_RETURN(0); 406} 407 408// Returns |returnOK| if the user presses OK in the dialog. 409int16_t nsFilePicker::PutLocalFile(nsIFile** outFile) { 410 NS_OBJC_BEGIN_TRY_BLOCK_RETURN; 411 NS_ASSERTION(outFile, "this protected member function expects a null initialized out pointer"); 412 413 int16_t retVal = returnCancel; 414 NSSavePanel* thePanel = [NSSavePanel savePanel]; 415 416 SetShowHiddenFileState(thePanel); 417 418 SetDialogTitle(mTitle, thePanel); 419 420 // set up accessory view for file format options 421 NSView* accessoryView = GetAccessoryView(); 422 [thePanel setAccessoryView:accessoryView]; 423 424 // set up default file name 425 NSString* defaultFilename = [NSString stringWithCharacters:(const unichar*)mDefaultFilename.get() 426 length:mDefaultFilename.Length()]; 427 428 // Set up the allowed type. This prevents the extension from being selected. 429 NSString* extension = defaultFilename.pathExtension; 430 if (extension.length != 0) { 431 thePanel.allowedFileTypes = @[ extension ]; 432 } 433 // Allow users to change the extension. 434 thePanel.allowsOtherFileTypes = YES; 435 436 // If extensions are hidden and we’re saving a file with multiple extensions, 437 // only the last extension will be hidden in the panel (".tar.gz" will become 438 // ".tar"). If the remaining extension is known, the OS will think that we're 439 // trying to add a non-default extension. To avoid the confusion, we ensure 440 // that all extensions are shown in the panel if the remaining extension is 441 // known by the OS. 442 NSString* fileName = [[defaultFilename lastPathComponent] stringByDeletingPathExtension]; 443 NSString* otherExtension = fileName.pathExtension; 444 if (otherExtension.length != 0) { 445 // There's another extension here. Get the UTI. 446 CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, 447 (CFStringRef)otherExtension, NULL); 448 if (type) { 449 if (!CFStringHasPrefix(type, CFSTR("dyn."))) { 450 // We have a UTI, otherwise the type would have a "dyn." prefix. Ensure 451 // extensions are shown in the panel. 452 [thePanel setExtensionHidden:NO]; 453 } 454 CFRelease(type); 455 } 456 } 457 458 // set up default directory 459 NSString* theDir = PanelDefaultDirectory(); 460 if (theDir) { 461 [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; 462 } 463 464 // load the panel 465 nsCocoaUtils::PrepareForNativeAppModalDialog(); 466 [thePanel setNameFieldStringValue:defaultFilename]; 467 int result = [thePanel runModal]; 468 nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); 469 if (result == NSFileHandlingPanelCancelButton) return retVal; 470 471 // get the save type 472 NSPopUpButton* popupButton = [accessoryView viewWithTag:kSaveTypeControlTag]; 473 if (popupButton) { 474 mSelectedTypeIndex = [popupButton indexOfSelectedItem]; 475 } 476 477 NSURL* fileURL = [thePanel URL]; 478 if (fileURL) { 479 nsCOMPtr<nsIFile> localFile; 480 NS_NewLocalFile(u""_ns, true, getter_AddRefs(localFile)); 481 nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile); 482 if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)fileURL))) { 483 *outFile = localFile; 484 NS_ADDREF(*outFile); 485 // We tell if we are replacing or not by just looking to see if the file exists. 486 // The user could not have hit OK and not meant to replace the file. 487 if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) 488 retVal = returnReplace; 489 else 490 retVal = returnOK; 491 } 492 } 493 494 return retVal; 495 496 NS_OBJC_END_TRY_BLOCK_RETURN(0); 497} 498 499NSArray* nsFilePicker::GetFilterList() { 500 NS_OBJC_BEGIN_TRY_BLOCK_RETURN; 501 502 if (!mFilters.Length()) { 503 return nil; 504 } 505 506 if (mFilters.Length() <= (uint32_t)mSelectedTypeIndex) { 507 NS_WARNING("An out of range index has been selected. Using the first index instead."); 508 mSelectedTypeIndex = 0; 509 } 510 511 const nsString& filterWide = mFilters[mSelectedTypeIndex]; 512 if (!filterWide.Length()) { 513 return nil; 514 } 515 516 if (filterWide.Equals(u"*"_ns)) { 517 return nil; 518 } 519 520 // The extensions in filterWide are in the format "*.ext" but are expected 521 // in the format "ext" by NSOpenPanel. So we need to filter some characters. 522 NSMutableString* filterString = [[[NSMutableString alloc] 523 initWithString:[NSString 524 stringWithCharacters:reinterpret_cast<const unichar*>(filterWide.get()) 525 length:filterWide.Length()]] autorelease]; 526 NSCharacterSet* set = [NSCharacterSet characterSetWithCharactersInString:@". *"]; 527 NSRange range = [filterString rangeOfCharacterFromSet:set]; 528 while (range.length) { 529 [filterString replaceCharactersInRange:range withString:@""]; 530 range = [filterString rangeOfCharacterFromSet:set]; 531 } 532 533 return 534 [[[NSArray alloc] initWithArray:[filterString componentsSeparatedByString:@";"]] autorelease]; 535 536 NS_OBJC_END_TRY_BLOCK_RETURN(nil); 537} 538 539// Sets the dialog title to whatever it should be. If it fails, eh, 540// the OS will provide a sensible default. 541void nsFilePicker::SetDialogTitle(const nsString& inTitle, id aPanel) { 542 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; 543 544 [aPanel setTitle:[NSString stringWithCharacters:(const unichar*)inTitle.get() 545 length:inTitle.Length()]]; 546 547 if (!mOkButtonLabel.IsEmpty()) { 548 [aPanel setPrompt:[NSString stringWithCharacters:(const unichar*)mOkButtonLabel.get() 549 length:mOkButtonLabel.Length()]]; 550 } 551 552 NS_OBJC_END_TRY_IGNORE_BLOCK; 553} 554 555// Converts path from an nsIFile into a NSString path 556// If it fails, returns an empty string. 557NSString* nsFilePicker::PanelDefaultDirectory() { 558 NS_OBJC_BEGIN_TRY_BLOCK_RETURN; 559 560 NSString* directory = nil; 561 if (mDisplayDirectory) { 562 nsAutoString pathStr; 563 mDisplayDirectory->GetPath(pathStr); 564 directory = 565 [[[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(pathStr.get()) 566 length:pathStr.Length()] autorelease]; 567 } 568 return directory; 569 570 NS_OBJC_END_TRY_BLOCK_RETURN(nil); 571} 572 573NS_IMETHODIMP nsFilePicker::GetFile(nsIFile** aFile) { 574 NS_ENSURE_ARG_POINTER(aFile); 575 *aFile = nullptr; 576 577 // just return the first file 578 if (mFiles.Count() > 0) { 579 *aFile = mFiles.ObjectAt(0); 580 NS_IF_ADDREF(*aFile); 581 } 582 583 return NS_OK; 584} 585 586NS_IMETHODIMP nsFilePicker::GetFileURL(nsIURI** aFileURL) { 587 NS_ENSURE_ARG_POINTER(aFileURL); 588 *aFileURL = nullptr; 589 590 if (mFiles.Count() == 0) return NS_OK; 591 592 return NS_NewFileURI(aFileURL, mFiles.ObjectAt(0)); 593} 594 595NS_IMETHODIMP nsFilePicker::GetFiles(nsISimpleEnumerator** aFiles) { 596 return NS_NewArrayEnumerator(aFiles, mFiles, NS_GET_IID(nsIFile)); 597} 598 599NS_IMETHODIMP nsFilePicker::SetDefaultString(const nsAString& aString) { 600 mDefaultFilename = aString; 601 return NS_OK; 602} 603 604NS_IMETHODIMP nsFilePicker::GetDefaultString(nsAString& aString) { return NS_ERROR_FAILURE; } 605 606// The default extension to use for files 607NS_IMETHODIMP nsFilePicker::GetDefaultExtension(nsAString& aExtension) { 608 aExtension.Truncate(); 609 return NS_OK; 610} 611 612NS_IMETHODIMP nsFilePicker::SetDefaultExtension(const nsAString& aExtension) { return NS_OK; } 613 614// Append an entry to the filters array 615NS_IMETHODIMP 616nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter) { 617 // "..apps" has to be translated with native executable extensions. 618 if (aFilter.EqualsLiteral("..apps")) { 619 mFilters.AppendElement(u"*.app"_ns); 620 } else { 621 mFilters.AppendElement(aFilter); 622 } 623 mTitles.AppendElement(aTitle); 624 625 return NS_OK; 626} 627 628// Get the filter index - do we still need this? 629NS_IMETHODIMP nsFilePicker::GetFilterIndex(int32_t* aFilterIndex) { 630 *aFilterIndex = mSelectedTypeIndex; 631 return NS_OK; 632} 633 634// Set the filter index - do we still need this? 635NS_IMETHODIMP nsFilePicker::SetFilterIndex(int32_t aFilterIndex) { 636 mSelectedTypeIndex = aFilterIndex; 637 return NS_OK; 638} 639