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