chromium/ios/chrome/browser/web/model/image_fetch/image_fetch_java_script_feature.h

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

#ifndef IOS_CHROME_BROWSER_WEB_MODEL_IMAGE_FETCH_IMAGE_FETCH_JAVA_SCRIPT_FEATURE_H_
#define IOS_CHROME_BROWSER_WEB_MODEL_IMAGE_FETCH_IMAGE_FETCH_JAVA_SCRIPT_FEATURE_H_

#include <optional>

#include "base/functional/callback.h"
#include "base/no_destructor.h"
#import "ios/web/public/js_messaging/java_script_feature.h"

class GURL;

// A feature which can retrieve image data from a webpage.
class ImageFetchJavaScriptFeature : public web::JavaScriptFeature {
 public:
  // Returns a singleton instance of ImageFetchJavaScriptFeature which uses
  // ImageFetchTabHelper as its handler.
  static ImageFetchJavaScriptFeature* GetInstance();

  class Handler {
   public:
    virtual ~Handler() = default;

    // Called when the webpage successfully sends back image data. `call_id` was
    // the token originally passed to GetImageData(). `decoded_data` is the raw
    // image data. `from` is a string explaining how the image data was
    // retrieved and may be empty.
    virtual void HandleJsSuccess(int call_id,
                                 std::string& decoded_data,
                                 std::string& from) = 0;

    // Called when the webpage fails to retrieve image data. `call_id` was the
    // token originally passed to GetImageData().
    virtual void HandleJsFailure(int call_id) = 0;
  };

  // Gets image data in binary format by trying 2 JavaScript methods in order:
  //   1. Draw <img> to <canvas> and export its data;
  //   2. Download the image by XMLHttpRequest and hopefully get responded from
  //   cache.
  // `url` should be equal to the resolved "src" attribute of <img>, otherwise
  // method 1 will fail. `call_id` is an opaque token that will be passed back
  // along with the response.
  //
  // Upon success or failure, this will invoke the appropriate Handler method.
  void GetImageData(web::WebState* web_state, int call_id, const GURL& url);

 private:
  // Tests are added as friends so that they can call the
  // ImageFetchJavaScriptFeature constructor, rather than relying on the
  // singleton instance.
  friend class ImageFetchTabHelperTest;
  friend class ImageFetchJavaScriptFeatureTest;
  friend class base::NoDestructor<ImageFetchJavaScriptFeature>;

  // Constructs an ImageFetchJavaScriptFeature which uses the given
  // `handler_factory`. Production code will generally install a factory which
  // returns the ImageFetchTabHelper for the given WebState, while test code can
  // install a custom factory to make testing easier. `handler_factory` can
  // return nullptr and will always be passed a non-nullptr WebState.
  ImageFetchJavaScriptFeature(
      base::RepeatingCallback<Handler*(web::WebState*)> handler_factory);
  ~ImageFetchJavaScriptFeature() override;

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

  // JavaScriptFeature:
  std::optional<std::string> GetScriptMessageHandlerName() const override;
  void ScriptMessageReceived(web::WebState* web_state,
                             const web::ScriptMessage& message) override;

  base::RepeatingCallback<Handler*(web::WebState*)> handler_factory_;
};

#endif  // IOS_CHROME_BROWSER_WEB_MODEL_IMAGE_FETCH_IMAGE_FETCH_JAVA_SCRIPT_FEATURE_H_