summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Horak <jhorak@redhat.com>2015-06-08 14:00:59 +0200
committerJan Horak <jhorak@redhat.com>2015-06-08 14:00:59 +0200
commitf52480c71b0f7f067caabc4a7e32adc903f69fe4 (patch)
tree1fc201cc61e8aceef29922c3119ce1983ee31ca5
parentDisabled system colors by default (rhbz#1226489) (diff)
downloadlibrewolf-fedora-ff-f52480c71b0f7f067caabc4a7e32adc903f69fe4.tar.gz
librewolf-fedora-ff-f52480c71b0f7f067caabc4a7e32adc903f69fe4.tar.bz2
librewolf-fedora-ff-f52480c71b0f7f067caabc4a7e32adc903f69fe4.zip
Added application picker patch
-rw-r--r--firefox.spec8
-rw-r--r--mozilla-1129873-apppicker.patch553
2 files changed, 560 insertions, 1 deletions
diff --git a/firefox.spec b/firefox.spec
index 1a3c639..96db593 100644
--- a/firefox.spec
+++ b/firefox.spec
@@ -107,7 +107,7 @@
Summary: Mozilla Firefox Web browser
Name: firefox
Version: 38.0.5
-Release: 2%{?pre_tag}%{?dist}
+Release: 3%{?pre_tag}%{?dist}
URL: http://www.mozilla.org/projects/firefox/
License: MPLv1.1 or GPLv2+ or LGPLv2+
Group: Applications/Internet
@@ -161,10 +161,12 @@ Patch419: mozilla-1144745-3.patch
Patch420: mozilla-1160154.patch
Patch421: mozilla-1169233.patch
Patch422: mozilla-1169232.patch
+Patch423: mozilla-1129873-apppicker.patch
# Fix Skia Neon stuff on AArch64
Patch500: aarch64-fix-skia.patch
+
%if %{official_branding}
# Required by Mozilla Corporation
@@ -323,6 +325,7 @@ cd %{tarballdir}
%patch420 -p1 -b .1160154
%patch421 -p1 -b .1169233
%patch422 -p1 -b .1169232
+%patch423 -p1 -b .1129873-apppicker
%endif
%patch500 -p1
@@ -814,6 +817,9 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
#---------------------------------------------------------------------
%changelog
+* Mon Jun 8 2015 Jan Horak <jhorak@redhat.com> - 38.0.5-3
+- System application picker dialog (mozbz#1129873)
+
* Fri Jun 5 2015 Martin Stransky <stransky@redhat.com> - 38.0.5-2
- Disabled system colors by default (rhbz#1226489)
diff --git a/mozilla-1129873-apppicker.patch b/mozilla-1129873-apppicker.patch
new file mode 100644
index 0000000..81d9e6b
--- /dev/null
+++ b/mozilla-1129873-apppicker.patch
@@ -0,0 +1,553 @@
+# HG changeset patch
+# Parent 58c9d079f31811f3f325d4f439084a9ceb36764b
+# User Jan Horak <jhorak@redhat.com>
+# Bug 1129873 - Implementation of GtkAppChooserDialog wrapper for GTK3 to enable native application chooser in Linux
+# Parent 7d4ab4a9febdf66c18d752afd4bd241c41be921f
+try: -b do -p all -u all -t none
+
+diff --git a/toolkit/mozapps/downloads/nsHelperAppDlg.js b/toolkit/mozapps/downloads/nsHelperAppDlg.js
+--- a/toolkit/mozapps/downloads/nsHelperAppDlg.js
++++ b/toolkit/mozapps/downloads/nsHelperAppDlg.js
+@@ -999,16 +999,44 @@ nsUnknownContentTypeDialog.prototype = {
+ try {
+ return file.bundleDisplayName;
+ } catch (e) {}
+ }
+ #endif
+ return file.leafName;
+ },
+
++ finishChooseApp: function() {
++ if (this.chosenApp) {
++ // Show the "handler" menulist since we have a (user-specified)
++ // application now.
++ this.dialogElement("modeDeck").setAttribute("selectedIndex", "0");
++
++ // Update dialog.
++ var otherHandler = this.dialogElement("otherHandler");
++ otherHandler.removeAttribute("hidden");
++ otherHandler.setAttribute("path", this.getPath(this.chosenApp.executable));
++#ifdef XP_WIN
++ otherHandler.label = this.getFileDisplayName(this.chosenApp.executable);
++#else
++ otherHandler.label = this.chosenApp.name;
++#endif
++ this.dialogElement("openHandler").selectedIndex = 1;
++ this.dialogElement("openHandler").setAttribute("lastSelectedItemID", "otherHandler");
++
++ this.dialogElement("mode").selectedItem = this.dialogElement("open");
++ }
++ else {
++ var openHandler = this.dialogElement("openHandler");
++ var lastSelectedID = openHandler.getAttribute("lastSelectedItemID");
++ if (!lastSelectedID)
++ lastSelectedID = "defaultHandler";
++ openHandler.selectedItem = this.dialogElement(lastSelectedID);
++ }
++ },
+ // chooseApp: Open file picker and prompt user for application.
+ chooseApp: function() {
+ #ifdef XP_WIN
+ // Protect against the lack of an extension
+ var fileExtension = "";
+ try {
+ fileExtension = this.mLauncher.MIMEInfo.primaryExtension;
+ } catch(ex) {
+@@ -1042,17 +1070,33 @@ nsUnknownContentTypeDialog.prototype = {
+ "chrome,modal,centerscreen,titlebar,dialog=yes",
+ params);
+
+ if (params.handlerApp &&
+ params.handlerApp.executable &&
+ params.handlerApp.executable.isFile()) {
+ // Remember the file they chose to run.
+ this.chosenApp = params.handlerApp;
+-
++ }
++#else
++#if MOZ_WIDGET_GTK == 3
++ var nsIApplicationChooser = Components.interfaces.nsIApplicationChooser;
++ var appChooser = Components.classes["@mozilla.org/applicationchooser;1"]
++ .createInstance(nsIApplicationChooser);
++ appChooser.init(this.mDialog, this.dialogElement("strings").getString("chooseAppFilePickerTitle"));
++ var contentTypeDialogObj = this;
++ let appChooserCallback = function appChooserCallback_done(aResult) {
++ if (aResult) {
++ contentTypeDialogObj.chosenApp = aResult.QueryInterface(Components.interfaces.nsILocalHandlerApp);
++ }
++ contentTypeDialogObj.finishChooseApp();
++ };
++ appChooser.open(this.mLauncher.MIMEInfo.MIMEType, appChooserCallback);
++ // The finishChooseApp is called from appChooserCallback
++ return;
+ #else
+ var nsIFilePicker = Components.interfaces.nsIFilePicker;
+ var fp = Components.classes["@mozilla.org/filepicker;1"]
+ .createInstance(nsIFilePicker);
+ fp.init(this.mDialog,
+ this.dialogElement("strings").getString("chooseAppFilePickerTitle"),
+ nsIFilePicker.modeOpen);
+
+@@ -1060,39 +1104,21 @@ nsUnknownContentTypeDialog.prototype = {
+
+ if (fp.show() == nsIFilePicker.returnOK && fp.file) {
+ // Remember the file they chose to run.
+ var localHandlerApp =
+ Components.classes["@mozilla.org/uriloader/local-handler-app;1"].
+ createInstance(Components.interfaces.nsILocalHandlerApp);
+ localHandlerApp.executable = fp.file;
+ this.chosenApp = localHandlerApp;
+-#endif
++ }
++#endif // MOZ_WIDGET_GTK3
+
+- // Show the "handler" menulist since we have a (user-specified)
+- // application now.
+- this.dialogElement("modeDeck").setAttribute("selectedIndex", "0");
+-
+- // Update dialog.
+- var otherHandler = this.dialogElement("otherHandler");
+- otherHandler.removeAttribute("hidden");
+- otherHandler.setAttribute("path", this.getPath(this.chosenApp.executable));
+- otherHandler.label = this.getFileDisplayName(this.chosenApp.executable);
+- this.dialogElement("openHandler").selectedIndex = 1;
+- this.dialogElement("openHandler").setAttribute("lastSelectedItemID", "otherHandler");
+-
+- this.dialogElement("mode").selectedItem = this.dialogElement("open");
+- }
+- else {
+- var openHandler = this.dialogElement("openHandler");
+- var lastSelectedID = openHandler.getAttribute("lastSelectedItemID");
+- if (!lastSelectedID)
+- lastSelectedID = "defaultHandler";
+- openHandler.selectedItem = this.dialogElement(lastSelectedID);
+- }
++#endif // XP_WIN
++ this.finishChooseApp();
+ },
+
+ // Turn this on to get debugging messages.
+ debug: false,
+
+ // Dump text (if debug is on).
+ dump: function( text ) {
+ if ( this.debug ) {
+diff --git a/widget/gtk/moz.build b/widget/gtk/moz.build
+--- a/widget/gtk/moz.build
++++ b/widget/gtk/moz.build
+@@ -69,16 +69,17 @@ if CONFIG['ACCESSIBILITY']:
+
+ if CONFIG['MOZ_ENABLE_GTK2']:
+ UNIFIED_SOURCES += [
+ 'gtk2drawing.c',
+ ]
+ else:
+ UNIFIED_SOURCES += [
+ 'gtk3drawing.c',
++ 'nsApplicationChooser.cpp',
+ ]
+
+ include('/ipc/chromium/chromium-config.mozbuild')
+
+ FINAL_LIBRARY = 'xul'
+
+ LOCAL_INCLUDES += [
+ '/layout/generic',
+diff --git a/widget/gtk/mozgtk/mozgtk.c b/widget/gtk/mozgtk/mozgtk.c
+--- a/widget/gtk/mozgtk/mozgtk.c
++++ b/widget/gtk/mozgtk/mozgtk.c
+@@ -535,16 +535,21 @@ STUB(gtk_style_context_save)
+ STUB(gtk_style_context_set_path)
+ STUB(gtk_style_context_set_state)
+ STUB(gtk_tree_view_column_get_button)
+ STUB(gtk_widget_get_preferred_size)
+ STUB(gtk_widget_get_style_context)
+ STUB(gtk_widget_path_append_type)
+ STUB(gtk_widget_path_new)
+ STUB(gtk_widget_set_visual)
++STUB(gtk_app_chooser_dialog_new_for_content_type)
++STUB(gtk_app_chooser_get_type)
++STUB(gtk_app_chooser_get_app_info)
++STUB(gtk_app_chooser_dialog_get_type)
++STUB(gtk_app_chooser_dialog_set_heading)
+ #endif
+
+ #ifdef GTK2_SYMBOLS
+ STUB(gdk_drawable_get_screen)
+ STUB(gdk_rgb_get_colormap)
+ STUB(gdk_rgb_get_visual)
+ STUB(gdk_window_lookup)
+ STUB(gdk_window_set_back_pixmap)
+diff --git a/widget/gtk/nsApplicationChooser.cpp b/widget/gtk/nsApplicationChooser.cpp
+new file mode 100644
+--- /dev/null
++++ b/widget/gtk/nsApplicationChooser.cpp
+@@ -0,0 +1,123 @@
++/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++#include "mozilla/Types.h"
++
++#include <gtk/gtk.h>
++
++#include "nsApplicationChooser.h"
++#include "WidgetUtils.h"
++#include "nsIMIMEInfo.h"
++#include "nsCExternalHandlerService.h"
++#include "nsGtkUtils.h"
++
++using namespace mozilla;
++
++NS_IMPL_ISUPPORTS(nsApplicationChooser, nsIApplicationChooser)
++
++nsApplicationChooser::nsApplicationChooser()
++{
++}
++
++nsApplicationChooser::~nsApplicationChooser()
++{
++}
++
++NS_IMETHODIMP
++nsApplicationChooser::Init(nsIDOMWindow* aParent, const nsACString& aTitle)
++{
++ NS_ENSURE_TRUE(aParent, NS_ERROR_FAILURE);
++ mParentWidget = widget::WidgetUtils::DOMWindowToWidget(aParent);
++ mWindowTitle.Assign(aTitle);
++ return NS_OK;
++}
++
++NS_IMETHODIMP
++nsApplicationChooser::Open(const nsACString& aContentType, nsIApplicationChooserFinishedCallback *aCallback)
++{
++ MOZ_ASSERT(aCallback);
++ if (mCallback) {
++ NS_WARNING("Chooser is already in progress.");
++ return NS_ERROR_ALREADY_INITIALIZED;
++ }
++ mCallback = aCallback;
++ NS_ENSURE_TRUE(mParentWidget, NS_ERROR_FAILURE);
++ GtkWindow *parent_widget =
++ GTK_WINDOW(mParentWidget->GetNativeData(NS_NATIVE_SHELLWIDGET));
++
++ GtkWidget* chooser =
++ gtk_app_chooser_dialog_new_for_content_type(parent_widget,
++ (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
++ PromiseFlatCString(aContentType).get());
++ gtk_app_chooser_dialog_set_heading(GTK_APP_CHOOSER_DIALOG(chooser), mWindowTitle.BeginReading());
++ NS_ADDREF_THIS();
++ g_signal_connect(chooser, "response", G_CALLBACK(OnResponse), this);
++ g_signal_connect(chooser, "destroy", G_CALLBACK(OnDestroy), this);
++ gtk_widget_show(chooser);
++ return NS_OK;
++}
++
++/* static */ void
++nsApplicationChooser::OnResponse(GtkWidget* chooser, gint response_id, gpointer user_data)
++{
++ static_cast<nsApplicationChooser*>(user_data)->Done(chooser, response_id);
++}
++
++/* static */ void
++nsApplicationChooser::OnDestroy(GtkWidget *chooser, gpointer user_data)
++{
++ static_cast<nsApplicationChooser*>(user_data)->Done(chooser, GTK_RESPONSE_CANCEL);
++}
++
++void nsApplicationChooser::Done(GtkWidget* chooser, gint response)
++{
++ nsCOMPtr<nsILocalHandlerApp> localHandler;
++ nsresult rv;
++ switch (response) {
++ case GTK_RESPONSE_OK:
++ case GTK_RESPONSE_ACCEPT:
++ {
++ localHandler = do_CreateInstance(NS_LOCALHANDLERAPP_CONTRACTID, &rv);
++ if (NS_FAILED(rv)) {
++ NS_WARNING("Out of memory.");
++ break;
++ }
++ GAppInfo *app_info = gtk_app_chooser_get_app_info(GTK_APP_CHOOSER(chooser));
++
++ nsCOMPtr<nsIFile> localExecutable;
++ gchar *fileWithFullPath = g_find_program_in_path(g_app_info_get_executable(app_info));
++ rv = NS_NewNativeLocalFile(nsDependentCString(fileWithFullPath), false, getter_AddRefs(localExecutable));
++ g_free(fileWithFullPath);
++ if (NS_FAILED(rv)) {
++ NS_WARNING("Cannot create local filename.");
++ localHandler = nullptr;
++ } else {
++ localHandler->SetExecutable(localExecutable);
++ localHandler->SetName(NS_ConvertUTF8toUTF16(g_app_info_get_display_name(app_info)));
++ }
++ g_object_unref(app_info);
++ }
++
++ break;
++ case GTK_RESPONSE_CANCEL:
++ case GTK_RESPONSE_CLOSE:
++ case GTK_RESPONSE_DELETE_EVENT:
++ break;
++ default:
++ NS_WARNING("Unexpected response");
++ break;
++ }
++
++ // A "response" signal won't be sent again but "destroy" will be.
++ g_signal_handlers_disconnect_by_func(chooser, FuncToGpointer(OnDestroy), this);
++ gtk_widget_destroy(chooser);
++
++ if (mCallback) {
++ mCallback->Done(localHandler);
++ mCallback = nullptr;
++ }
++ NS_RELEASE_THIS();
++}
++
+diff --git a/widget/gtk/nsApplicationChooser.h b/widget/gtk/nsApplicationChooser.h
+new file mode 100644
+--- /dev/null
++++ b/widget/gtk/nsApplicationChooser.h
+@@ -0,0 +1,28 @@
++/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++#ifndef nsApplicationChooser_h__
++#define nsApplicationChooser_h__
++
++#include <gtk/gtk.h>
++#include "nsIApplicationChooser.h"
++
++class nsApplicationChooser : public nsIApplicationChooser
++{
++public:
++ nsApplicationChooser();
++ NS_DECL_ISUPPORTS
++ NS_DECL_NSIAPPLICATIONCHOOSER
++ void Done(GtkWidget* chooser, gint response);
++
++private:
++ ~nsApplicationChooser();
++ nsCOMPtr<nsIWidget> mParentWidget;
++ nsCString mWindowTitle;
++ nsCOMPtr<nsIApplicationChooserFinishedCallback> mCallback;
++ static void OnResponse(GtkWidget* chooser, gint response_id, gpointer user_data);
++ static void OnDestroy(GtkWidget* chooser, gpointer user_data);
++};
++#endif
+diff --git a/widget/gtk/nsWidgetFactory.cpp b/widget/gtk/nsWidgetFactory.cpp
+--- a/widget/gtk/nsWidgetFactory.cpp
++++ b/widget/gtk/nsWidgetFactory.cpp
+@@ -16,16 +16,19 @@
+ #include "nsWindow.h"
+ #include "nsTransferable.h"
+ #include "nsHTMLFormatConverter.h"
+ #ifdef MOZ_X11
+ #include "nsClipboardHelper.h"
+ #include "nsClipboard.h"
+ #include "nsDragService.h"
+ #endif
++#if (MOZ_WIDGET_GTK == 3)
++#include "nsApplicationChooser.h"
++#endif
+ #include "nsColorPicker.h"
+ #include "nsFilePicker.h"
+ #include "nsSound.h"
+ #include "nsBidiKeyboard.h"
+ #include "nsScreenManagerGtk.h"
+ #include "nsGTKToolkit.h"
+ #include "WakeLockListener.h"
+
+@@ -147,16 +150,35 @@ nsFilePickerConstructor(nsISupports *aOu
+
+ if (!picker) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return picker->QueryInterface(aIID, aResult);
+ }
+
++#if (MOZ_WIDGET_GTK == 3)
++static nsresult
++nsApplicationChooserConstructor(nsISupports *aOuter, REFNSIID aIID,
++ void **aResult)
++{
++ *aResult = nullptr;
++ if (aOuter != nullptr) {
++ return NS_ERROR_NO_AGGREGATION;
++ }
++ nsCOMPtr<nsIApplicationChooser> chooser = new nsApplicationChooser;
++
++ if (!chooser) {
++ return NS_ERROR_OUT_OF_MEMORY;
++ }
++
++ return chooser->QueryInterface(aIID, aResult);
++}
++#endif
++
+ static nsresult
+ nsColorPickerConstructor(nsISupports *aOuter, REFNSIID aIID,
+ void **aResult)
+ {
+ *aResult = nullptr;
+ if (aOuter != nullptr) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+@@ -170,16 +192,19 @@ nsColorPickerConstructor(nsISupports *aO
+ return picker->QueryInterface(aIID, aResult);
+ }
+
+ NS_DEFINE_NAMED_CID(NS_WINDOW_CID);
+ NS_DEFINE_NAMED_CID(NS_CHILD_CID);
+ NS_DEFINE_NAMED_CID(NS_APPSHELL_CID);
+ NS_DEFINE_NAMED_CID(NS_COLORPICKER_CID);
+ NS_DEFINE_NAMED_CID(NS_FILEPICKER_CID);
++#if (MOZ_WIDGET_GTK == 3)
++NS_DEFINE_NAMED_CID(NS_APPLICATIONCHOOSER_CID);
++#endif
+ NS_DEFINE_NAMED_CID(NS_SOUND_CID);
+ NS_DEFINE_NAMED_CID(NS_TRANSFERABLE_CID);
+ #ifdef MOZ_X11
+ NS_DEFINE_NAMED_CID(NS_CLIPBOARD_CID);
+ NS_DEFINE_NAMED_CID(NS_CLIPBOARDHELPER_CID);
+ NS_DEFINE_NAMED_CID(NS_DRAGSERVICE_CID);
+ #endif
+ NS_DEFINE_NAMED_CID(NS_HTMLFORMATCONVERTER_CID);
+@@ -201,16 +226,19 @@ NS_DEFINE_NAMED_CID(NS_GFXINFO_CID);
+
+
+ static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
+ { &kNS_WINDOW_CID, false, nullptr, nsWindowConstructor },
+ { &kNS_CHILD_CID, false, nullptr, nsChildWindowConstructor },
+ { &kNS_APPSHELL_CID, false, nullptr, nsAppShellConstructor },
+ { &kNS_COLORPICKER_CID, false, nullptr, nsColorPickerConstructor, Module::MAIN_PROCESS_ONLY },
+ { &kNS_FILEPICKER_CID, false, nullptr, nsFilePickerConstructor, Module::MAIN_PROCESS_ONLY },
++#if (MOZ_WIDGET_GTK == 3)
++ { &kNS_APPLICATIONCHOOSER_CID, false, nullptr, nsApplicationChooserConstructor, Module::MAIN_PROCESS_ONLY },
++#endif
+ { &kNS_SOUND_CID, false, nullptr, nsSoundConstructor, Module::MAIN_PROCESS_ONLY },
+ { &kNS_TRANSFERABLE_CID, false, nullptr, nsTransferableConstructor },
+ #ifdef MOZ_X11
+ { &kNS_CLIPBOARD_CID, false, nullptr, nsClipboardConstructor, Module::MAIN_PROCESS_ONLY },
+ { &kNS_CLIPBOARDHELPER_CID, false, nullptr, nsClipboardHelperConstructor },
+ { &kNS_DRAGSERVICE_CID, false, nullptr, nsDragServiceConstructor, Module::MAIN_PROCESS_ONLY },
+ #endif
+ { &kNS_HTMLFORMATCONVERTER_CID, false, nullptr, nsHTMLFormatConverterConstructor },
+@@ -234,16 +262,19 @@ static const mozilla::Module::CIDEntry k
+ };
+
+ static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
+ { "@mozilla.org/widget/window/gtk;1", &kNS_WINDOW_CID },
+ { "@mozilla.org/widgets/child_window/gtk;1", &kNS_CHILD_CID },
+ { "@mozilla.org/widget/appshell/gtk;1", &kNS_APPSHELL_CID },
+ { "@mozilla.org/colorpicker;1", &kNS_COLORPICKER_CID, Module::MAIN_PROCESS_ONLY },
+ { "@mozilla.org/filepicker;1", &kNS_FILEPICKER_CID, Module::MAIN_PROCESS_ONLY },
++#if (MOZ_WIDGET_GTK == 3)
++ { "@mozilla.org/applicationchooser;1", &kNS_APPLICATIONCHOOSER_CID, Module::MAIN_PROCESS_ONLY },
++#endif
+ { "@mozilla.org/sound;1", &kNS_SOUND_CID, Module::MAIN_PROCESS_ONLY },
+ { "@mozilla.org/widget/transferable;1", &kNS_TRANSFERABLE_CID },
+ #ifdef MOZ_X11
+ { "@mozilla.org/widget/clipboard;1", &kNS_CLIPBOARD_CID, Module::MAIN_PROCESS_ONLY },
+ { "@mozilla.org/widget/clipboardhelper;1", &kNS_CLIPBOARDHELPER_CID },
+ { "@mozilla.org/widget/dragservice;1", &kNS_DRAGSERVICE_CID, Module::MAIN_PROCESS_ONLY },
+ #endif
+ { "@mozilla.org/widget/htmlformatconverter;1", &kNS_HTMLFORMATCONVERTER_CID },
+diff --git a/widget/moz.build b/widget/moz.build
+--- a/widget/moz.build
++++ b/widget/moz.build
+@@ -205,16 +205,20 @@ if toolkit in ('qt', 'gtk2', 'gtk3', 'co
+ UNIFIED_SOURCES += [
+ 'nsBaseFilePicker.cpp',
+ ]
+
+ if toolkit in ('qt', 'gtk2', 'gtk3', 'windows', 'cocoa'):
+ UNIFIED_SOURCES += [
+ 'nsNativeTheme.cpp',
+ ]
++if toolkit == 'gtk3':
++ XPIDL_SOURCES += [
++ 'nsIApplicationChooser.idl',
++ ]
+
+ if not CONFIG['MOZ_B2G']:
+ DEFINES['MOZ_CROSS_PROCESS_IME'] = True
+
+ FAIL_ON_WARNINGS = True
+
+ include('/ipc/chromium/chromium-config.mozbuild')
+
+diff --git a/widget/nsIApplicationChooser.idl b/widget/nsIApplicationChooser.idl
+new file mode 100644
+--- /dev/null
++++ b/widget/nsIApplicationChooser.idl
+@@ -0,0 +1,39 @@
++/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
++ *
++ * This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++#include "nsISupports.idl"
++#include "nsIMIMEInfo.idl"
++interface nsIDOMWindow;
++
++[scriptable, function, uuid(8144404d-e6c7-4861-bcca-47de912ee811)]
++interface nsIApplicationChooserFinishedCallback : nsISupports
++{
++ void done(in nsIHandlerApp handlerApp);
++};
++
++[scriptable, uuid(8413fc42-d6c4-4d78-bf70-64cd78ebcc5c)]
++interface nsIApplicationChooser : nsISupports
++{
++ /**
++ * Initialize the application chooser picker widget. The application chooser
++ * is not valid until this method is called.
++ *
++ * @param parent nsIDOMWindow parent. This dialog will be dependent
++ * on this parent. parent must be non-null.
++ * @param title The title for the file widget
++ *
++ */
++ void init(in nsIDOMWindow parent, in ACString title);
++
++ /**
++ * Open application chooser dialog.
++ *
++ * @param contentType content type of file to open
++ * @param applicationChooserFinishedCallback callback fuction to run when dialog is closed
++ */
++ void open(in ACString contentType, in nsIApplicationChooserFinishedCallback applicationChooserFinishedCallback);
++};
++
+diff --git a/widget/nsWidgetsCID.h b/widget/nsWidgetsCID.h
+--- a/widget/nsWidgetsCID.h
++++ b/widget/nsWidgetsCID.h
+@@ -19,16 +19,21 @@
+ { 0xba7de611, 0x6088, 0x11d3, \
+ { 0xa8, 0x3e, 0x0, 0x10, 0x5a, 0x18, 0x34, 0x19 } }
+
+ /* bd57cee8-1dd1-11b2-9fe7-95cf4709aea3 */
+ #define NS_FILEPICKER_CID \
+ { 0xbd57cee8, 0x1dd1, 0x11b2, \
+ {0x9f, 0xe7, 0x95, 0xcf, 0x47, 0x09, 0xae, 0xa3} }
+
++/* e221df9b-3d66-4045-9a66-5720949f8d10 */
++#define NS_APPLICATIONCHOOSER_CID \
++{ 0xe221df9b, 0x3d66, 0x4045, \
++ {0x9a, 0x66, 0x57, 0x20, 0x94, 0x9f, 0x8d, 0x10} }
++
+ /* 0f872c8c-3ee6-46bd-92a2-69652c6b474e */
+ #define NS_COLORPICKER_CID \
+ { 0x0f872c8c, 0x3ee6, 0x46bd, \
+ { 0x92, 0xa2, 0x69, 0x65, 0x2c, 0x6b, 0x47, 0x4e } }
+
+ /* 2d96b3df-c051-11d1-a827-0040959a28c9 */
+ #define NS_APPSHELL_CID \
+ { 0x2d96b3df, 0xc051, 0x11d1, \
bgstack15