LCOV - code coverage report
Current view: top level - widget/gtk - nsFilePicker.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 289 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 35 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13