1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115

content / public / browser / web_ui_controller_interface_binder.h [blame]

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

#ifndef CONTENT_PUBLIC_BROWSER_WEB_UI_CONTROLLER_INTERFACE_BINDER_H_
#define CONTENT_PUBLIC_BROWSER_WEB_UI_CONTROLLER_INTERFACE_BINDER_H_

#include <type_traits>

#include "content/common/content_export.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_controller.h"
#include "mojo/public/cpp/bindings/binder_map.h"

namespace content {
namespace internal {

template <typename Interface, int N, typename... Subclasses>
struct BinderHelper;

template <typename Interface, typename WebUIControllerSubclass, typename = void>
struct BinderTakesRenderFrameHost : std::false_type {};

template <typename Interface, typename WebUIControllerSubclass>
struct BinderTakesRenderFrameHost<
    Interface,
    WebUIControllerSubclass,
    decltype(std::declval<WebUIControllerSubclass>().BindInterface(
        std::declval<RenderFrameHost*>(),
        std::declval<mojo::PendingReceiver<Interface>>()))> : std::true_type {};

template <typename Interface, typename WebUIControllerSubclass>
bool SafeDownCastAndBindInterface(RenderFrameHost* host,
                                  mojo::PendingReceiver<Interface>& receiver) {
  // Performs a safe downcast to the concrete WebUIController subclass.
  WebUI* web_ui = host->GetWebUI();
  WebUIControllerSubclass* concrete_controller =
      web_ui ? web_ui->GetController()->GetAs<WebUIControllerSubclass>()
             : nullptr;

  if (!concrete_controller)
    return false;

  // Fails to compile if |Subclass| does not implement the appropriate overload
  // for |Interface|.
  if constexpr (BinderTakesRenderFrameHost<Interface,
                                           WebUIControllerSubclass>::value) {
    concrete_controller->BindInterface(host, std::move(receiver));
  } else {
    concrete_controller->BindInterface(std::move(receiver));
  }
  return true;
}

template <typename Interface, int N, typename Subclass, typename... Subclasses>
struct BinderHelper<Interface, N, std::tuple<Subclass, Subclasses...>> {
  static bool BindInterface(RenderFrameHost* host,
                            mojo::PendingReceiver<Interface> receiver) {
    // Try a different subclass if the current one is not the right
    // WebUIController for the current WebUI page, and only fail if none of the
    // passed subclasses match.
    if (!SafeDownCastAndBindInterface<Interface, Subclass>(host, receiver)) {
      return BinderHelper<Interface, N - 1, std::tuple<Subclasses...>>::
          BindInterface(host, std::move(receiver));
    }
    return true;
  }
};

template <typename Interface, typename Subclass, typename... Subclasses>
struct BinderHelper<Interface, 0, std::tuple<Subclass, Subclasses...>> {
  static bool BindInterface(RenderFrameHost* host,
                            mojo::PendingReceiver<Interface> receiver) {
    return SafeDownCastAndBindInterface<Interface, Subclass>(host, receiver);
  }
};

void CONTENT_EXPORT ReceivedInvalidWebUIControllerMessage(RenderFrameHost* rfh);

}  // namespace internal

// Registers a binder in |map| that binds |Interface| iff the RenderFrameHost
// has a WebUIController among type |WebUIControllerSubclasses|.
template <typename Interface, typename... WebUIControllerSubclasses>
void RegisterWebUIControllerInterfaceBinder(
    mojo::BinderMapWithContext<RenderFrameHost*>* map) {
  DCHECK(!map->Contains<Interface>())
      << "A binder for " << Interface::Name_ << " has already been registered.";
  map->Add<Interface>(base::BindRepeating(
      [](RenderFrameHost* host, mojo::PendingReceiver<Interface> receiver) {
        // This is expected to be called only for outermost main frames.
        if (host->GetParentOrOuterDocument()) {
          internal::ReceivedInvalidWebUIControllerMessage(host);
          return;
        }

        const int size = sizeof...(WebUIControllerSubclasses);
        bool is_bound =
            internal::BinderHelper<Interface, size - 1,
                                   std::tuple<WebUIControllerSubclasses...>>::
                BindInterface(host, std::move(receiver));

        // This is expected to be called only for the right WebUI pages matching
        // the same WebUI associated to the RenderFrameHost.
        if (!is_bound) {
          internal::ReceivedInvalidWebUIControllerMessage(host);
          return;
        }
      }));
}

}  // namespace content

#endif  // CONTENT_PUBLIC_BROWSER_WEB_UI_CONTROLLER_INTERFACE_BINDER_H_