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

#ifndef CHROME_BROWSER_UI_EXTENSIONS_EXTENSION_ACTION_VIEW_CONTROLLER_H_
#define CHROME_BROWSER_UI_EXTENSIONS_EXTENSION_ACTION_VIEW_CONTROLLER_H_

#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h"
#include "chrome/browser/extensions/extension_action_icon_factory.h"
#include "chrome/browser/extensions/extension_context_menu_model.h"
#include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
#include "extensions/browser/extension_host_observer.h"
#include "ui/gfx/image/image.h"

class Browser;
class ExtensionAction;
class ExtensionActionPlatformDelegate;
class GURL;
class IconWithBadgeImageSource;
class ToolbarActionsBar;

namespace extensions {
class Command;
class Extension;
class ExtensionRegistry;
class ExtensionViewHost;
}

// The platform-independent controller for an ExtensionAction that is shown on
// the toolbar (such as a page or browser action).
// Since this class doesn't own the extension or extension action in question,
// be sure to check for validity using ExtensionIsValid() before using those
// members (see also comments above ExtensionIsValid()).
class ExtensionActionViewController
    : public ToolbarActionViewController,
      public ExtensionActionIconFactory::Observer,
      public extensions::ExtensionContextMenuModel::PopupDelegate,
      public extensions::ExtensionHostObserver {
 public:
  // The different options for showing a popup.
  enum PopupShowAction { SHOW_POPUP, SHOW_POPUP_AND_INSPECT };

  ExtensionActionViewController(const extensions::Extension* extension,
                                Browser* browser,
                                ExtensionAction* extension_action,
                                ToolbarActionsBar* toolbar_actions_bar);
  ~ExtensionActionViewController() override;

  // ToolbarActionViewController:
  std::string GetId() const override;
  void SetDelegate(ToolbarActionViewDelegate* delegate) override;
  gfx::Image GetIcon(content::WebContents* web_contents,
                     const gfx::Size& size) override;
  base::string16 GetActionName() const override;
  base::string16 GetAccessibleName(content::WebContents* web_contents) const
      override;
  base::string16 GetTooltip(content::WebContents* web_contents) const override;
  bool IsEnabled(content::WebContents* web_contents) const override;
  bool WantsToRun(content::WebContents* web_contents) const override;
  bool HasPopup(content::WebContents* web_contents) const override;
  void HidePopup() override;
  gfx::NativeView GetPopupNativeView() override;
  ui::MenuModel* GetContextMenu() override;
  void OnContextMenuClosed() override;
  bool ExecuteAction(bool by_user) override;
  void UpdateState() override;
  void RegisterCommand() override;
  bool DisabledClickOpensMenu() const override;

  // ExtensionContextMenuModel::PopupDelegate:
  void InspectPopup() override;

  // Closes the active popup (whether it was this action's popup or not).
  void HideActivePopup();

  // Populates |command| with the command associated with |extension|, if one
  // exists. Returns true if |command| was populated.
  bool GetExtensionCommand(extensions::Command* command);

  const extensions::Extension* extension() const { return extension_.get(); }
  Browser* browser() { return browser_; }
  ExtensionAction* extension_action() { return extension_action_; }
  const ExtensionAction* extension_action() const { return extension_action_; }
  ToolbarActionViewDelegate* view_delegate() { return view_delegate_; }
  bool is_showing_popup() const { return popup_host_ != nullptr; }

  void set_icon_observer(ExtensionActionIconFactory::Observer* icon_observer) {
    icon_observer_ = icon_observer;
  }

  scoped_ptr<IconWithBadgeImageSource> GetIconImageSourceForTesting(
      content::WebContents* web_contents,
      const gfx::Size& size);

 private:
  // ExtensionActionIconFactory::Observer:
  void OnIconUpdated() override;

  // ExtensionHostObserver:
  void OnExtensionHostDestroyed(const extensions::ExtensionHost* host) override;

  // Checks if the associated |extension| is still valid by checking its
  // status in the registry. Since the OnExtensionUnloaded() notifications are
  // not in a deterministic order, it's possible that the view tries to refresh
  // itself before we're notified to remove it.
  bool ExtensionIsValid() const;

  // In some cases (such as when an action is shown in a menu), a substitute
  // ToolbarActionViewController should be used for showing popups. This
  // returns the preferred controller.
  ExtensionActionViewController* GetPreferredPopupViewController();

  // Executes the extension action with |show_action|. If
  // |grant_tab_permissions| is true, this will grant the extension active tab
  // permissions. Only do this if this was done through a user action (and not
  // e.g. an API). Returns true if a popup is shown.
  bool ExecuteAction(PopupShowAction show_action, bool grant_tab_permissions);

  // Begins the process of showing the popup for the extension action, given the
  // associated |popup_url|. |grant_tab_permissions| is true if active tab
  // permissions should be given to the extension; this is only true if the
  // popup is opened through a user action.
  // The popup may not be shown synchronously if the extension is hidden and
  // first needs to slide itself out.
  // Returns true if a popup will be shown.
  bool TriggerPopupWithUrl(PopupShowAction show_action,
                           const GURL& popup_url,
                           bool grant_tab_permissions);

  // Shows the popup with the given |host|.
  void ShowPopup(scoped_ptr<extensions::ExtensionViewHost> host,
                 bool grant_tab_permissions,
                 PopupShowAction show_action);

  // Handles cleanup after the popup closes.
  void OnPopupClosed();

  // Returns the image source for the icon.
  scoped_ptr<IconWithBadgeImageSource> GetIconImageSource(
      content::WebContents* web_contents,
      const gfx::Size& size);

  // Returns true if this extension has a page action and that page action wants
  // to run on the given |web_contents|.
  bool PageActionWantsToRun(content::WebContents* web_contents) const;

  // Returns true if this extension has been blocked on the given
  // |web_contents|.
  bool HasBeenBlocked(content::WebContents* web_contents) const;

  // The extension associated with the action we're displaying.
  scoped_refptr<const extensions::Extension> extension_;

  // The corresponding browser.
  Browser* browser_;

  // The browser action this view represents. The ExtensionAction is not owned
  // by this class.
  ExtensionAction* extension_action_;

  // The owning ToolbarActionsBar, if any. This will be null if this is a
  // page action without the toolbar redesign turned on.
  // TODO(devlin): Would this be better behind a delegate interface? On the one
  // hand, it's odd for this class to know about ToolbarActionsBar, but on the
  // other, yet-another-delegate-class might just confuse things.
  ToolbarActionsBar* toolbar_actions_bar_;

  // The extension popup's host if the popup is visible; null otherwise.
  extensions::ExtensionViewHost* popup_host_;

  // The context menu model for the extension.
  scoped_ptr<extensions::ExtensionContextMenuModel> context_menu_model_;

  // Our view delegate.
  ToolbarActionViewDelegate* view_delegate_;

  // The delegate to handle platform-specific implementations.
  scoped_ptr<ExtensionActionPlatformDelegate> platform_delegate_;

  // The object that will be used to get the browser action icon for us.
  // It may load the icon asynchronously (in which case the initial icon
  // returned by the factory will be transparent), so we have to observe it for
  // updates to the icon.
  ExtensionActionIconFactory icon_factory_;

  // An additional observer that we need to notify when the icon of the button
  // has been updated.
  ExtensionActionIconFactory::Observer* icon_observer_;

  // The associated ExtensionRegistry; cached for quick checking.
  extensions::ExtensionRegistry* extension_registry_;

  ScopedObserver<extensions::ExtensionHost, extensions::ExtensionHostObserver>
      popup_host_observer_;

  base::WeakPtrFactory<ExtensionActionViewController> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(ExtensionActionViewController);
};

#endif  // CHROME_BROWSER_UI_EXTENSIONS_EXTENSION_ACTION_VIEW_CONTROLLER_H_