| 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 |