// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/renderer/plugins/plugin_uma.h"

#include <algorithm>
#include <cstring>

#include "base/metrics/histogram.h"
#include "base/strings/string_util.h"
#include "content/public/common/content_constants.h"
#include "third_party/widevine/cdm/widevine_cdm_common.h"

namespace {

// String we will use to convert mime type to plugin type.
const char kWindowsMediaPlayerType[] = "application/x-mplayer2";
const char kSilverlightTypePrefix[] = "application/x-silverlight";
const char kRealPlayerTypePrefix[] = "audio/x-pn-realaudio";
const char kJavaTypeSubstring[] = "application/x-java-applet";
const char kQuickTimeType[] = "video/quicktime";

// Arrays containing file extensions connected with specific plugins.
// Note: THE ARRAYS MUST BE SORTED BECAUSE BINARY SEARCH IS USED ON THEM!
const char* kWindowsMediaPlayerExtensions[] = {".asx"};

const char* kRealPlayerExtensions[] = {".ra",  ".ram", ".rm",
                                       ".rmm", ".rmp", ".rpm"};

const char* kQuickTimeExtensions[] = {".moov", ".mov", ".qif",
                                      ".qt",   ".qti", ".qtif"};

const char* kShockwaveFlashExtensions[] = {".spl", ".swf"};

}  // namespace.

class UMASenderImpl : public PluginUMAReporter::UMASender {
  virtual void SendPluginUMA(
      PluginUMAReporter::ReportType report_type,
      PluginUMAReporter::PluginType plugin_type) OVERRIDE;
};

void UMASenderImpl::SendPluginUMA(PluginUMAReporter::ReportType report_type,
                                  PluginUMAReporter::PluginType plugin_type) {
  // UMA_HISTOGRAM_ENUMERATION requires constant histogram name. Use string
  // constants explicitly instead of trying to use variables for names.
  switch (report_type) {
    case PluginUMAReporter::MISSING_PLUGIN:
      UMA_HISTOGRAM_ENUMERATION("Plugin.MissingPlugins",
                                plugin_type,
                                PluginUMAReporter::PLUGIN_TYPE_MAX);
      break;
    case PluginUMAReporter::DISABLED_PLUGIN:
      UMA_HISTOGRAM_ENUMERATION("Plugin.DisabledPlugins",
                                plugin_type,
                                PluginUMAReporter::PLUGIN_TYPE_MAX);
      break;
    default:
      NOTREACHED();
  }
}

// static.
PluginUMAReporter* PluginUMAReporter::GetInstance() {
  return Singleton<PluginUMAReporter>::get();
}

void PluginUMAReporter::ReportPluginMissing(const std::string& plugin_mime_type,
                                            const GURL& plugin_src) {
  report_sender_->SendPluginUMA(MISSING_PLUGIN,
                                GetPluginType(plugin_mime_type, plugin_src));
}

void PluginUMAReporter::ReportPluginDisabled(
    const std::string& plugin_mime_type,
    const GURL& plugin_src) {
  report_sender_->SendPluginUMA(DISABLED_PLUGIN,
                                GetPluginType(plugin_mime_type, plugin_src));
}

PluginUMAReporter::PluginUMAReporter() : report_sender_(new UMASenderImpl()) {}

PluginUMAReporter::~PluginUMAReporter() {}

// static.
bool PluginUMAReporter::CompareCStrings(const char* first, const char* second) {
  return strcmp(first, second) < 0;
}

bool PluginUMAReporter::CStringArrayContainsCString(const char** array,
                                                    size_t array_size,
                                                    const char* str) {
  return std::binary_search(array, array + array_size, str, CompareCStrings);
}

void PluginUMAReporter::ExtractFileExtension(const GURL& src,
                                             std::string* extension) {
  std::string extension_file_path(src.ExtractFileName());
  if (extension_file_path.empty())
    extension_file_path = src.host();

  size_t last_dot = extension_file_path.find_last_of('.');
  if (last_dot != std::string::npos) {
    *extension = extension_file_path.substr(last_dot);
  } else {
    extension->clear();
  }

  StringToLowerASCII(extension);
}

PluginUMAReporter::PluginType PluginUMAReporter::GetPluginType(
    const std::string& plugin_mime_type,
    const GURL& plugin_src) {
  // If we know plugin's mime type, we use it to determine plugin's type. Else,
  // we try to determine plugin type using plugin source's extension.
  if (!plugin_mime_type.empty())
    return MimeTypeToPluginType(StringToLowerASCII(plugin_mime_type));

  return SrcToPluginType(plugin_src);
}

PluginUMAReporter::PluginType PluginUMAReporter::SrcToPluginType(
    const GURL& src) {
  std::string file_extension;
  ExtractFileExtension(src, &file_extension);
  if (CStringArrayContainsCString(kWindowsMediaPlayerExtensions,
                                  arraysize(kWindowsMediaPlayerExtensions),
                                  file_extension.c_str())) {
    return WINDOWS_MEDIA_PLAYER;
  }

  if (CStringArrayContainsCString(kQuickTimeExtensions,
                                  arraysize(kQuickTimeExtensions),
                                  file_extension.c_str())) {
    return QUICKTIME;
  }

  if (CStringArrayContainsCString(kRealPlayerExtensions,
                                  arraysize(kRealPlayerExtensions),
                                  file_extension.c_str())) {
    return REALPLAYER;
  }

  if (CStringArrayContainsCString(kShockwaveFlashExtensions,
                                  arraysize(kShockwaveFlashExtensions),
                                  file_extension.c_str())) {
    return SHOCKWAVE_FLASH;
  }

  return UNSUPPORTED_EXTENSION;
}

PluginUMAReporter::PluginType PluginUMAReporter::MimeTypeToPluginType(
    const std::string& mime_type) {
  if (mime_type == kWindowsMediaPlayerType)
    return WINDOWS_MEDIA_PLAYER;

  size_t prefix_length = strlen(kSilverlightTypePrefix);
  if (strncmp(mime_type.c_str(), kSilverlightTypePrefix, prefix_length) == 0)
    return SILVERLIGHT;

  prefix_length = strlen(kRealPlayerTypePrefix);
  if (strncmp(mime_type.c_str(), kRealPlayerTypePrefix, prefix_length) == 0)
    return REALPLAYER;

  if (strstr(mime_type.c_str(), kJavaTypeSubstring))
    return JAVA;

  if (mime_type == kQuickTimeType)
    return QUICKTIME;

  if (mime_type == content::kBrowserPluginMimeType)
    return BROWSER_PLUGIN;

  if (mime_type == content::kFlashPluginSwfMimeType ||
      mime_type == content::kFlashPluginSplMimeType) {
    return SHOCKWAVE_FLASH;
  }

#if defined(ENABLE_PEPPER_CDMS)
  if (mime_type == kWidevineCdmPluginMimeType)
    return WIDEVINE_CDM;
#endif

  return UNSUPPORTED_MIMETYPE;
}