// Copyright (c) 2016 The Chromium Embedded Framework 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 "tests/cefclient/browser/drm_test.h"

#include <algorithm>
#include <string>

#include "include/cef_parser.h"
#include "include/cef_web_plugin.h"
#include "tests/cefclient/browser/test_runner.h"

namespace client {
namespace drm_test {

namespace {

// Application-specific error codes.
const int kMessageFormatError = 1;
const int kCdmLoadError = 2;

const char kTestUrlPath[] = "/drm";
const char kWidevineCdmPathKey[] = "widevine_cdm_path";

// Callback executed once CDM registration is complete.
class CdmCallback : public CefRegisterCdmCallback {
 public:
  CdmCallback(CefRefPtr<CefMessageRouterBrowserSide::Callback> callback)
      : callback_(callback) {}

  void OnCdmRegistrationComplete(cef_cdm_registration_error_t result,
                                 const CefString& error_message) OVERRIDE {
    if (result == CEF_CDM_REGISTRATION_ERROR_NONE)
      callback_->Success("");
    else
      callback_->Failure(kCdmLoadError, error_message);
    callback_ = nullptr;
  }

 private:
  CefRefPtr<CefMessageRouterBrowserSide::Callback> callback_;

  IMPLEMENT_REFCOUNTING(CdmCallback);
  DISALLOW_COPY_AND_ASSIGN(CdmCallback);
};

// Handle messages in the browser process.
class Handler : public CefMessageRouterBrowserSide::Handler {
 public:
  Handler() {}

  // Called due to cefQuery execution in drm.html.
  virtual bool OnQuery(CefRefPtr<CefBrowser> browser,
                       CefRefPtr<CefFrame> frame,
                       int64 query_id,
                       const CefString& request,
                       bool persistent,
                       CefRefPtr<Callback> callback) OVERRIDE {
    // Only handle messages from the test URL.
    const std::string& url = frame->GetURL();
    if (!test_runner::IsTestURL(url, kTestUrlPath))
      return false;

    // Parse |request| as a JSON dictionary.
    CefRefPtr<CefDictionaryValue> request_dict = ParseJSON(request);
    if (!request_dict) {
      callback->Failure(kMessageFormatError, "Incorrect message format");
      return true;
    }

    // Verify the "widevine_cdm_path" key.
    if (!VerifyKey(request_dict, kWidevineCdmPathKey, VTYPE_STRING, callback))
      return true;

    const std::string& widevine_cdm_path =
        request_dict->GetString(kWidevineCdmPathKey);
    if (widevine_cdm_path.empty()) {
      callback->Failure(kMessageFormatError, "Empty widevine CDM path");
      return true;
    }

    // Register the Widvine CDM.
    CefRegisterWidevineCdm(widevine_cdm_path, new CdmCallback(callback));
    return true;
  }

 private:
  // Convert a JSON string to a dictionary value.
  static CefRefPtr<CefDictionaryValue> ParseJSON(const CefString& string) {
    CefRefPtr<CefValue> value = CefParseJSON(string, JSON_PARSER_RFC);
    if (value.get() && value->GetType() == VTYPE_DICTIONARY)
      return value->GetDictionary();
    return NULL;
  }

  // Verify that |key| exists in |dictionary| and has type |value_type|. Fails
  // |callback| and returns false on failure.
  static bool VerifyKey(CefRefPtr<CefDictionaryValue> dictionary,
                        const char* key,
                        cef_value_type_t value_type,
                        CefRefPtr<Callback> callback) {
    if (!dictionary->HasKey(key) || dictionary->GetType(key) != value_type) {
      callback->Failure(
          kMessageFormatError,
          "Missing or incorrectly formatted message key: " + std::string(key));
      return false;
    }
    return true;
  }
};

}  // namespace

void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) {
  handlers.insert(new Handler());
}

}  // namespace drm_test
}  // namespace client
