chromium/ios/chrome/browser/follow/model/follow_java_script_feature.mm

// 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.

#import "ios/chrome/browser/follow/model/follow_java_script_feature.h"

#import "ios/web/public/js_messaging/web_frames_manager.h"
#import "ios/web/public/web_state.h"
#import "net/base/apple/url_conversions.h"

namespace {
const char kRSSLinkScript[] = "rss_link";
const char kGetRSSLinkFunction[] = "rssLink.getRSSLinks";
// The timeout for any JavaScript call in this file.
const double kJavaScriptExecutionTimeoutInMs = 500.0;

}  // namespace

// static
FollowJavaScriptFeature* FollowJavaScriptFeature::GetInstance() {
  static base::NoDestructor<FollowJavaScriptFeature> instance;
  return instance.get();
}

FollowJavaScriptFeature::FollowJavaScriptFeature()
    : JavaScriptFeature(
          web::ContentWorld::kIsolatedWorld,
          {FeatureScript::CreateWithFilename(
              kRSSLinkScript,
              FeatureScript::InjectionTime::kDocumentStart,
              FeatureScript::TargetFrames::kMainFrame,
              FeatureScript::ReinjectionBehavior::kInjectOncePerWindow)}),
      weak_ptr_factory_(this) {}

FollowJavaScriptFeature::~FollowJavaScriptFeature() = default;

void FollowJavaScriptFeature::GetWebPageURLs(web::WebState* web_state,
                                             ResultCallback callback) {
  web::WebFrame* main_frame = GetWebFramesManager(web_state)->GetMainWebFrame();
  if (!main_frame) {
    std::move(callback).Run(nil);
    return;
  }
  CallJavaScriptFunction(
      main_frame, kGetRSSLinkFunction,
      /* parameters= */ {},
      base::BindOnce(&FollowJavaScriptFeature::HandleResponse,
                     weak_ptr_factory_.GetWeakPtr(),
                     web_state->GetLastCommittedURL(), std::move(callback)),
      base::Milliseconds(kJavaScriptExecutionTimeoutInMs));
}

void FollowJavaScriptFeature::HandleResponse(const GURL& url,
                                             ResultCallback callback,
                                             const base::Value* response) {
  NSMutableArray<NSURL*>* rss_urls = nil;
  if (response && response->is_list()) {
    for (const auto& link : response->GetList()) {
      if (link.is_string()) {
        NSURL* nsurl = net::NSURLWithGURL(GURL(link.GetString()));
        if (nsurl) {
          if (!rss_urls) {
            rss_urls = [[NSMutableArray alloc] init];
          }

          [rss_urls addObject:nsurl];
        }
      }
    }
  }

  WebPageURLs* web_page_urls =
      [[WebPageURLs alloc] initWithURL:net::NSURLWithGURL(url)
                               RSSURLs:rss_urls];

  std::move(callback).Run(web_page_urls);
}