Index: chrome/browser/page_cycler/page_cycler.cc |
diff --git a/chrome/browser/page_cycler/page_cycler.cc b/chrome/browser/page_cycler/page_cycler.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b7a22a91ac4806e7e3a3e0e0e58d7fddd7291474 |
--- /dev/null |
+++ b/chrome/browser/page_cycler/page_cycler.cc |
@@ -0,0 +1,257 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/page_cycler/page_cycler.h" |
Aaron Boodman
2012/05/16 23:37:00
Not reviewing this because I think I already revie
clintstaley
2012/05/31 01:54:30
Right. Just a Git collision here.
|
+ |
+#include "base/bind.h" |
+#include "base/file_path.h" |
+#include "base/file_util.h" |
+#include "base/message_loop.h" |
+#include "base/process_util.h" |
+#include "base/string_split.h" |
+#include "base/string_number_conversions.h" |
+#include "base/utf_string_conversions.h" |
+#include "chrome/app/chrome_command_ids.h" |
+#include "chrome/common/chrome_notification_types.h" |
+#include "chrome/test/base/chrome_process_util.h" |
+#include "chrome/test/perf/perf_test.h" |
+#include "content/public/browser/browser_thread.h" |
+#include "content/public/browser/notification_source.h" |
+#include "content/public/browser/notification_service.h" |
+#include "content/public/browser/notification_details.h" |
+#include "content/public/browser/notification_types.h" |
+#include "content/public/browser/web_contents.h" |
+#include "content/public/common/url_constants.h" |
+ |
+using content::NavigationController; |
+using content::OpenURLParams; |
+using content::Referrer; |
+using content::WebContents; |
+ |
+PageCycler::PageCycler(Browser* browser, |
+ FilePath urls_file, |
+ FilePath errors_file) |
+ : content::WebContentsObserver(browser->GetSelectedWebContents()), |
+ browser_(browser), |
+ urls_file_(urls_file), |
+ errors_file_(errors_file), |
+ url_index_(0), |
+ total_iterations_(0), |
+ current_iteration_(0), |
+ aborted_(false) { |
+ BrowserList::AddObserver(this); |
+ AddRef(); // Balanced in Finish()/Abort() (only one should be called). |
+} |
+ |
+PageCycler::~PageCycler() { |
+} |
+ |
+bool PageCycler::IsLoadCallbackValid(const GURL& validated_url, |
+ bool is_main_frame) { |
+ // If |url_index_| is equal to zero, that means that this was called before |
+ // LoadNextURL() - this can happen at startup, loading the new tab page; or |
+ // if the user specified a bad url as the final url in the list. In these |
+ // cases, do not report success or failure, and load the next page. |
+ if (!url_index_) { |
+ LoadNextURL(); |
+ return false; |
+ } |
+ return (is_main_frame && |
+ validated_url.spec() != content::kUnreachableWebDataURL); |
+} |
+ |
+void PageCycler::DidFinishLoad(int64 frame_id, |
+ const GURL& validated_url, |
+ bool is_main_frame) { |
+ if (IsLoadCallbackValid(validated_url, is_main_frame)) |
+ LoadSucceeded(); |
+} |
+ |
+void PageCycler::DidFailProvisionalLoad(int64 frame_id, |
+ bool is_main_frame, |
+ const GURL& validated_url, |
+ int error_code, |
+ const string16& error_description) { |
+ if (IsLoadCallbackValid(validated_url, is_main_frame)) |
+ LoadFailed(validated_url, error_description); |
+} |
+ |
+void PageCycler::Run(const int& total_iterations) { |
+ total_iterations_ = total_iterations; |
+ content::BrowserThread::PostBlockingPoolTask( |
+ FROM_HERE, |
+ base::Bind(&PageCycler::ReadURLsOnBackgroundThread, this)); |
+} |
+ |
+void PageCycler::ReadURLsOnBackgroundThread() { |
+ CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ std::string file_contents; |
+ std::vector<std::string> url_strings; |
+ |
+ CHECK(file_util::PathExists(urls_file_)) << urls_file_.value(); |
+ file_util::ReadFileToString(urls_file_, &file_contents); |
+ base::SplitStringAlongWhitespace(file_contents, &url_strings); |
+ |
+ if (!url_strings.size()) { |
+#if defined(OS_POSIX) |
+ error_.append(ASCIIToUTF16("Page Cycler: No URLs in given file: " + |
+ urls_file_.value())); |
+#elif defined(OS_WIN) |
+ error_.append(ASCIIToUTF16("Page Cycler: No URLs in given file: ")) |
+ .append(urls_file_.value()); |
+#endif // OS_WIN |
+ } |
+ |
+ for (std::vector<std::string>::const_iterator iter = url_strings.begin(); |
+ iter != url_strings.end(); ++iter) { |
+ GURL gurl(*iter); |
+ if (!gurl.is_valid()) |
+ error_.append(ASCIIToUTF16("Omitting invalid URL: " + *iter + ".\n")); |
+ // Since we don't count kUnreachableWebData as a valid load, we don't want |
+ // the user to specify this as one of the pages to visit. |
+ else if (*iter == content::kUnreachableWebDataURL) { |
+ error_.append(ASCIIToUTF16( |
+ "Chrome error pages are not allowed as urls. Omitting url: " + |
+ *iter + ".\n")); |
+ } else { |
+ urls_.push_back(gurl); |
+ } |
+ } |
+ |
+ content::BrowserThread::PostTask(content::BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(&PageCycler::BeginCycle, this)); |
+} |
+ |
+void PageCycler::BeginCycle() { |
+ CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ |
+ // Upon launch, Chrome will automatically load the newtab page. This can |
+ // result in the browser being in a state of loading when PageCycler is ready |
+ // to start. Instead of interrupting the load, we wait for it to finish, and |
+ // will call LoadNextURL() from DidFinishLoad() or DidFailProvisionalLoad(). |
+ if (browser_->GetSelectedWebContents()->IsLoading()) |
+ return; |
+ |
+ LoadNextURL(); |
+} |
+ |
+void PageCycler::LoadNextURL() { |
+ CHECK(browser_); |
+ if (url_index_ >= urls_.size()) { |
+ if (current_iteration_ < total_iterations_ - 1) { |
+ ++current_iteration_; |
+ url_index_ = 0; |
+ } else { |
+ PrepareResults(); |
+ return; |
+ } |
+ } |
+ if (url_index_ || current_iteration_) { |
+ timings_string_.append(", "); |
+ urls_string_.append(", "); |
+ } |
+ urls_string_.append(urls_[url_index_].spec()); |
+ initial_time_ = base::TimeTicks::HighResNow(); |
+ OpenURLParams params(urls_[url_index_], |
+ Referrer(), |
+ CURRENT_TAB, |
+ content::PAGE_TRANSITION_TYPED, |
+ false); |
+ ++url_index_; |
+ browser_->OpenURL(params); |
+} |
+ |
+void PageCycler::LoadSucceeded() { |
+ base::TimeDelta time_elapsed = |
+ (base::TimeTicks::HighResNow() - initial_time_) / 1000.0; |
+ timings_string_.append(base::Int64ToString(time_elapsed.ToInternalValue())); |
+ LoadNextURL(); |
+} |
+ |
+void PageCycler::LoadFailed(const GURL& url, |
+ const string16& error_description) { |
+ error_.append(ASCIIToUTF16("Failed to load the page at: " + |
+ url.spec() + ": ")).append(error_description). |
+ append(ASCIIToUTF16("\n")); |
+ base::TimeDelta time_elapsed = |
+ (base::TimeTicks::HighResNow() - initial_time_) / 1000.0; |
+ timings_string_.append(base::Int64ToString(time_elapsed.ToInternalValue()) + |
+ (" (failed)")); |
+ LoadNextURL(); |
+} |
+ |
+void PageCycler::PrepareResults() { |
+ std::string output; |
+ if (!stats_file_.empty()) { |
+#if defined(OS_POSIX) |
+ base::ProcessId pid = base::GetParentProcessId(base::GetCurrentProcId()); |
+#elif defined(OS_WIN) |
+ base::ProcessId pid = base::GetCurrentProcID(); |
+#endif // OS_WIN |
+ ChromeProcessList chrome_processes(GetRunningChromeProcesses(pid)); |
+ output += perf_test::MemoryUsageInfoToString("", chrome_processes, pid); |
+ output += perf_test::IOPerfInfoToString("", chrome_processes, pid); |
+ output += perf_test::SystemCommitChargeToString("", |
+ base::GetSystemCommitCharge(), false); |
+ output.append("Pages: [" + urls_string_ + "]\n"); |
+ output.append("*RESULT times: t_ref= [" + timings_string_ + "] ms\n"); |
+ } |
+ content::BrowserThread::PostBlockingPoolTask( |
+ FROM_HERE, |
+ base::Bind(&PageCycler::WriteResultsOnBackgroundThread, this, output)); |
+} |
+ |
+void PageCycler::WriteResultsOnBackgroundThread(std::string output) { |
+ CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ if (!output.empty()) { |
+ CHECK(!stats_file_.empty()); |
+ file_util::WriteFile(stats_file_, output.c_str(), output.size()); |
+ } |
+ CHECK(!errors_file_.empty()); |
+ if (!error_.empty()) { |
+ file_util::WriteFile(errors_file_, UTF16ToUTF8(error_).c_str(), |
+ error_.size()); |
+ } else if (file_util::PathExists(errors_file_)) { |
+ // If there is an old error file, delete it to avoid confusion. |
+ file_util::Delete(errors_file_, false); |
+ } |
+ if (aborted_) { |
+ content::BrowserThread::PostTask(content::BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(&PageCycler::Abort, this)); |
+ } else { |
+ content::BrowserThread::PostTask(content::BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(&PageCycler::Finish, this)); |
+ } |
+} |
+ |
+void PageCycler::Finish() { |
+ CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ BrowserList::RemoveObserver(this); |
+ browser_->OnWindowClosing(); |
+ browser_->ExecuteCommand(IDC_EXIT); |
+ Release(); // Balanced in Init() (only one of Finish/Abort should be called). |
+} |
+ |
+void PageCycler::Abort() { |
+ browser_ = NULL; |
+ BrowserList::RemoveObserver(this); |
+ Release(); // Balanced in Init() (only one of Finish/Abort should be called). |
+} |
+ |
+void PageCycler::OnBrowserAdded(const Browser* browser) {} |
+ |
+void PageCycler::OnBrowserRemoved(const Browser* browser) { |
+ if (browser == browser_) { |
+ aborted_ = true; |
+ error_.append(ASCIIToUTF16( |
+ "Browser was closed before the run was completed.")); |
+ DLOG(WARNING) << |
+ "Page Cycler: browser was closed before the run was completed."; |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, base::Bind(&PageCycler::PrepareResults, this)); |
+ } |
+} |