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