// Copyright 2020 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/web/js_messaging/java_script_feature_manager.h"

#import <WebKit/WebKit.h>

#import "base/check_op.h"
#import "base/ios/ios_util.h"
#import "base/notreached.h"
#import "ios/web/public/browser_state.h"
#import "ios/web/public/js_messaging/java_script_feature.h"
#import "ios/web/public/js_messaging/java_script_feature_util.h"

namespace {

// Key used to associate a JavaScriptFeatureManager instances with a
// BrowserState.
const char kWebJavaScriptFeatureManagerKeyName[] =
    "web_java_script_feature_manager";

}  // namespace

namespace web {

JavaScriptFeatureManager::JavaScriptFeatureManager(BrowserState* browser_state)
    : browser_state_(browser_state) {}
JavaScriptFeatureManager::~JavaScriptFeatureManager() {}

JavaScriptFeatureManager* JavaScriptFeatureManager::FromBrowserState(
    BrowserState* browser_state) {
  DCHECK(browser_state);

  JavaScriptFeatureManager* feature_manager =
      static_cast<JavaScriptFeatureManager*>(
          browser_state->GetUserData(kWebJavaScriptFeatureManagerKeyName));
  if (!feature_manager) {
    feature_manager = new JavaScriptFeatureManager(browser_state);
    browser_state->SetUserData(kWebJavaScriptFeatureManagerKeyName,
                               base::WrapUnique(feature_manager));
  }
  return feature_manager;
}

JavaScriptContentWorld*
JavaScriptFeatureManager::GetContentWorldForBrowserState(
    ContentWorld content_world,
    BrowserState* browser_state) {
  DCHECK(browser_state);
  CHECK_NE(content_world, ContentWorld::kAllContentWorlds);

  JavaScriptFeatureManager* feature_manager = FromBrowserState(browser_state);
  switch (content_world) {
    case ContentWorld::kPageContentWorld:
      return feature_manager->page_content_world_.get();
    case ContentWorld::kIsolatedWorld:
      return feature_manager->isolated_world_.get();
    case ContentWorld::kAllContentWorlds:
      NOTREACHED();
  }
}

void JavaScriptFeatureManager::ConfigureFeatures(
    std::vector<JavaScriptFeature*> features) {
  page_content_world_ = std::make_unique<JavaScriptContentWorld>(
      browser_state_, WKContentWorld.pageWorld);

  isolated_world_ = std::make_unique<JavaScriptContentWorld>(
      browser_state_, WKContentWorld.defaultClientWorld);

  for (JavaScriptFeature* feature : features) {
    switch (feature->GetSupportedContentWorld()) {
      case ContentWorld::kAllContentWorlds:
        isolated_world_->AddFeature(feature);
        page_content_world_->AddFeature(feature);
        break;
      case ContentWorld::kIsolatedWorld:
        isolated_world_->AddFeature(feature);
        break;
      case ContentWorld::kPageContentWorld:
        page_content_world_->AddFeature(feature);
        break;
    }
  }
}

JavaScriptContentWorld* JavaScriptFeatureManager::GetContentWorldForFeature(
    JavaScriptFeature* feature) {
  if (isolated_world_->HasFeature(feature)) {
    return isolated_world_.get();
  }
  if (page_content_world_->HasFeature(feature)) {
    return page_content_world_.get();
  }
  return nullptr;
}

std::vector<JavaScriptContentWorld*>
JavaScriptFeatureManager::GetAllContentWorlds() {
  return {isolated_world_.get(), page_content_world_.get()};
}

std::vector<ContentWorld> JavaScriptFeatureManager::GetAllContentWorldEnums() {
  // Return these from WKContentWorld directly instead of from
  // JavaScriptContentWorld instances because JavaScriptContentWorld are not
  // created until after `ConfigureFeatures`.
  return {ContentWorld::kPageContentWorld, ContentWorld::kIsolatedWorld};
}

}  // namespace web
