chromium/chrome/browser/ui/views/select_file_dialog_extension.h

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_UI_VIEWS_SELECT_FILE_DIALOG_EXTENSION_H_
#define CHROME_BROWSER_UI_VIEWS_SELECT_FILE_DIALOG_EXTENSION_H_

#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_file_destination.h"
#include "ui/color/color_provider_source_observer.h"
#include "ui/gfx/native_widget_types.h"  // gfx::NativeWindow
#include "ui/shell_dialogs/select_file_dialog.h"
#include "url/gurl.h"

class Profile;

namespace aura {
class Window;
}  // namespace aura

namespace content {
class RenderFrameHost;
class WebContents;
}  // namespace content

namespace ui {
struct SelectedFileInfo;
class SelectFilePolicy;
}  // namespace ui

// Shows a dialog box for selecting a file or a folder, using the
// file manager extension implementation.
class SelectFileDialogExtension : public ui::SelectFileDialog {
 public:
  // Opaque ID type for identifying the tab spawned each dialog, unique for
  // every WebContents or every Android task ID.
  typedef std::string RoutingID;

  SelectFileDialogExtension(const SelectFileDialogExtension&) = delete;
  SelectFileDialogExtension& operator=(SelectFileDialogExtension&) = delete;

  static SelectFileDialogExtension* Create(
      ui::SelectFileDialog::Listener* listener,
      std::unique_ptr<ui::SelectFilePolicy> policy);

  // ui::SelectFileDialog:
  bool IsRunning(gfx::NativeWindow owner_window) const override;
  void ListenerDestroyed() override;

  // Routes callback to appropriate SelectFileDialog::Listener based on the
  // owning |web_contents|.
  static void OnFileSelected(RoutingID routing_id,
                             const ui::SelectedFileInfo& file,
                             int index);
  static void OnMultiFilesSelected(
      RoutingID routing_id,
      const std::vector<ui::SelectedFileInfo>& files);
  static void OnFileSelectionCanceled(RoutingID routing_id);

  // Helper method that given parameters that are passed to the
  // SelectFileWithFileManagerParams method creates a URL for launching File
  // Manager as a dialog.
  static GURL MakeDialogURL(Type type,
                            const std::u16string& title,
                            const base::FilePath& default_path,
                            const FileTypeInfo* file_types,
                            int file_type_index,
                            const std::string& search_query,
                            bool show_android_picker_apps,
                            std::vector<std::string> volume_filter,
                            Profile* profile);

  // Allows access to the extension's main frame for injecting javascript.
  content::RenderFrameHost* GetPrimaryMainFrame();

  // Call SelectFile with params specific to Chrome OS file manager.
  // |owner| specifies the window and app type that opened the dialog.
  // |show_android_picker_apps| determines whether to show Android picker apps
  //     in the select file dialog.
  struct Owner {
    Owner();
    ~Owner();
    Owner(const Owner&);
    Owner& operator=(const Owner&);
    Owner(Owner&&);
    Owner& operator=(Owner&&);

    // The native window that opened the dialog.
    raw_ptr<aura::Window, LeakedDanglingUntriaged> window = nullptr;
    // Android task ID if the owner window is an Android app.
    std::optional<int> android_task_id;
    // Lacros window ID if the owner window is a Lacros browser. This field
    // can be nullopt even when is_lacros is true, for dialogs that are not
    // owned by a particular window, aka "modeless" dialog.
    std::optional<std::string> lacros_window_id;
    // Set to true only if SelectFileAsh opened the dialog.
    bool is_lacros = false;
    // The URL or Component type of the caller that opened the dialog (Save
    // As/File Picker).
    std::optional<policy::DlpFileDestination> dialog_caller;
  };
  void SelectFileWithFileManagerParams(Type type,
                                       const std::u16string& title,
                                       const base::FilePath& default_path,
                                       const FileTypeInfo* file_types,
                                       int file_type_index,
                                       const Owner& owner,
                                       const std::string& search_query,
                                       bool show_android_picker_apps,
                                       bool use_media_store_filter = false);

 protected:
  // ui::SelectFileDialog:
  void SelectFileImpl(Type type,
                      const std::u16string& title,
                      const base::FilePath& default_path,
                      const FileTypeInfo* file_types,
                      int file_type_index,
                      const base::FilePath::StringType& default_extension,
                      gfx::NativeWindow owning_window,
                      const GURL* caller) override;
  bool HasMultipleFileTypeChoicesImpl() override;

 private:
  friend class BaseSelectFileDialogExtensionBrowserTest;
  friend class SelectFileDialogExtensionTest;
  friend class SelectFileDialogExtensionTestFactory;
  friend class SystemFilesAppDialogDelegate;
  FRIEND_TEST_ALL_PREFIXES(SelectFileDialogExtensionTest, FileSelected);
  FRIEND_TEST_ALL_PREFIXES(SelectFileDialogExtensionTest,
                           FileSelectionCanceled);
  FRIEND_TEST_ALL_PREFIXES(SelectFileDialogExtensionTest, SelfDeleting);
  FRIEND_TEST_ALL_PREFIXES(SelectFileDialogExtensionBrowserTest,
                           DialogCallerSetWhenPassed);

  // For the benefit of SystemFilesAppDialogDelegate.
  void OnSystemDialogShown(content::WebContents* content,
                           const std::string& id);
  void OnSystemDialogWillClose();

  // Object is ref-counted, use Create().
  explicit SelectFileDialogExtension(
      SelectFileDialog::Listener* listener,
      std::unique_ptr<ui::SelectFilePolicy> policy);
  ~SelectFileDialogExtension() override;

  // Applies DLP policies if there's any, then notifies listeners accordingly.
  void ApplyPolicyAndNotifyListener(
      std::optional<policy::DlpFileDestination> dialog_caller);

  // Invokes the appropriate file selection callback on our listener.
  void NotifyListener(std::vector<ui::SelectedFileInfo> selection_files);

  // Adds this to the list of pending dialogs, used for testing.
  void AddPending(RoutingID routing_id);

  // Check if the list of pending dialogs contains dialog for |routing_id|.
  static bool PendingExists(RoutingID routing_id);

  // Returns true if |extension_dialog_| is resizable; the dialog must be
  // non-null at the time of this call.
  bool IsResizeable() const;

  bool has_multiple_file_type_choices_ = false;

  // If System Files App is enabled it stores the web contents associated with
  // System File App dialog. Not owned by this class. Set only while System
  // Files App dialog is opened.
  raw_ptr<content::WebContents, LeakedDanglingUntriaged>
      system_files_app_web_contents_;

  // ID of the tab that spawned this dialog, used to route callbacks.
  RoutingID routing_id_;

  // Pointer to the profile the dialog is running in.
  raw_ptr<Profile, LeakedDanglingUntriaged> profile_ = nullptr;

  // Information about the dialog's owner, such as the window or app type.
  Owner owner_;

  // Dialog type.
  Type type_;

  // We defer the callback into SelectFileDialog::Listener until the window
  // closes, to match the semantics of file selection on Windows and Mac.
  // These are the data passed to the listener.
  enum SelectionType {
    CANCEL = 0,
    SINGLE_FILE,
    MULTIPLE_FILES
  };
  SelectionType selection_type_ = CANCEL;
  std::vector<ui::SelectedFileInfo> selection_files_;
  int selection_index_ = 0;
  bool can_resize_ = true;
  base::WeakPtrFactory<SelectFileDialogExtension> weak_factory_{this};
};

#endif  // CHROME_BROWSER_UI_VIEWS_SELECT_FILE_DIALOG_EXTENSION_H_