910e62b5创建于 1月15日历史提交
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/web_test/browser/leak_detector.h"

#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/json/json_writer.h"
#include "base/values.h"
#include "content/public/browser/render_process_host.h"

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 a target to count
// objects like RefCounted whose initial state is diffcult to estimate, we stop
// using hard-coded values. Instead, we need to load about:blank ahead of the
// web tests actually and initialize LeakDetector by the got values.
const int kInitialNumberOfLiveAudioNodes = 0;
const int kInitialNumberOfLiveDocuments = 1;
const int kInitialNumberOfLiveNodes = 4;
const int kInitialNumberOfLiveLayoutObjects = 3;
const int kInitialNumberOfLiveResources = 0;
const int kInitialNumberOfLiveFrames = 1;
const int kInitialNumberOfWorkerGlobalScopes = 0;
const int kInitialNumberOfLiveResourceFetchers = 1;
// Each Document has a ScriptRunner and a ScriptedAnimationController,
// which are ContextLifecycleStateObservers.
const int kInitialNumberOfLiveContextLifecycleStateObservers = 2;

// This includes not only about:blank's context but also ScriptRegexp (e.g.
// created by isValidEmailAddress in EmailInputType.cpp). The leak detector
// always creates the latter to stabilize the number of V8PerContextData
// objects.
const int kInitialNumberOfV8PerContextData = 2;

LeakDetector::LeakDetector() {
  previous_result_ = blink::mojom::LeakDetectionResult::New();
  previous_result_->number_of_live_audio_nodes = kInitialNumberOfLiveAudioNodes;
  previous_result_->number_of_live_documents = kInitialNumberOfLiveDocuments;
  previous_result_->number_of_live_nodes = kInitialNumberOfLiveNodes;
  previous_result_->number_of_live_layout_objects =
      kInitialNumberOfLiveLayoutObjects;
  previous_result_->number_of_live_resources = kInitialNumberOfLiveResources;
  previous_result_->number_of_live_context_lifecycle_state_observers =
      kInitialNumberOfLiveContextLifecycleStateObservers;
  previous_result_->number_of_live_frames = kInitialNumberOfLiveFrames;
  previous_result_->number_of_live_v8_per_context_data =
      kInitialNumberOfV8PerContextData;
  previous_result_->number_of_worker_global_scopes =
      kInitialNumberOfWorkerGlobalScopes;
  previous_result_->number_of_live_resource_fetchers =
      kInitialNumberOfLiveResourceFetchers;
}

LeakDetector::~LeakDetector() {}

void LeakDetector::TryLeakDetection(RenderProcessHost* process,
                                    ReportCallback callback) {
  callback_ = std::move(callback);

  if (!leak_detector_) {
    process->BindReceiver(leak_detector_.BindNewPipeAndPassReceiver());
    leak_detector_.set_disconnect_handler(base::BindOnce(
        &LeakDetector::OnLeakDetectorIsGone, base::Unretained(this)));
  }
  leak_detector_->PerformLeakDetection(base::BindOnce(
      &LeakDetector::OnLeakDetectionComplete, weak_factory_.GetWeakPtr()));
}

void LeakDetector::OnLeakDetectionComplete(
    blink::mojom::LeakDetectionResultPtr result) {
  LeakDetectionReport report;
  report.leaked = false;
  base::Value::Dict detail;

  if (previous_result_ && !result.is_null()) {
    if (previous_result_->number_of_live_audio_nodes <
        result->number_of_live_audio_nodes) {
      base::Value::List list;
      list.Append(
          static_cast<int>(previous_result_->number_of_live_audio_nodes));
      list.Append(static_cast<int>(result->number_of_live_audio_nodes));
      detail.Set("numberOfLiveAudioNodes", std::move(list));
    }
    if (previous_result_->number_of_live_documents <
        result->number_of_live_documents) {
      base::Value::List list;
      list.Append(static_cast<int>(previous_result_->number_of_live_documents));
      list.Append(static_cast<int>(result->number_of_live_documents));
      detail.Set("numberOfLiveDocuments", std::move(list));
    }
    if (previous_result_->number_of_live_nodes < result->number_of_live_nodes) {
      base::Value::List list;
      list.Append(static_cast<int>(previous_result_->number_of_live_nodes));
      list.Append(static_cast<int>(result->number_of_live_nodes));
      detail.Set("numberOfLiveNodes", std::move(list));
    }
    if (previous_result_->number_of_live_layout_objects <
        result->number_of_live_layout_objects) {
      base::Value::List list;
      list.Append(
          static_cast<int>(previous_result_->number_of_live_layout_objects));
      list.Append(static_cast<int>(result->number_of_live_layout_objects));
      detail.Set("numberOfLiveLayoutObjects", std::move(list));
    }
    // Resources associated with User Agent CSS should be excluded from leak
    // detection as they are persisted through page navigation.
    if (previous_result_->number_of_live_resources <
        (result->number_of_live_resources -
         result->number_of_live_ua_css_resources)) {
      base::Value::List list;
      list.Append(static_cast<int>(previous_result_->number_of_live_resources));
      list.Append(static_cast<int>(result->number_of_live_resources));
      detail.Set("numberOfLiveResources", std::move(list));
    }
    if (previous_result_->number_of_live_context_lifecycle_state_observers <
        result->number_of_live_context_lifecycle_state_observers) {
      base::Value::List list;
      list.Append(static_cast<int>(
          previous_result_->number_of_live_context_lifecycle_state_observers));
      list.Append(static_cast<int>(
          result->number_of_live_context_lifecycle_state_observers));
      detail.Set("numberOfLiveContextLifecycleStateObservers", std::move(list));
    }
    if (previous_result_->number_of_live_frames <
        result->number_of_live_frames) {
      base::Value::List list;
      list.Append(static_cast<int>(previous_result_->number_of_live_frames));
      list.Append(static_cast<int>(result->number_of_live_frames));
      detail.Set("numberOfLiveFrames", std::move(list));
    }
    if (previous_result_->number_of_live_v8_per_context_data <
        result->number_of_live_v8_per_context_data) {
      base::Value::List list;
      list.Append(static_cast<int>(
          previous_result_->number_of_live_v8_per_context_data));
      list.Append(static_cast<int>(result->number_of_live_v8_per_context_data));
      detail.Set("numberOfLiveV8PerContextData", std::move(list));
    }
    if (previous_result_->number_of_worker_global_scopes <
        result->number_of_worker_global_scopes) {
      base::Value::List list;
      list.Append(
          static_cast<int>(previous_result_->number_of_worker_global_scopes));
      list.Append(static_cast<int>(result->number_of_worker_global_scopes));
      detail.Set("numberOfWorkerGlobalScopes", std::move(list));
    }
    if (previous_result_->number_of_live_resource_fetchers <
        result->number_of_live_resource_fetchers) {
      base::Value::List list;
      list.Append(
          static_cast<int>(previous_result_->number_of_live_resource_fetchers));
      list.Append(static_cast<int>(result->number_of_live_resource_fetchers));
      detail.Set("numberOfLiveResourceFetchers", std::move(list));
    }
  }

  if (!detail.empty()) {
    report.detail = base::WriteJson(detail).value_or("");
    report.leaked = true;
  }

  if (!result.is_null()) {
    previous_result_ = std::move(result);
  }
  std::move(callback_).Run(report);
}

void LeakDetector::OnLeakDetectorIsGone() {
  leak_detector_.reset();
  if (!callback_)
    return;
  LeakDetectionReport report;
  report.leaked = false;
  std::move(callback_).Run(report);
}

}  // namespace content