Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/widget/gtk/nsFilePicker.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "mozilla/Types.h"
7
#include <sys/types.h>
8
#include <sys/stat.h>
9
#include <unistd.h>
10
11
#include <gtk/gtk.h>
12
13
#include "nsGtkUtils.h"
14
#include "nsIFileURL.h"
15
#include "nsIURI.h"
16
#include "nsIWidget.h"
17
#include "nsIFile.h"
18
#include "nsIStringBundle.h"
19
20
#include "nsArrayEnumerator.h"
21
#include "nsMemory.h"
22
#include "nsEnumeratorUtils.h"
23
#include "nsNetUtil.h"
24
#include "nsReadableUtils.h"
25
#include "mozcontainer.h"
26
27
#include "nsFilePicker.h"
28
29
using namespace mozilla;
30
31
0
#define MAX_PREVIEW_SIZE 180
32
// bug 1184009
33
0
#define MAX_PREVIEW_SOURCE_SIZE 4096
34
35
nsIFile *nsFilePicker::mPrevDisplayDirectory = nullptr;
36
37
void
38
nsFilePicker::Shutdown()
39
0
{
40
0
  NS_IF_RELEASE(mPrevDisplayDirectory);
41
0
}
42
43
static GtkFileChooserAction
44
GetGtkFileChooserAction(int16_t aMode)
45
0
{
46
0
  GtkFileChooserAction action;
47
0
48
0
  switch (aMode) {
49
0
    case nsIFilePicker::modeSave:
50
0
    action = GTK_FILE_CHOOSER_ACTION_SAVE;
51
0
    break;
52
0
53
0
    case nsIFilePicker::modeGetFolder:
54
0
    action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
55
0
    break;
56
0
57
0
    case nsIFilePicker::modeOpen:
58
0
    case nsIFilePicker::modeOpenMultiple:
59
0
    action = GTK_FILE_CHOOSER_ACTION_OPEN;
60
0
    break;
61
0
62
0
    default:
63
0
    NS_WARNING("Unknown nsIFilePicker mode");
64
0
    action = GTK_FILE_CHOOSER_ACTION_OPEN;
65
0
    break;
66
0
  }
67
0
68
0
  return action;
69
0
}
70
71
72
static void
73
UpdateFilePreviewWidget(GtkFileChooser *file_chooser,
74
                        gpointer preview_widget_voidptr)
75
0
{
76
0
  GtkImage *preview_widget = GTK_IMAGE(preview_widget_voidptr);
77
0
  char *image_filename = gtk_file_chooser_get_preview_filename(file_chooser);
78
0
  struct stat st_buf;
79
0
80
0
  if (!image_filename) {
81
0
    gtk_file_chooser_set_preview_widget_active(file_chooser, FALSE);
82
0
    return;
83
0
  }
84
0
85
0
  gint preview_width = 0;
86
0
  gint preview_height = 0;
87
0
  /* check type of file
88
0
   * if file is named pipe, Open is blocking which may lead to UI
89
0
   *  nonresponsiveness; if file is directory/socket, it also isn't
90
0
   *  likely to get preview */
91
0
  if (stat(image_filename, &st_buf) || (!S_ISREG(st_buf.st_mode))) {
92
0
    g_free(image_filename);
93
0
    gtk_file_chooser_set_preview_widget_active(file_chooser, FALSE);
94
0
    return; /* stat failed or file is not regular */
95
0
  }
96
0
97
0
  GdkPixbufFormat *preview_format = gdk_pixbuf_get_file_info(image_filename,
98
0
                                                             &preview_width,
99
0
                                                             &preview_height);
100
0
  if (!preview_format ||
101
0
      preview_width <= 0 || preview_height <= 0 ||
102
0
      preview_width > MAX_PREVIEW_SOURCE_SIZE ||
103
0
      preview_height > MAX_PREVIEW_SOURCE_SIZE) {
104
0
    g_free(image_filename);
105
0
    gtk_file_chooser_set_preview_widget_active(file_chooser, FALSE);
106
0
    return;
107
0
  }
108
0
109
0
  GdkPixbuf *preview_pixbuf = nullptr;
110
0
  // Only scale down images that are too big
111
0
  if (preview_width > MAX_PREVIEW_SIZE || preview_height > MAX_PREVIEW_SIZE) {
112
0
    preview_pixbuf = gdk_pixbuf_new_from_file_at_size(image_filename,
113
0
                                                      MAX_PREVIEW_SIZE,
114
0
                                                      MAX_PREVIEW_SIZE,
115
0
                                                      nullptr);
116
0
  }
117
0
  else {
118
0
    preview_pixbuf = gdk_pixbuf_new_from_file(image_filename, nullptr);
119
0
  }
120
0
121
0
  g_free(image_filename);
122
0
123
0
  if (!preview_pixbuf) {
124
0
    gtk_file_chooser_set_preview_widget_active(file_chooser, FALSE);
125
0
    return;
126
0
  }
127
0
128
0
  GdkPixbuf *preview_pixbuf_temp = preview_pixbuf;
129
0
  preview_pixbuf = gdk_pixbuf_apply_embedded_orientation(preview_pixbuf_temp);
130
0
  g_object_unref(preview_pixbuf_temp);
131
0
132
0
  // This is the easiest way to do center alignment without worrying about containers
133
0
  // Minimum 3px padding each side (hence the 6) just to make things nice
134
0
  gint x_padding = (MAX_PREVIEW_SIZE + 6 - gdk_pixbuf_get_width(preview_pixbuf)) / 2;
135
0
  gtk_misc_set_padding(GTK_MISC(preview_widget), x_padding, 0);
136
0
137
0
  gtk_image_set_from_pixbuf(preview_widget, preview_pixbuf);
138
0
  g_object_unref(preview_pixbuf);
139
0
  gtk_file_chooser_set_preview_widget_active(file_chooser, TRUE);
140
0
}
141
142
static nsAutoCString
143
0
MakeCaseInsensitiveShellGlob(const char* aPattern) {
144
0
  // aPattern is UTF8
145
0
  nsAutoCString result;
146
0
  unsigned int len = strlen(aPattern);
147
0
148
0
  for (unsigned int i = 0; i < len; i++) {
149
0
    if (!g_ascii_isalpha(aPattern[i])) {
150
0
      // non-ASCII characters will also trigger this path, so unicode
151
0
      // is safely handled albeit case-sensitively
152
0
      result.Append(aPattern[i]);
153
0
      continue;
154
0
    }
155
0
156
0
    // add the lowercase and uppercase version of a character to a bracket
157
0
    // match, so it matches either the lowercase or uppercase char.
158
0
    result.Append('[');
159
0
    result.Append(g_ascii_tolower(aPattern[i]));
160
0
    result.Append(g_ascii_toupper(aPattern[i]));
161
0
    result.Append(']');
162
0
163
0
  }
164
0
165
0
  return result;
166
0
}
167
168
NS_IMPL_ISUPPORTS(nsFilePicker, nsIFilePicker)
169
170
nsFilePicker::nsFilePicker()
171
  : mSelectedType(0)
172
  , mRunning(false)
173
  , mAllowURLs(false)
174
#ifdef MOZ_WIDGET_GTK
175
  , mFileChooserDelegate(nullptr)
176
#endif
177
0
{
178
0
}
179
180
nsFilePicker::~nsFilePicker()
181
0
{
182
0
}
183
184
void
185
ReadMultipleFiles(gpointer filename, gpointer array)
186
0
{
187
0
  nsCOMPtr<nsIFile> localfile;
188
0
  nsresult rv = NS_NewNativeLocalFile(nsDependentCString(static_cast<char*>(filename)),
189
0
                                      false,
190
0
                                      getter_AddRefs(localfile));
191
0
  if (NS_SUCCEEDED(rv)) {
192
0
    nsCOMArray<nsIFile>& files = *static_cast<nsCOMArray<nsIFile>*>(array);
193
0
    files.AppendObject(localfile);
194
0
  }
195
0
196
0
  g_free(filename);
197
0
}
198
199
void
200
nsFilePicker::ReadValuesFromFileChooser(GtkWidget *file_chooser)
201
0
{
202
0
  mFiles.Clear();
203
0
204
0
  if (mMode == nsIFilePicker::modeOpenMultiple) {
205
0
    mFileURL.Truncate();
206
0
207
0
    GSList *list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(file_chooser));
208
0
    g_slist_foreach(list, ReadMultipleFiles, static_cast<gpointer>(&mFiles));
209
0
    g_slist_free(list);
210
0
  } else {
211
0
    gchar *filename = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(file_chooser));
212
0
    mFileURL.Assign(filename);
213
0
    g_free(filename);
214
0
  }
215
0
216
0
  GtkFileFilter *filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(file_chooser));
217
0
  GSList *filter_list = gtk_file_chooser_list_filters(GTK_FILE_CHOOSER(file_chooser));
218
0
219
0
  mSelectedType = static_cast<int16_t>(g_slist_index(filter_list, filter));
220
0
  g_slist_free(filter_list);
221
0
222
0
  // Remember last used directory.
223
0
  nsCOMPtr<nsIFile> file;
224
0
  GetFile(getter_AddRefs(file));
225
0
  if (file) {
226
0
    nsCOMPtr<nsIFile> dir;
227
0
    file->GetParent(getter_AddRefs(dir));
228
0
    if (dir) {
229
0
      dir.swap(mPrevDisplayDirectory);
230
0
    }
231
0
  }
232
0
}
233
234
void
235
nsFilePicker::InitNative(nsIWidget *aParent,
236
                         const nsAString& aTitle)
237
0
{
238
0
  mParentWidget = aParent;
239
0
  mTitle.Assign(aTitle);
240
0
}
241
242
NS_IMETHODIMP
243
nsFilePicker::AppendFilters(int32_t aFilterMask)
244
0
{
245
0
  mAllowURLs = !!(aFilterMask & filterAllowURLs);
246
0
  return nsBaseFilePicker::AppendFilters(aFilterMask);
247
0
}
248
249
NS_IMETHODIMP
250
nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter)
251
0
{
252
0
  if (aFilter.EqualsLiteral("..apps")) {
253
0
    // No platform specific thing we can do here, really....
254
0
    return NS_OK;
255
0
  }
256
0
257
0
  nsAutoCString filter, name;
258
0
  CopyUTF16toUTF8(aFilter, filter);
259
0
  CopyUTF16toUTF8(aTitle, name);
260
0
261
0
  mFilters.AppendElement(filter);
262
0
  mFilterNames.AppendElement(name);
263
0
264
0
  return NS_OK;
265
0
}
266
267
NS_IMETHODIMP
268
nsFilePicker::SetDefaultString(const nsAString& aString)
269
0
{
270
0
  mDefault = aString;
271
0
272
0
  return NS_OK;
273
0
}
274
275
NS_IMETHODIMP
276
nsFilePicker::GetDefaultString(nsAString& aString)
277
0
{
278
0
  // Per API...
279
0
  return NS_ERROR_FAILURE;
280
0
}
281
282
NS_IMETHODIMP
283
nsFilePicker::SetDefaultExtension(const nsAString& aExtension)
284
0
{
285
0
  mDefaultExtension = aExtension;
286
0
287
0
  return NS_OK;
288
0
}
289
290
NS_IMETHODIMP
291
nsFilePicker::GetDefaultExtension(nsAString& aExtension)
292
0
{
293
0
  aExtension = mDefaultExtension;
294
0
295
0
  return NS_OK;
296
0
}
297
298
NS_IMETHODIMP
299
nsFilePicker::GetFilterIndex(int32_t *aFilterIndex)
300
0
{
301
0
  *aFilterIndex = mSelectedType;
302
0
303
0
  return NS_OK;
304
0
}
305
306
NS_IMETHODIMP
307
nsFilePicker::SetFilterIndex(int32_t aFilterIndex)
308
0
{
309
0
  mSelectedType = aFilterIndex;
310
0
311
0
  return NS_OK;
312
0
}
313
314
NS_IMETHODIMP
315
nsFilePicker::GetFile(nsIFile **aFile)
316
0
{
317
0
  NS_ENSURE_ARG_POINTER(aFile);
318
0
319
0
  *aFile = nullptr;
320
0
  nsCOMPtr<nsIURI> uri;
321
0
  nsresult rv = GetFileURL(getter_AddRefs(uri));
322
0
  if (!uri)
323
0
    return rv;
324
0
325
0
  nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(uri, &rv));
326
0
  NS_ENSURE_SUCCESS(rv, rv);
327
0
328
0
  nsCOMPtr<nsIFile> file;
329
0
  rv = fileURL->GetFile(getter_AddRefs(file));
330
0
  NS_ENSURE_SUCCESS(rv, rv);
331
0
332
0
  file.forget(aFile);
333
0
  return NS_OK;
334
0
}
335
336
NS_IMETHODIMP
337
nsFilePicker::GetFileURL(nsIURI **aFileURL)
338
0
{
339
0
  *aFileURL = nullptr;
340
0
  return NS_NewURI(aFileURL, mFileURL);
341
0
}
342
343
NS_IMETHODIMP
344
nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles)
345
0
{
346
0
  NS_ENSURE_ARG_POINTER(aFiles);
347
0
348
0
  if (mMode == nsIFilePicker::modeOpenMultiple) {
349
0
    return NS_NewArrayEnumerator(aFiles, mFiles, NS_GET_IID(nsIFile));
350
0
  }
351
0
352
0
  return NS_ERROR_FAILURE;
353
0
}
354
355
nsresult
356
nsFilePicker::Show(int16_t *aReturn)
357
0
{
358
0
  NS_ENSURE_ARG_POINTER(aReturn);
359
0
360
0
  nsresult rv = Open(nullptr);
361
0
  if (NS_FAILED(rv))
362
0
    return rv;
363
0
364
0
  while (mRunning) {
365
0
    g_main_context_iteration(nullptr, TRUE);
366
0
  }
367
0
368
0
  *aReturn = mResult;
369
0
  return NS_OK;
370
0
}
371
372
NS_IMETHODIMP
373
nsFilePicker::Open(nsIFilePickerShownCallback *aCallback)
374
0
{
375
0
  // Can't show two dialogs concurrently with the same filepicker
376
0
  if (mRunning)
377
0
    return NS_ERROR_NOT_AVAILABLE;
378
0
379
0
  nsCString title;
380
0
  title.Adopt(ToNewUTF8String(mTitle));
381
0
382
0
  GtkWindow *parent_widget =
383
0
    GTK_WINDOW(mParentWidget->GetNativeData(NS_NATIVE_SHELLWIDGET));
384
0
385
0
  GtkFileChooserAction action = GetGtkFileChooserAction(mMode);
386
0
387
0
  const gchar* accept_button;
388
0
  NS_ConvertUTF16toUTF8 buttonLabel(mOkButtonLabel);
389
0
  if (!mOkButtonLabel.IsEmpty()) {
390
0
    accept_button = buttonLabel.get();
391
0
  } else {
392
0
    accept_button = (action == GTK_FILE_CHOOSER_ACTION_SAVE) ?
393
0
                    GTK_STOCK_SAVE : GTK_STOCK_OPEN;
394
0
  }
395
0
396
0
  GtkWidget *file_chooser =
397
0
      gtk_file_chooser_dialog_new(title.get(), parent_widget, action,
398
0
                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
399
0
                                  accept_button, GTK_RESPONSE_ACCEPT,
400
0
                                  nullptr);
401
0
  gtk_dialog_set_alternative_button_order(GTK_DIALOG(file_chooser),
402
0
                                          GTK_RESPONSE_ACCEPT,
403
0
                                          GTK_RESPONSE_CANCEL,
404
0
                                          -1);
405
0
406
0
  // If we have --enable-proxy-bypass-protection, then don't allow
407
0
  // remote URLs to be used.
408
0
#ifndef MOZ_PROXY_BYPASS_PROTECTION
409
0
  if (mAllowURLs) {
410
0
    gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(file_chooser), FALSE);
411
0
  }
412
0
#endif
413
0
414
0
  if (action == GTK_FILE_CHOOSER_ACTION_OPEN || action == GTK_FILE_CHOOSER_ACTION_SAVE) {
415
0
    GtkWidget *img_preview = gtk_image_new();
416
0
    gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(file_chooser), img_preview);
417
0
    g_signal_connect(file_chooser, "update-preview", G_CALLBACK(UpdateFilePreviewWidget), img_preview);
418
0
  }
419
0
420
0
  GtkWindow *window = GTK_WINDOW(file_chooser);
421
0
  gtk_window_set_modal(window, TRUE);
422
0
  if (parent_widget) {
423
0
    gtk_window_set_destroy_with_parent(window, TRUE);
424
0
  }
425
0
426
0
  NS_ConvertUTF16toUTF8 defaultName(mDefault);
427
0
  switch (mMode) {
428
0
    case nsIFilePicker::modeOpenMultiple:
429
0
      gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(file_chooser), TRUE);
430
0
      break;
431
0
    case nsIFilePicker::modeSave:
432
0
      gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser),
433
0
                                        defaultName.get());
434
0
      break;
435
0
  }
436
0
437
0
  nsCOMPtr<nsIFile> defaultPath;
438
0
  if (mDisplayDirectory) {
439
0
    mDisplayDirectory->Clone(getter_AddRefs(defaultPath));
440
0
  } else if (mPrevDisplayDirectory) {
441
0
    mPrevDisplayDirectory->Clone(getter_AddRefs(defaultPath));
442
0
  }
443
0
444
0
  if (defaultPath) {
445
0
    if (!defaultName.IsEmpty() && mMode != nsIFilePicker::modeSave) {
446
0
      // Try to select the intended file. Even if it doesn't exist, GTK still switches
447
0
      // directories.
448
0
      defaultPath->AppendNative(defaultName);
449
0
      nsAutoCString path;
450
0
      defaultPath->GetNativePath(path);
451
0
      gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(file_chooser), path.get());
452
0
    } else {
453
0
      nsAutoCString directory;
454
0
      defaultPath->GetNativePath(directory);
455
0
456
0
#ifdef MOZ_WIDGET_GTK
457
0
      // Workaround for problematic refcounting in GTK3 before 3.16.
458
0
      // We need to keep a reference to the dialog's internal delegate.
459
0
      // Otherwise, if our dialog gets destroyed, we'll lose the dialog's
460
0
      // delegate by the time this gets processed in the event loop.
461
0
      // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1166741
462
0
      GtkDialog *dialog = GTK_DIALOG(file_chooser);
463
0
      GtkContainer *area = GTK_CONTAINER(gtk_dialog_get_content_area(dialog));
464
0
      gtk_container_forall(area, [](GtkWidget *widget,
465
0
                                    gpointer data) {
466
0
          if (GTK_IS_FILE_CHOOSER_WIDGET(widget)) {
467
0
            auto result = static_cast<GtkFileChooserWidget**>(data);
468
0
            *result = GTK_FILE_CHOOSER_WIDGET(widget);
469
0
          }
470
0
      }, &mFileChooserDelegate);
471
0
472
0
      if (mFileChooserDelegate)
473
0
        g_object_ref(mFileChooserDelegate);
474
0
#endif
475
0
476
0
      gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(file_chooser),
477
0
                                          directory.get());
478
0
    }
479
0
  }
480
0
481
0
  gtk_dialog_set_default_response(GTK_DIALOG(file_chooser), GTK_RESPONSE_ACCEPT);
482
0
483
0
  int32_t count = mFilters.Length();
484
0
  for (int32_t i = 0; i < count; ++i) {
485
0
    // This is fun... the GTK file picker does not accept a list of filters
486
0
    // so we need to split out each string, and add it manually.
487
0
488
0
    char **patterns = g_strsplit(mFilters[i].get(), ";", -1);
489
0
    if (!patterns) {
490
0
      return NS_ERROR_OUT_OF_MEMORY;
491
0
    }
492
0
493
0
    GtkFileFilter *filter = gtk_file_filter_new();
494
0
    for (int j = 0; patterns[j] != nullptr; ++j) {
495
0
      nsAutoCString caseInsensitiveFilter = MakeCaseInsensitiveShellGlob(g_strstrip(patterns[j]));
496
0
      gtk_file_filter_add_pattern(filter, caseInsensitiveFilter.get());
497
0
    }
498
0
499
0
    g_strfreev(patterns);
500
0
501
0
    if (!mFilterNames[i].IsEmpty()) {
502
0
      // If we have a name for our filter, let's use that.
503
0
      const char *filter_name = mFilterNames[i].get();
504
0
      gtk_file_filter_set_name(filter, filter_name);
505
0
    } else {
506
0
      // If we don't have a name, let's just use the filter pattern.
507
0
      const char *filter_pattern = mFilters[i].get();
508
0
      gtk_file_filter_set_name(filter, filter_pattern);
509
0
    }
510
0
511
0
    gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(file_chooser), filter);
512
0
513
0
    // Set the initially selected filter
514
0
    if (mSelectedType == i) {
515
0
      gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(file_chooser), filter);
516
0
    }
517
0
  }
518
0
519
0
  gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(file_chooser), TRUE);
520
0
521
0
  mRunning = true;
522
0
  mCallback = aCallback;
523
0
  NS_ADDREF_THIS();
524
0
  g_signal_connect(file_chooser, "response", G_CALLBACK(OnResponse), this);
525
0
  g_signal_connect(file_chooser, "destroy", G_CALLBACK(OnDestroy), this);
526
0
  gtk_widget_show(file_chooser);
527
0
528
0
  return NS_OK;
529
0
}
530
531
/* static */ void
532
nsFilePicker::OnResponse(GtkWidget* file_chooser, gint response_id,
533
                         gpointer user_data)
534
0
{
535
0
  static_cast<nsFilePicker*>(user_data)->
536
0
    Done(file_chooser, response_id);
537
0
}
538
539
/* static */ void
540
nsFilePicker::OnDestroy(GtkWidget* file_chooser, gpointer user_data)
541
0
{
542
0
  static_cast<nsFilePicker*>(user_data)->
543
0
    Done(file_chooser, GTK_RESPONSE_CANCEL);
544
0
}
545
546
void
547
nsFilePicker::Done(GtkWidget* file_chooser, gint response)
548
0
{
549
0
  mRunning = false;
550
0
551
0
  int16_t result;
552
0
  switch (response) {
553
0
    case GTK_RESPONSE_OK:
554
0
    case GTK_RESPONSE_ACCEPT:
555
0
    ReadValuesFromFileChooser(file_chooser);
556
0
    result = nsIFilePicker::returnOK;
557
0
    if (mMode == nsIFilePicker::modeSave) {
558
0
      nsCOMPtr<nsIFile> file;
559
0
      GetFile(getter_AddRefs(file));
560
0
      if (file) {
561
0
        bool exists = false;
562
0
        file->Exists(&exists);
563
0
        if (exists)
564
0
          result = nsIFilePicker::returnReplace;
565
0
      }
566
0
    }
567
0
    break;
568
0
569
0
    case GTK_RESPONSE_CANCEL:
570
0
    case GTK_RESPONSE_CLOSE:
571
0
    case GTK_RESPONSE_DELETE_EVENT:
572
0
    result = nsIFilePicker::returnCancel;
573
0
    break;
574
0
575
0
    default:
576
0
    NS_WARNING("Unexpected response");
577
0
    result = nsIFilePicker::returnCancel;
578
0
    break;
579
0
  }
580
0
581
0
  // A "response" signal won't be sent again but "destroy" will be.
582
0
  g_signal_handlers_disconnect_by_func(file_chooser,
583
0
                                       FuncToGpointer(OnDestroy), this);
584
0
585
0
  // When response_id is GTK_RESPONSE_DELETE_EVENT or when called from
586
0
  // OnDestroy, the widget would be destroyed anyway but it is fine if
587
0
  // gtk_widget_destroy is called more than once.  gtk_widget_destroy has
588
0
  // requests that any remaining references be released, but the reference
589
0
  // count will not be decremented again if GtkWindow's reference has already
590
0
  // been released.
591
0
  gtk_widget_destroy(file_chooser);
592
0
593
0
#ifdef MOZ_WIDGET_GTK
594
0
      if (mFileChooserDelegate) {
595
0
        // Properly deref our acquired reference. We call this after
596
0
        // gtk_widget_destroy() to try and ensure that pending file info
597
0
        // queries caused by updating the current folder have been cancelled.
598
0
        // However, we do not know for certain when the callback will run after
599
0
        // cancelled.
600
0
        g_idle_add([](gpointer data) -> gboolean {
601
0
            g_object_unref(data);
602
0
            return G_SOURCE_REMOVE;
603
0
        }, mFileChooserDelegate);
604
0
        mFileChooserDelegate = nullptr;
605
0
      }
606
0
#endif
607
0
608
0
  if (mCallback) {
609
0
    mCallback->Done(result);
610
0
    mCallback = nullptr;
611
0
  } else {
612
0
    mResult = result;
613
0
  }
614
0
  NS_RELEASE_THIS();
615
0
}