Bug 1514364 - Add appendRawFilter to nsIFilePicker to expose actual accept filters to GV for onFilePrompt. r=snorp, smaug
authorDylan Roeh <droeh@mozilla.com>
Thu, 24 Jan 2019 10:21:05 -0600
changeset 515375 ab592ce5d5ae37bd2d5008600d8c566ed30becae
parent 515374 250c2923583de86eb9afd4e47ddc0a1ad242d5d4
child 515376 a3b8503e6109c7e6e81cee27af25b9ec1b01d74d
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp, smaug
bugs1514364
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1514364 - Add appendRawFilter to nsIFilePicker to expose actual accept filters to GV for onFilePrompt. r=snorp, smaug
dom/html/HTMLInputElement.cpp
dom/ipc/FilePickerParent.cpp
dom/ipc/FilePickerParent.h
dom/ipc/PFilePicker.ipdl
mobile/android/components/geckoview/GeckoViewPrompt.js
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
widget/nsBaseFilePicker.cpp
widget/nsBaseFilePicker.h
widget/nsFilePickerProxy.cpp
widget/nsIFilePicker.idl
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -6931,16 +6931,20 @@ void HTMLInputElement::SetFilePickerFilt
     }
 
     if (!filterMask && (extensionListStr.IsEmpty() || filterName.IsEmpty())) {
       // No valid filter found
       allMimeTypeFiltersAreValid = false;
       continue;
     }
 
+    // At this point we're sure the token represents a valid filter, so pass
+    // it directly as a raw filter.
+    filePicker->AppendRawFilter(token);
+
     // If we arrived here, that means we have a valid filter: let's create it
     // and add it to our list, if no similar filter is already present
     nsFilePickerFilter filter;
     if (filterMask) {
       filter = nsFilePickerFilter(filterMask);
     } else {
       filter = nsFilePickerFilter(filterName, extensionListStr);
     }
--- a/dom/ipc/FilePickerParent.cpp
+++ b/dom/ipc/FilePickerParent.cpp
@@ -236,29 +236,34 @@ bool FilePickerParent::CreateFilePicker(
   return NS_SUCCEEDED(mFilePicker->Init(window, mTitle, mMode));
 }
 
 mozilla::ipc::IPCResult FilePickerParent::RecvOpen(
     const int16_t& aSelectedType, const bool& aAddToRecentDocs,
     const nsString& aDefaultFile, const nsString& aDefaultExtension,
     InfallibleTArray<nsString>&& aFilters,
     InfallibleTArray<nsString>&& aFilterNames,
+    InfallibleTArray<nsString>&& aRawFilters,
     const nsString& aDisplayDirectory, const nsString& aDisplaySpecialDirectory,
     const nsString& aOkButtonLabel) {
   if (!CreateFilePicker()) {
     Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel);
     return IPC_OK();
   }
 
   mFilePicker->SetAddToRecentDocs(aAddToRecentDocs);
 
   for (uint32_t i = 0; i < aFilters.Length(); ++i) {
     mFilePicker->AppendFilter(aFilterNames[i], aFilters[i]);
   }
 
+  for (uint32_t i = 0; i < aRawFilters.Length(); ++i) {
+    mFilePicker->AppendRawFilter(aRawFilters[i]);
+  }
+
   mFilePicker->SetDefaultString(aDefaultFile);
   mFilePicker->SetDefaultExtension(aDefaultExtension);
   mFilePicker->SetFilterIndex(aSelectedType);
   mFilePicker->SetOkButtonLabel(aOkButtonLabel);
 
   if (!aDisplayDirectory.IsEmpty()) {
     nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
     if (localFile) {
--- a/dom/ipc/FilePickerParent.h
+++ b/dom/ipc/FilePickerParent.h
@@ -37,16 +37,17 @@ class FilePickerParent : public PFilePic
 
   void SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData);
 
   virtual mozilla::ipc::IPCResult RecvOpen(
       const int16_t& aSelectedType, const bool& aAddToRecentDocs,
       const nsString& aDefaultFile, const nsString& aDefaultExtension,
       InfallibleTArray<nsString>&& aFilters,
       InfallibleTArray<nsString>&& aFilterNames,
+      InfallibleTArray<nsString>&& aRawFilters,
       const nsString& aDisplayDirectory,
       const nsString& aDisplaySpecialDirectory,
       const nsString& aOkButtonLabel) override;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   class FilePickerShownCallback : public nsIFilePickerShownCallback {
    public:
--- a/dom/ipc/PFilePicker.ipdl
+++ b/dom/ipc/PFilePicker.ipdl
@@ -36,17 +36,17 @@ union MaybeInputData
 
 protocol PFilePicker
 {
   manager PBrowser;
 
 parent:
     async Open(int16_t selectedType, bool addToRecentDocs, nsString defaultFile,
                nsString defaultExtension, nsString[] filters, nsString[] filterNames,
-               nsString displayDirectory, nsString displaySpecialDirectory,
-               nsString okButtonLabel);
+               nsString[] rawFilters, nsString displayDirectory,
+               nsString displaySpecialDirectory, nsString okButtonLabel);
 
 child:
     async __delete__(MaybeInputData data, int16_t result);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/mobile/android/components/geckoview/GeckoViewPrompt.js
+++ b/mobile/android/components/geckoview/GeckoViewPrompt.js
@@ -807,65 +807,32 @@ FilePickerDelegate.prototype = {
     this._prompt = new PromptDelegate(aParent);
     this._msg = {
       type: "file",
       title: aTitle,
       mode: (aMode === Ci.nsIFilePicker.modeOpenMultiple) ? "multiple" : "single",
     };
     this._mode = aMode;
     this._mimeTypes = [];
-    this._extensions = [];
   },
 
   get mode() {
     return this._mode;
   },
 
-  appendFilters: function(aFilterMask) {
-    if (aFilterMask & Ci.nsIFilePicker.filterAll) {
-      this._mimeTypes.push("*/*");
-    }
-    if (aFilterMask & Ci.nsIFilePicker.filterAudio) {
-      this._mimeTypes.push("audio/*");
-    }
-    if (aFilterMask & Ci.nsIFilePicker.filterImages) {
-      this._mimeTypes.push("image/*");
-    }
-    if (aFilterMask & Ci.nsIFilePicker.filterVideo) {
-      this._mimeTypes.push("video/*");
-    }
-    if (aFilterMask & Ci.nsIFilePicker.filterHTML) {
-      this._mimeTypes.push("text/html");
-    }
-    if (aFilterMask & Ci.nsIFilePicker.filterText) {
-      this._mimeTypes.push("text/plain");
-    }
-    if (aFilterMask & Ci.nsIFilePicker.filterXML) {
-      this._mimeTypes.push("text/xml");
-    }
-    if (aFilterMask & Ci.nsIFilePicker.filterXUL) {
-      this._mimeTypes.push("application/vnd.mozilla.xul+xml");
-    }
-  },
-
-  appendFilter: function(aTitle, aFilter) {
-    // Only include filter that specify extensions (i.e. exclude generic ones like "*").
-    let filters = aFilter.split(/[\s,;]+/).filter(filter => filter.includes("."));
-    Array.prototype.push.apply(this._extensions, filters);
+  appendRawFilter: function(aFilter) {
+    this._mimeTypes.push(aFilter);
   },
 
   show: function() {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   },
 
   open: function(aFilePickerShownCallback) {
     this._msg.mimeTypes = this._mimeTypes;
-    if (this._extensions.length) {
-      this._msg.extensions = this._extensions;
-    }
     this._prompt.asyncShowPrompt(this._msg, result => {
       // OK: result
       // Cancel: !result
       if (!result || !result.files || !result.files.length) {
         aFilePickerShownCallback.done(Ci.nsIFilePicker.returnCancel);
       } else {
         this._files = result.files;
         aFilePickerShownCallback.done(Ci.nsIFilePicker.returnOK);
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -2347,30 +2347,16 @@ public class GeckoSession implements Par
                     intMode = PromptDelegate.FILE_TYPE_SINGLE;
                 } else if ("multiple".equals(mode)) {
                     intMode = PromptDelegate.FILE_TYPE_MULTIPLE;
                 } else {
                     callback.sendError("Invalid mode");
                     return;
                 }
                 String[] mimeTypes = message.getStringArray("mimeTypes");
-                final String[] extensions = message.getStringArray("extension");
-                if (extensions != null) {
-                    final ArrayList<String> combined =
-                            new ArrayList<>(mimeTypes.length + extensions.length);
-                    combined.addAll(Arrays.asList(mimeTypes));
-                    for (final String extension : extensions) {
-                        final String mimeType =
-                                URLConnection.guessContentTypeFromName(extension);
-                        if (mimeType != null) {
-                            combined.add(mimeType);
-                        }
-                    }
-                    mimeTypes = combined.toArray(new String[combined.size()]);
-                }
                 delegate.onFilePrompt(session, title, intMode, mimeTypes, cb);
                 break;
             }
             case "popup": {
                 GeckoResult<AllowOrDeny> res = delegate.onPopupRequest(session, message.getString("targetUri"));
 
                 if (res == null) {
                     // Keep the popup blocked if the delegate returns null
@@ -3604,19 +3590,20 @@ public class GeckoSession implements Par
         static final int FILE_TYPE_MULTIPLE = 2;
 
         /**
          * Display a file prompt.
          *
          * @param session GeckoSession that triggered the prompt
          * @param title Title for the prompt dialog.
          * @param type One of FILE_TYPE_* indicating the prompt type.
-         * @param mimeTypes Array of permissible MIME types for the selected files, in
-         *                  the form "type/subtype", where "type" and/or "subtype" can be
-         *                  "*" to indicate any value.
+         * @param mimeTypes Array of permissible MIME types or extensions for the selected
+         *                  files. MIME types are of the form "type/subtype", where "type"
+         *                  and/or "subtype" can be "*" to indicate any value. Extensions
+         *                  are of the form ".ext".
          * @param callback Callback interface.
          */
         @UiThread
         void onFilePrompt(@NonNull GeckoSession session, @Nullable String title, @FileType int type,
                           @Nullable String[] mimeTypes, @NonNull FileCallback callback);
 
         /**
          * Display a popup request prompt; this occurs when content attempts to open
--- a/widget/nsBaseFilePicker.cpp
+++ b/widget/nsBaseFilePicker.cpp
@@ -227,16 +227,21 @@ nsBaseFilePicker::AppendFilters(int32_t 
     titleBundle->GetStringFromName("appsTitle", title);
     // Pass the magic string "..apps" to the platform filepicker, which it
     // should recognize and do the correct platform behavior for.
     AppendFilter(title, NS_LITERAL_STRING("..apps"));
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP nsBaseFilePicker::AppendRawFilter(const nsAString& aFilter) {
+  mRawFilters.AppendElement(aFilter);
+  return NS_OK;
+}
+
 // Set the filter index
 NS_IMETHODIMP nsBaseFilePicker::GetFilterIndex(int32_t* aFilterIndex) {
   *aFilterIndex = 0;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsBaseFilePicker::SetFilterIndex(int32_t aFilterIndex) {
   return NS_OK;
--- a/widget/nsBaseFilePicker.h
+++ b/widget/nsBaseFilePicker.h
@@ -24,16 +24,17 @@ class nsBaseFilePicker : public nsIFileP
   nsBaseFilePicker();
   virtual ~nsBaseFilePicker();
 
   NS_IMETHOD Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle,
                   int16_t aMode) override;
 
   NS_IMETHOD Open(nsIFilePickerShownCallback* aCallback) override;
   NS_IMETHOD AppendFilters(int32_t filterMask) override;
+  NS_IMETHOD AppendRawFilter(const nsAString& aFilter) override;
   NS_IMETHOD GetFilterIndex(int32_t* aFilterIndex) override;
   NS_IMETHOD SetFilterIndex(int32_t aFilterIndex) override;
   NS_IMETHOD GetFiles(nsISimpleEnumerator** aFiles) override;
   NS_IMETHOD GetDisplayDirectory(nsIFile** aDisplayDirectory) override;
   NS_IMETHOD SetDisplayDirectory(nsIFile* aDisplayDirectory) override;
   NS_IMETHOD GetDisplaySpecialDirectory(nsAString& aDisplayDirectory) override;
   NS_IMETHOD SetDisplaySpecialDirectory(
       const nsAString& aDisplayDirectory) override;
@@ -53,11 +54,12 @@ class nsBaseFilePicker : public nsIFileP
 
   bool mAddToRecentDocs;
   nsCOMPtr<nsIFile> mDisplayDirectory;
   nsString mDisplaySpecialDirectory;
 
   nsCOMPtr<nsPIDOMWindowOuter> mParent;
   int16_t mMode;
   nsString mOkButtonLabel;
+  InfallibleTArray<nsString> mRawFilters;
 };
 
 #endif  // nsBaseFilePicker_h__
--- a/widget/nsFilePickerProxy.cpp
+++ b/widget/nsFilePickerProxy.cpp
@@ -120,18 +120,18 @@ nsFilePickerProxy::Open(nsIFilePickerSho
     mDisplayDirectory->GetPath(displayDirectory);
   }
 
   if (!mIPCActive) {
     return NS_ERROR_FAILURE;
   }
 
   SendOpen(mSelectedType, mAddToRecentDocs, mDefault, mDefaultExtension,
-           mFilters, mFilterNames, displayDirectory, mDisplaySpecialDirectory,
-           mOkButtonLabel);
+           mFilters, mFilterNames, mRawFilters, displayDirectory,
+           mDisplaySpecialDirectory, mOkButtonLabel);
 
   return NS_OK;
 }
 
 mozilla::ipc::IPCResult nsFilePickerProxy::Recv__delete__(
     const MaybeInputData& aData, const int16_t& aResult) {
   if (aData.type() == MaybeInputData::TInputBlobs) {
     const InfallibleTArray<IPCBlob>& blobs = aData.get_InputBlobs().blobs();
--- a/widget/nsIFilePicker.idl
+++ b/widget/nsIFilePicker.idl
@@ -83,16 +83,26 @@ interface nsIFilePicker : nsISupports
   *
   * @param      title    name of the filter
   * @param      filter   extensions to filter -- semicolon and space separated
   *
   */
   void appendFilter(in AString title,
                     in AString filter);
 
+  /**
+   * Add a raw filter (eg, add a MIME type without transforming it to a list of
+   * extensions).
+   *
+   * @param     filter   a filter taken directly from the accept attribute
+   *                     without processing
+   *
+   */
+  void appendRawFilter(in AString filter);
+
  /**
   * The filename that should be suggested to the user as a default. This should
   * include the extension.
   *
   * @throws NS_ERROR_FAILURE on attempts to get
   */
   attribute AString defaultString;