Don't crash when SimpleCache index is corrupt.
[chromium-blink-merge.git] / chrome / browser / page_cycler / page_cycler.cc
blob4912fe7f74dcd841755f32914533a5ff5b59b16f
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.
5 #include "chrome/browser/page_cycler/page_cycler.h"
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/files/file_path.h"
10 #include "base/message_loop.h"
11 #include "base/process_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/app/chrome_command_ids.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_commands.h"
18 #include "chrome/browser/ui/browser_list.h"
19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
20 #include "chrome/common/chrome_notification_types.h"
21 #include "chrome/test/base/chrome_process_util.h"
22 #include "chrome/test/perf/perf_test.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/common/url_constants.h"
29 using content::NavigationController;
30 using content::OpenURLParams;
31 using content::Referrer;
32 using content::WebContents;
34 PageCycler::PageCycler(Browser* browser,
35 const base::FilePath& urls_file)
36 : content::WebContentsObserver(
37 browser->tab_strip_model()->GetActiveWebContents()),
38 browser_(browser),
39 urls_file_(urls_file),
40 url_index_(0),
41 aborted_(false) {
42 BrowserList::AddObserver(this);
43 AddRef(); // Balanced in Finish()/Abort() (only one should be called).
46 PageCycler::~PageCycler() {
49 bool PageCycler::IsLoadCallbackValid(const GURL& validated_url,
50 bool is_main_frame) {
51 // If |url_index_| is equal to zero, that means that this was called before
52 // LoadNextURL() - this can happen at startup, loading the new tab page; or
53 // if the user specified a bad url as the final url in the list. In these
54 // cases, do not report success or failure, and load the next page.
55 if (!url_index_) {
56 LoadNextURL();
57 return false;
59 return (is_main_frame &&
60 validated_url.spec() != content::kUnreachableWebDataURL);
63 void PageCycler::DidFinishLoad(int64 frame_id,
64 const GURL& validated_url,
65 bool is_main_frame,
66 content::RenderViewHost* render_view_host) {
67 if (IsLoadCallbackValid(validated_url, is_main_frame))
68 LoadSucceeded();
71 void PageCycler::DidFailProvisionalLoad(
72 int64 frame_id,
73 bool is_main_frame,
74 const GURL& validated_url,
75 int error_code,
76 const string16& error_description,
77 content::RenderViewHost* render_view_host) {
78 if (IsLoadCallbackValid(validated_url, is_main_frame))
79 LoadFailed(validated_url, error_description);
82 void PageCycler::Run() {
83 if (browser_)
84 content::BrowserThread::PostBlockingPoolTask(
85 FROM_HERE,
86 base::Bind(&PageCycler::ReadURLsOnBackgroundThread, this));
89 void PageCycler::ReadURLsOnBackgroundThread() {
90 CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
92 std::string file_contents;
93 std::vector<std::string> url_strings;
95 CHECK(file_util::PathExists(urls_file_)) << urls_file_.value();
96 file_util::ReadFileToString(urls_file_, &file_contents);
97 base::SplitStringAlongWhitespace(file_contents, &url_strings);
99 if (!url_strings.size()) {
100 #if defined(OS_POSIX)
101 error_.append(ASCIIToUTF16("Page Cycler: No URLs in given file: " +
102 urls_file_.value()));
103 #elif defined(OS_WIN)
104 error_.append(ASCIIToUTF16("Page Cycler: No URLs in given file: "))
105 .append(urls_file_.value());
106 #endif // OS_WIN
109 for (std::vector<std::string>::const_iterator iter = url_strings.begin();
110 iter != url_strings.end(); ++iter) {
111 GURL gurl(*iter);
112 if (!gurl.is_valid())
113 error_.append(ASCIIToUTF16("Omitting invalid URL: " + *iter + ".\n"));
114 // Since we don't count kUnreachableWebData as a valid load, we don't want
115 // the user to specify this as one of the pages to visit.
116 else if (*iter == content::kUnreachableWebDataURL) {
117 error_.append(ASCIIToUTF16(
118 "Chrome error pages are not allowed as urls. Omitting url: " +
119 *iter + ".\n"));
120 } else {
121 urls_.push_back(gurl);
125 content::BrowserThread::PostTask(content::BrowserThread::UI,
126 FROM_HERE,
127 base::Bind(&PageCycler::BeginCycle, this));
130 void PageCycler::BeginCycle() {
131 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
133 CHECK(browser_);
134 // Upon launch, Chrome will automatically load the newtab page. This can
135 // result in the browser being in a state of loading when PageCycler is ready
136 // to start. Instead of interrupting the load, we wait for it to finish, and
137 // will call LoadNextURL() from DidFinishLoad() or DidFailProvisionalLoad().
138 if (browser_->tab_strip_model()->GetActiveWebContents()->IsLoading())
139 return;
140 LoadNextURL();
143 void PageCycler::LoadNextURL() {
144 CHECK(browser_);
145 if (url_index_ >= urls_.size()) {
146 content::BrowserThread::PostBlockingPoolTask(
147 FROM_HERE,
148 base::Bind(&PageCycler::PrepareResultsOnBackgroundThread, this));
149 return;
151 if (url_index_) {
152 timings_string_.append(", ");
153 urls_string_.append(", ");
155 urls_string_.append(urls_[url_index_].spec());
156 initial_time_ = base::TimeTicks::HighResNow();
157 OpenURLParams params(urls_[url_index_],
158 Referrer(),
159 CURRENT_TAB,
160 content::PAGE_TRANSITION_TYPED,
161 false);
162 ++url_index_;
163 browser_->OpenURL(params);
166 void PageCycler::LoadSucceeded() {
167 base::TimeDelta time_elapsed =
168 (base::TimeTicks::HighResNow() - initial_time_) / 1000.0;
169 timings_string_.append(base::Int64ToString(time_elapsed.ToInternalValue()));
170 LoadNextURL();
173 void PageCycler::LoadFailed(const GURL& url,
174 const string16& error_description) {
175 error_.append(ASCIIToUTF16("Failed to load the page at: " +
176 url.spec() + ": ")).append(error_description).
177 append(ASCIIToUTF16("\n"));
178 base::TimeDelta time_elapsed =
179 (base::TimeTicks::HighResNow() - initial_time_) / 1000.0;
180 timings_string_.append(base::Int64ToString(time_elapsed.ToInternalValue()) +
181 (" (failed)"));
182 LoadNextURL();
185 void PageCycler::PrepareResultsOnBackgroundThread() {
186 CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
187 std::string output;
188 if (!stats_file_.empty()) {
189 #if defined(OS_POSIX)
190 base::ProcessId pid = base::GetParentProcessId(base::GetCurrentProcId());
191 #elif defined(OS_WIN)
192 base::ProcessId pid = base::GetCurrentProcId();
193 #endif // OS_WIN
194 ChromeProcessList chrome_processes(GetRunningChromeProcesses(pid));
195 output += perf_test::MemoryUsageInfoToString(
196 std::string(), chrome_processes, pid);
197 output +=
198 perf_test::IOPerfInfoToString(std::string(), chrome_processes, pid);
199 output += perf_test::SystemCommitChargeToString(
200 std::string(), base::GetSystemCommitCharge(), false);
201 output.append("Pages: [" + urls_string_ + "]\n");
202 output.append("*RESULT times: t_ref= [" + timings_string_ + "] ms\n");
204 WriteResultsOnBackgroundThread(output);
207 void PageCycler::WriteResultsOnBackgroundThread(const std::string& output) {
208 CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
210 if (!output.empty()) {
211 CHECK(!stats_file_.empty());
212 if (file_util::PathExists(stats_file_)) {
213 VLOG(1) << "PageCycler: Previous stats file found; appending.";
214 file_util::AppendToFile(stats_file_, output.c_str(), output.size());
215 } else {
216 file_util::WriteFile(stats_file_, output.c_str(), output.size());
219 if (!errors_file_.empty()) {
220 if (!error_.empty()) {
221 file_util::WriteFile(errors_file_, UTF16ToUTF8(error_).c_str(),
222 error_.size());
223 } else if (file_util::PathExists(errors_file_)) {
224 // If there is an old error file, delete it to avoid confusion.
225 file_util::Delete(errors_file_, false);
228 if (aborted_) {
229 content::BrowserThread::PostTask(content::BrowserThread::UI,
230 FROM_HERE,
231 base::Bind(&PageCycler::Abort, this));
232 } else {
233 content::BrowserThread::PostTask(content::BrowserThread::UI,
234 FROM_HERE,
235 base::Bind(&PageCycler::Finish, this));
239 void PageCycler::Finish() {
240 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
241 BrowserList::RemoveObserver(this);
242 browser_->OnWindowClosing();
243 chrome::ExecuteCommand(browser_, IDC_EXIT);
244 Release(); // Balanced in PageCycler constructor;
245 // (only one of Finish/Abort should be called).
248 void PageCycler::Abort() {
249 browser_ = NULL;
250 BrowserList::RemoveObserver(this);
251 Release(); // Balanced in PageCycler constructor;
252 // (only one of Finish/Abort should be called).
255 void PageCycler::OnBrowserAdded(Browser* browser) {}
257 void PageCycler::OnBrowserRemoved(Browser* browser) {
258 if (browser == browser_) {
259 aborted_ = true;
260 error_.append(ASCIIToUTF16(
261 "Browser was closed before the run was completed."));
262 DLOG(WARNING) <<
263 "Page Cycler: browser was closed before the run was completed.";
264 content::BrowserThread::PostBlockingPoolTask(
265 FROM_HERE,
266 base::Bind(&PageCycler::PrepareResultsOnBackgroundThread, this));