OLD | NEW |
---|---|
(Empty) | |
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/page_cycler/page_cycler.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/file_path.h" | |
9 #include "base/file_util.h" | |
10 #include "base/message_loop.h" | |
11 #include "base/process_util.h" | |
12 #include "base/string_split.h" | |
13 #include "base/string_number_conversions.h" | |
14 #include "base/utf_string_conversions.h" | |
15 #include "chrome/common/chrome_notification_types.h" | |
16 #include "chrome/test/base/chrome_process_util.h" | |
17 #include "chrome/test/perf/perf_test.h" | |
18 #include "content/public/browser/browser_thread.h" | |
19 #include "content/public/browser/notification_source.h" | |
20 #include "content/public/browser/notification_service.h" | |
21 #include "content/public/browser/notification_details.h" | |
22 #include "content/public/browser/notification_types.h" | |
23 #include "content/public/browser/web_contents.h" | |
24 #include "content/public/common/url_constants.h" | |
25 | |
26 using content::NavigationController; | |
27 using content::OpenURLParams; | |
28 using content::Referrer; | |
29 using content::WebContents; | |
30 | |
31 PageCycler::PageCycler(Browser* browser, | |
32 FilePath urls_file, | |
33 FilePath errors_file) | |
34 : content::WebContentsObserver(browser->GetSelectedWebContents()), | |
35 browser_(browser), | |
36 urls_file_(urls_file), | |
37 errors_file_(errors_file), | |
38 url_index_(0), | |
39 iterations_(0) { | |
40 Init(); | |
41 } | |
42 | |
43 PageCycler::PageCycler(Browser* browser, | |
44 FilePath urls_file, | |
45 FilePath errors_file, | |
46 FilePath stats_file) | |
47 : content::WebContentsObserver(browser->GetSelectedWebContents()), | |
48 browser_(browser), | |
49 urls_file_(urls_file), | |
50 errors_file_(errors_file), | |
51 stats_file_(stats_file), | |
52 url_index_(0), | |
53 iterations_(0) { | |
54 Init(); | |
55 } | |
56 | |
57 PageCycler::~PageCycler() { | |
58 } | |
59 | |
60 void PageCycler::Init() { | |
61 BrowserList::AddObserver(this); | |
62 AddRef(); // Balanced in Finish()/Abort() (only one should be called). | |
63 } | |
64 | |
65 void PageCycler::DidFinishLoad(int64 frame_id, | |
66 const GURL& validated_url, | |
67 bool is_main_frame) { | |
68 // In the event of certain load failures, chrome will load the error page | |
69 // kUnreachableWebDataURL. We do not want to count this as a load success or | |
70 // failure, because it will throw off the count. | |
71 if (is_main_frame && validated_url.spec() != chrome::kUnreachableWebDataURL) | |
Aaron Boodman
2012/03/20 19:28:16
Man, that is a bummer. It would be easy for code e
| |
72 LoadSucceeded(); | |
73 } | |
74 | |
75 void PageCycler::DidFailProvisionalLoad(int64 frame_id, | |
76 bool is_main_frame, | |
77 const GURL& validated_url, | |
78 int error_code, | |
79 const string16& error_description) { | |
80 // In the event of certain load failures, chrome will load the error page | |
81 // kUnreachableWebDataURL. We do not want to count this as a load success or | |
82 // failure, because it will throw off the count. | |
83 if (is_main_frame && validated_url.spec() != chrome::kUnreachableWebDataURL) | |
84 LoadFailed(validated_url, error_description); | |
85 } | |
86 | |
87 void PageCycler::Run(unsigned int iterations) { | |
88 iterations_ = iterations; | |
89 content::BrowserThread::PostBlockingPoolTask( | |
90 FROM_HERE, | |
91 base::Bind(&PageCycler::ReadURLsOnBackgroundThread, this)); | |
92 } | |
93 | |
94 void PageCycler::ReadURLsOnBackgroundThread() { | |
95 CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
96 std::string file_contents; | |
97 std::vector<std::string> url_strings; | |
98 | |
99 CHECK(file_util::PathExists(urls_file_)); | |
100 file_util::ReadFileToString(urls_file_, &file_contents); | |
101 base::SplitStringAlongWhitespace(file_contents, &url_strings); | |
102 | |
103 if (!url_strings.size()) | |
104 DLOG(WARNING) << "Page Cycler: No URLs in given file: " << | |
105 urls_file_.value(); | |
106 | |
107 for (std::vector<std::string>::const_iterator iter = url_strings.begin(); | |
108 iter != url_strings.end(); ++iter) { | |
109 // Since we don't count kUnreachableWebData as a valid load, we don't want | |
110 // the user to specify this as one of the pages to visit. | |
111 if (*iter == chrome::kUnreachableWebDataURL) { | |
112 DLOG(WARNING) << "Chrome error pages are not allowed as urls." << | |
113 "Omitting url: " << *iter << "."; | |
114 } else { | |
115 urls_.push_back(GURL(*iter)); | |
116 } | |
117 } | |
118 | |
119 content::BrowserThread::PostTask(content::BrowserThread::UI, | |
120 FROM_HERE, | |
121 base::Bind(&PageCycler::BeginCycle, this)); | |
122 } | |
123 | |
124 void PageCycler::BeginCycle() { | |
125 CHECK(iterations_); | |
126 --iterations_; | |
127 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
128 | |
129 // Upon launch, Chrome will automatically load the newtab page. This results | |
130 // in the browser most likely (but not definitely) being in a state of loading | |
131 // when PageCycler is ready to start. If we load the next URL when it is | |
132 // already trying to load, this will result in a page-load error for the | |
133 // previous URL (in this case, newtab); this will call LoadFailed(), and the | |
134 // first page of the url list will be skipped. If we are loading, we wait for | |
135 // the load to finish, which will then call LoadSucceeded() or LoadFailed(), | |
136 // and subsequently call LoadNextURL(). If we are not loading, we are safe to | |
137 // begin the cycle now. | |
138 if (browser_->GetSelectedWebContents()->IsLoading()) | |
139 return; | |
140 | |
141 LoadNextURL(); | |
142 } | |
143 | |
144 void PageCycler::LoadNextURL() { | |
145 CHECK(browser_); | |
146 if (url_index_ >= urls_.size()) { | |
147 if (iterations_) { | |
148 url_index_ = 0; | |
149 BeginCycle(); | |
150 return; | |
151 } else { | |
152 PrepareResults(); | |
153 return; | |
154 } | |
155 } | |
156 | |
157 if (url_index_) { | |
158 timings_string_.append(", "); | |
159 urls_string_.append(", "); | |
160 } | |
161 urls_string_.append(urls_[url_index_].spec()); | |
162 initial_time_ = base::TimeTicks::HighResNow(); | |
163 OpenURLParams params(urls_[url_index_], | |
164 Referrer(), | |
165 CURRENT_TAB, | |
166 content::PAGE_TRANSITION_TYPED, | |
167 false); | |
168 ++url_index_; | |
169 browser_->OpenURL(params); | |
170 } | |
171 | |
172 void PageCycler::LoadSucceeded() { | |
173 base::TimeDelta time_elapsed = | |
174 (base::TimeTicks::HighResNow() - initial_time_) / 1000.0; | |
175 timings_string_.append(base::Int64ToString(time_elapsed.ToInternalValue())); | |
176 LoadNextURL(); | |
177 } | |
178 | |
179 void PageCycler::LoadFailed(const GURL& url, | |
180 const string16& error_description) { | |
181 error_.append(ASCIIToUTF16("Failed to load the page at: " + | |
182 url.spec() + ": ")).append(error_description). | |
183 append(ASCIIToUTF16("\n")); | |
184 LoadNextURL(); | |
185 } | |
186 | |
187 void PageCycler::PrepareResults() { | |
188 std::string output; | |
189 if (!stats_file_.empty()) { | |
190 base::ProcessId pid = base::GetParentProcessId(base::GetCurrentProcId()); | |
191 ChromeProcessList chrome_processes(GetRunningChromeProcesses(pid)); | |
192 output += perf_test::MemoryUsageInfoToString("", chrome_processes, pid); | |
193 output += perf_test::IOPerfInfoToString("", chrome_processes, pid); | |
194 output += perf_test::SystemCommitChargeToString("", | |
195 base::GetSystemCommitCharge(), false); | |
196 output.append("Pages: [" + urls_string_ + "]\n"); | |
197 output.append("*RESULT times: t_ref= [" + timings_string_ + "] ms\n"); | |
198 } | |
199 content::BrowserThread::PostBlockingPoolTask( | |
200 FROM_HERE, | |
201 base::Bind(&PageCycler::WriteResultsOnBackgroundThread, this, output)); | |
202 } | |
203 | |
204 void PageCycler::WriteResultsOnBackgroundThread(std::string output) { | |
205 CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
206 if (!output.empty()) { | |
207 CHECK(!stats_file_.empty()); | |
208 file_util::WriteFile(stats_file_, output.c_str(), output.size()); | |
209 } | |
210 CHECK(!errors_file_.empty()); | |
211 if (!error_.empty()) { | |
212 file_util::WriteFile(errors_file_, UTF16ToUTF8(error_).c_str(), | |
213 error_.size()); | |
214 } else if (file_util::PathExists(errors_file_)) { | |
215 // If there is an old error file, delete it to avoid confusion. | |
216 file_util::Delete(errors_file_, false); | |
217 } | |
218 content::BrowserThread::PostTask(content::BrowserThread::UI, | |
219 FROM_HERE, | |
220 base::Bind(&PageCycler::Finish, this)); | |
221 } | |
222 | |
223 void PageCycler::Finish() { | |
224 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
225 BrowserList::RemoveObserver(this); | |
226 browser_->Exit(); | |
227 Release(); // Balanced in Init() (only one of Finish/Abort should be called). | |
228 } | |
229 | |
230 void PageCycler::Abort() { | |
231 browser_ = NULL; | |
232 BrowserList::RemoveObserver(this); | |
233 Release(); // Balanced in Init() (only one of Finish/Abort should be called). | |
234 } | |
235 | |
236 void PageCycler::OnBrowserAdded(const Browser* browser) {} | |
237 | |
238 void PageCycler::OnBrowserRemoved(const Browser* browser) { | |
239 if (browser == browser_) { | |
240 DLOG(WARNING) << | |
241 "Page Cycler: browser was closed before the run was completed."; | |
242 MessageLoop::current()->PostTask(FROM_HERE, | |
243 base::Bind(&PageCycler::Abort, this)); | |
244 } | |
245 } | |
OLD | NEW |