1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/webui/profiler_ui.h"
6
7#include <string>
8
9// When testing the javacript code, it is cumbersome to have to keep
10// re-building the resouces package and reloading the browser. To solve
11// this, enable the following flag to read the webapp's source files
12// directly off disk, so all you have to do is refresh the page to
13// test the modifications.
14// #define USE_SOURCE_FILES_DIRECTLY
15
16#include "base/bind.h"
17#include "base/memory/scoped_ptr.h"
18#include "base/strings/string_util.h"
19#include "base/tracked_objects.h"
20#include "base/values.h"
21#include "chrome/browser/profiles/profile.h"
22#include "chrome/browser/task_profiler/task_profiler_data_serializer.h"
23#include "chrome/common/url_constants.h"
24#include "components/metrics/profiler/tracking_synchronizer.h"
25#include "content/public/browser/browser_thread.h"
26#include "content/public/browser/url_data_source.h"
27#include "content/public/browser/web_contents.h"
28#include "content/public/browser/web_ui.h"
29#include "content/public/browser/web_ui_data_source.h"
30#include "content/public/browser/web_ui_message_handler.h"
31#include "grit/browser_resources.h"
32
33#ifdef USE_SOURCE_FILES_DIRECTLY
34#include "base/base_paths.h"
35#include "base/files/file_util.h"
36#include "base/memory/ref_counted_memory.h"
37#include "base/path_service.h"
38#endif  //  USE_SOURCE_FILES_DIRECTLY
39
40using content::BrowserThread;
41using content::WebContents;
42using content::WebUIMessageHandler;
43using metrics::TrackingSynchronizer;
44
45namespace {
46
47#ifdef USE_SOURCE_FILES_DIRECTLY
48
49class ProfilerWebUIDataSource : public content::URLDataSource {
50 public:
51  ProfilerWebUIDataSource() {
52  }
53
54 protected:
55  // content::URLDataSource implementation.
56  virtual std::string GetSource() OVERRIDE {
57    return chrome::kChromeUIProfilerHost;
58  }
59
60  virtual std::string GetMimeType(const std::string& path) const OVERRIDE {
61    if (EndsWith(path, ".js", false))
62      return "application/javascript";
63    return "text/html";
64  }
65
66  virtual void StartDataRequest(
67      const std::string& path,
68      bool is_incognito,
69      const content::URLDataSource::GotDataCallback& callback) OVERRIDE {
70    base::FilePath base_path;
71    PathService::Get(base::DIR_SOURCE_ROOT, &base_path);
72    base_path = base_path.AppendASCII("chrome");
73    base_path = base_path.AppendASCII("browser");
74    base_path = base_path.AppendASCII("resources");
75    base_path = base_path.AppendASCII("profiler");
76
77    // If no resource was specified, default to profiler.html.
78    std::string filename = path.empty() ? "profiler.html" : path;
79
80    base::FilePath file_path;
81    file_path = base_path.AppendASCII(filename);
82
83    // Read the file synchronously and send it as the response.
84    base::ThreadRestrictions::ScopedAllowIO allow;
85    std::string file_contents;
86    if (!base::ReadFileToString(file_path, &file_contents))
87      LOG(ERROR) << "Couldn't read file: " << file_path.value();
88    scoped_refptr<base::RefCountedString> response =
89        new base::RefCountedString();
90    response->data() = file_contents;
91    callback.Run(response);
92  }
93
94 private:
95  DISALLOW_COPY_AND_ASSIGN(ProfilerWebUIDataSource);
96};
97
98#else  // USE_SOURCE_FILES_DIRECTLY
99
100content::WebUIDataSource* CreateProfilerHTMLSource() {
101  content::WebUIDataSource* source =
102      content::WebUIDataSource::Create(chrome::kChromeUIProfilerHost);
103
104  source->SetJsonPath("strings.js");
105  source->AddResourcePath("profiler.js", IDR_PROFILER_JS);
106  source->SetDefaultResource(IDR_PROFILER_HTML);
107  return source;
108}
109
110#endif
111
112// This class receives javascript messages from the renderer.
113// Note that the WebUI infrastructure runs on the UI thread, therefore all of
114// this class's methods are expected to run on the UI thread.
115class ProfilerMessageHandler : public WebUIMessageHandler {
116 public:
117  ProfilerMessageHandler() {}
118
119  // WebUIMessageHandler implementation.
120  virtual void RegisterMessages() OVERRIDE;
121
122  // Messages.
123  void OnGetData(const base::ListValue* list);
124  void OnResetData(const base::ListValue* list);
125
126 private:
127  DISALLOW_COPY_AND_ASSIGN(ProfilerMessageHandler);
128};
129
130void ProfilerMessageHandler::RegisterMessages() {
131  DCHECK_CURRENTLY_ON(BrowserThread::UI);
132
133  web_ui()->RegisterMessageCallback("getData",
134      base::Bind(&ProfilerMessageHandler::OnGetData, base::Unretained(this)));
135  web_ui()->RegisterMessageCallback("resetData",
136      base::Bind(&ProfilerMessageHandler::OnResetData,
137                 base::Unretained(this)));
138}
139
140void ProfilerMessageHandler::OnGetData(const base::ListValue* list) {
141  ProfilerUI* profiler_ui = static_cast<ProfilerUI*>(web_ui()->GetController());
142  profiler_ui->GetData();
143}
144
145void ProfilerMessageHandler::OnResetData(const base::ListValue* list) {
146  tracked_objects::ThreadData::ResetAllThreadData();
147}
148
149}  // namespace
150
151ProfilerUI::ProfilerUI(content::WebUI* web_ui)
152    : WebUIController(web_ui),
153      weak_ptr_factory_(this) {
154  web_ui->AddMessageHandler(new ProfilerMessageHandler());
155
156  // Set up the chrome://profiler/ source.
157  Profile* profile = Profile::FromWebUI(web_ui);
158#if defined(USE_SOURCE_FILES_DIRECTLY)
159  content::URLDataSource::Add(profile, new ProfilerWebUIDataSource);
160#else
161  content::WebUIDataSource::Add(profile, CreateProfilerHTMLSource());
162#endif
163}
164
165ProfilerUI::~ProfilerUI() {
166}
167
168void ProfilerUI::GetData() {
169  TrackingSynchronizer::FetchProfilerDataAsynchronously(
170      weak_ptr_factory_.GetWeakPtr());
171}
172
173void ProfilerUI::ReceivedProfilerData(
174    const tracked_objects::ProcessDataSnapshot& profiler_data,
175    int process_type) {
176  // Serialize the data to JSON.
177  base::DictionaryValue json_data;
178  task_profiler::TaskProfilerDataSerializer::ToValue(profiler_data,
179                                                     process_type,
180                                                     &json_data);
181
182  // Send the data to the renderer.
183  web_ui()->CallJavascriptFunction("g_browserBridge.receivedData", json_data);
184}
185