chromium/components/autofill/ios/form_util/child_frame_registrar.h

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

#ifndef COMPONENTS_AUTOFILL_IOS_FORM_UTIL_CHILD_FRAME_REGISTRAR_H_
#define COMPONENTS_AUTOFILL_IOS_FORM_UTIL_CHILD_FRAME_REGISTRAR_H_

#import <map>
#import <optional>

#import "base/observer_list.h"
#import "components/autofill/core/common/unique_ids.h"
#import "ios/web/public/js_messaging/java_script_feature.h"
#import "ios/web/public/web_state_user_data.h"

namespace autofill {

// Observer of ChildFrameRegistrar events.
class ChildFrameRegistrarObserver : public base::CheckedObserver {
 public:
  // Called when there was an attempt made to register the same remote token
  // twice with a different |local| token. This may mean spoofing so the
  // receiver of this notification should act accordingly.
  virtual void OnDidDoubleRegistration(LocalFrameToken local) = 0;
};

// Child frame registration is the process whereby a frame can assign an ID (a
// remote frame token) to a child frame, establishing a relationship between
// that frame in the DOM (and JS) and the corresponding WebFrame object in C++.
// This class maintains those mappings.
class ChildFrameRegistrar : public web::WebStateUserData<ChildFrameRegistrar> {
 public:
  ~ChildFrameRegistrar() override;

  // Maps from remote to local tokens for all registered frames, to allow
  // lookup of a frame based on its remote token.
  //
  // Frame Tokens are used by browser-layer Autofill code to identify and
  // interact with a specific frame. Local Frame Tokens must not leak to frames
  // other than the ones they identify, while Remote Frame Tokens are also known
  // to the parent frame.
  //
  // In the context of iOS, the LocalFrameToken is equal to the frameId and can
  // be used to fetch the appropriate WebFrame from the WebFramesManager.
  std::optional<LocalFrameToken> LookupChildFrame(RemoteFrameToken remote);

  // Informs the Registrar that `remote` corresponds to the frame with frame ID
  // `local`.
  void RegisterMapping(RemoteFrameToken remote, LocalFrameToken local);

  // Convenience method that looks for dict values with keys "local_frame_id"
  // and "remote_frame_id" in the given `message`, converts them to the
  // appropriate tokens, and calls `RegisterMapping` using them. No-op if
  // `message` is not a dict, if the needed keys are not present, or if the
  // values are malformed.
  void ProcessRegistrationMessage(base::Value* message);

  // Notifies the registrar that a remote token `remote` has been dispatched to
  // a child frame. If the registrar already knows about this token, `callback`
  // will be invoked immediately with the corresponding local token. If not,
  // the registrar will hold on to the callback until it encounters `remote` and
  // execute it at that time.
  // Be careful what you bind to `callback`, and use WeakPtr as necessary, as
  // it may be retained indefinitely.
  void DeclareNewRemoteToken(
      RemoteFrameToken remote,
      base::OnceCallback<void(LocalFrameToken)> callback);

  // Returns the existing registrar for the given `web_state`, if there is one,
  // or creates a new one. May return nullptr if this functionality is not
  // available.
  static ChildFrameRegistrar* GetOrCreateForWebState(web::WebState* web_state);

  // Adds the |observer| to the list of observers.
  void AddObserver(ChildFrameRegistrarObserver* observer);

  // Removes |observer| from the list of observers.
  void RemoveObserver(ChildFrameRegistrarObserver* observer);

 private:
  explicit ChildFrameRegistrar(web::WebState* web_state);
  friend class web::WebStateUserData<ChildFrameRegistrar>;
  WEB_STATE_USER_DATA_KEY_DECL();

  // Maintains the mapping used by `LookupChildFrame`. The value containing the
  // local frame token is set to std::nullopt if there was at least one attempt
  // to register the same remote token, as a security precaution, making the
  // token and thus the frame invalid for the entire registrar's lifetime.
  std::map<RemoteFrameToken, std::optional<LocalFrameToken>> lookup_map_;

  // When `DeclareNewRemoteToken` is called, the RemoteFrameToken may not
  // yet correspond to a known frame. In this case, the callback is stored in
  // this map until a matching remote token is registered.
  std::map<RemoteFrameToken, base::OnceCallback<void(LocalFrameToken)>>
      pending_callbacks_;

  base::ObserverList<ChildFrameRegistrarObserver> observers_;
};

}  // namespace autofill

#endif  // COMPONENTS_AUTOFILL_IOS_FORM_UTIL_CHILD_FRAME_REGISTRAR_H_