// Copyright 2014 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 "content/shell/renderer/leak_detector.h"

#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/values.h"
#include "content/shell/renderer/webkit_test_runner.h"
#include "third_party/WebKit/public/web/WebLeakDetector.h"

using blink::WebLeakDetector;

namespace content {

// The initial states of the DOM objects at about:blank. The four nodes are a
// Document, a HTML, a HEAD and a BODY.
//
// TODO(hajimehoshi): Now these are hard-corded. If we add target to count like
// RefCoutned objects whose initial state is diffcult to estimate, we stop using
// hard-coded values. Instead, we need to load about:blank ahead of the layout
// tests actually and initialize LeakDetector by the got values.
const int kInitialNumberOfLiveAudioNodes = 0;
const int kInitialNumberOfLiveDocuments = 1;
const int kInitialNumberOfLiveNodes = 4;
const int kInitialNumberOfLiveRenderObjects = 3;
const int kInitialNumberOfLiveResources = 0;

LeakDetector::LeakDetector(WebKitTestRunner* test_runner)
    : test_runner_(test_runner),
      web_leak_detector_(blink::WebLeakDetector::create(this)) {
  previous_result_.numberOfLiveAudioNodes = kInitialNumberOfLiveAudioNodes;
  previous_result_.numberOfLiveDocuments = kInitialNumberOfLiveDocuments;
  previous_result_.numberOfLiveNodes = kInitialNumberOfLiveNodes;
  previous_result_.numberOfLiveRenderObjects =
      kInitialNumberOfLiveRenderObjects;
  previous_result_.numberOfLiveResources = kInitialNumberOfLiveResources;
}

LeakDetector::~LeakDetector() {
}

void LeakDetector::TryLeakDetection(blink::WebLocalFrame* frame) {
  web_leak_detector_->collectGarbageAndGetDOMCounts(frame);
}

void LeakDetector::onLeakDetectionComplete(
    const WebLeakDetectorClient::Result& result) {
  LeakDetectionResult report;
  report.leaked = false;
  base::DictionaryValue detail;

  if (previous_result_.numberOfLiveAudioNodes < result.numberOfLiveAudioNodes) {
    base::ListValue* list = new base::ListValue();
    list->AppendInteger(previous_result_.numberOfLiveAudioNodes);
    list->AppendInteger(result.numberOfLiveAudioNodes);
    detail.Set("numberOfLiveAudioNodes", list);
  }
  if (previous_result_.numberOfLiveDocuments < result.numberOfLiveDocuments) {
    base::ListValue* list = new base::ListValue();
    list->AppendInteger(previous_result_.numberOfLiveDocuments);
    list->AppendInteger(result.numberOfLiveDocuments);
    detail.Set("numberOfLiveDocuments", list);
  }
  if (previous_result_.numberOfLiveNodes < result.numberOfLiveNodes) {
    base::ListValue* list = new base::ListValue();
    list->AppendInteger(previous_result_.numberOfLiveNodes);
    list->AppendInteger(result.numberOfLiveNodes);
    detail.Set("numberOfLiveNodes", list);
  }
  if (previous_result_.numberOfLiveRenderObjects <
      result.numberOfLiveRenderObjects) {
    base::ListValue* list = new base::ListValue();
    list->AppendInteger(previous_result_.numberOfLiveRenderObjects);
    list->AppendInteger(result.numberOfLiveRenderObjects);
    detail.Set("numberOfLiveRenderObjects", list);
  }
  if (previous_result_.numberOfLiveResources < result.numberOfLiveResources) {
    base::ListValue* list = new base::ListValue();
    list->AppendInteger(previous_result_.numberOfLiveResources);
    list->AppendInteger(result.numberOfLiveResources);
    detail.Set("numberOfLiveResources", list);
  }

  if (!detail.empty()) {
    std::string detail_str;
    base::JSONWriter::Write(&detail, &detail_str);
    report.detail = detail_str;
    report.leaked = true;
  }

  previous_result_ = result;
  test_runner_->ReportLeakDetectionResult(report);
}

}  // namespace content
